楽:要素を区切るスペースなしで配列を文字列に変換する超高速な方法はありますか?


8

それぞれが1メガバイトの長さの何千ものバイナリバイト文字列をASC文字列に変換する必要があります。これは私がやっていることであり、遅すぎるようです:

sub fileToCorrectUTF8Str ($fileName) { # binary file
    my $finalString = "";
    my $fileBuf = slurp($fileName, :bin);    
    for @$fileBuf { $finalString = $finalString ~ $_.chr; };    
    return $finalString;
}

〜@ bは@bをすべての要素をスペースで区切った文字列に変換しますが、これは私が望んでいることではありません。@b = <abcd>;の場合 〜@ bは「abc d」です。しかし、私は単に「abcd」が欲しいし、これを本当に速くしたい。

それで、最善の方法は何ですか?最終的な文字列は順番に作成されるため、並列処理にハイパーを実際に使用することはできません。または私はできますか?

回答:


10

TL; DR古い落語で.decodeは、約100倍高速です。

コードと一致する長い形式:

sub fileToCorrectUTF8Str ($fileName) { # binary file
  slurp($fileName, :bin).decode
}

パフォーマンスノート

最初に、これは私がテストのために書いたものです:

# Create million and 1 bytes long file:
spurt 'foo', "1234\n6789\n" x 1e5 ~ 'Z', :bin;

# (`say` the last character to check work is done)
say .decode.substr(1e6) with slurp 'foo', :bin;

# fileToCorrectUTF8Str 'foo' );

say now - INIT now;

TIO.runのOn 2018.12rakudo、上記.decodeの重さおよそ.05百万バイトのファイルあたりの秒の代わりに約5ソリューションのための秒。

もちろん、システムでテストしたり、rakudoの新しいバージョンを使用したりできます。私は違いが同じ順序のままであることを期待しますが、年が経つにつれ絶対時間は著しく改善されます。[1]

なぜ100倍速いのですか?

さて、まず、@Buf/ Blob明示的力はかつて表示するRAKU 単一の項目(のようにバッファ)の複数のもの(リスト別名要素の複数の項目)。つまり、100万要素のバッファーの場合、1回の高レベルの操作ではなく、すぐに100万回の高レベルの反復/操作になる高レベルの反復を意味します。

第2に、使用すると.decode反復が回避されるだけでなく、ファイルごとに1回だけメソッド呼び出しのオーバーヘッドが比較的遅くなるのに対して、反復処理を行う.chrとファイルごとに100万回の呼び出しが発生する可能性があります。メソッドの呼び出しは(少なくとも意味論的に)遅延バインドされておりこれはたとえば、メソッドの代わりにsubを呼び出す場合と比較して、原則として比較的コストがかかります(subは通常事前バインドされています)。

それはすべて言った:

  • 警告が空であることを覚えておいてください[1]。たとえば、rakudoの標準クラスはメソッドキャッシュを生成し、コンパイラーはメソッドをインライン化するだけなので、メソッド呼び出しのオーバーヘッドが無視できる可能性があります。

  • ドキュメントの「パフォーマンス」ページ、特に「既存の高性能コードを使用する」も参照してください。

あるBuf.StrエラーメッセージLTAは

更新 Liz ++のコメントを参照してください。

あなたが使用しようとした場合.StrBuf、またはBlob(など使用して、または同等の~それに接頭辞を)あなたは例外を取得します。現在のメッセージは:

Cannot use a Buf as a string, but you called the Str method on it

以下のためのドキュメント.Str上のBuf/はBlob現在、こう述べています。

Strに変換するには、を使用する必要があります.decode

エラーメッセージが同じことを示唆していないのは間違いなくLTAです。

その後、再び、このことについてどうするかを決定する前に、どちらかといえば、私たちは何を、どのように、フォークを検討する必要がある可能性がエラーメッセージなど、それについての信号を含め、うまくいかないものから学び、また何をどのように行う中を事実は現在学習しており、適切な文化とインフラストラクチャの構築に向けて私たちの反応にバイアスをかけています。

特に、人々が彼らが見るエラーメッセージとそれについて詳しく説明するオンラインディスカッションとの間を簡単に結びつけることができるなら、それは考慮に入れられ、おそらく奨励および/またはより簡単にされる必要があります。

たとえば、このSOがこの問題をカバーしていて、エラーメッセージが含まれているため、グーグルは誰かをここに連れてくる可能性があります。エラーメッセージを変更するよりも、それを利用する方が適切な場合があります。またはそうでないかもしれません。変更は簡単です...

以下のコメントや既存の落語の問題を検索して、Buf.Strエラーメッセージの改善が検討されているかどうか、および/または問題を開いて変更を提案するかどうかを確認してください。動くすべての岩は少なくとも素晴らしい運動であり、私たちの集団的努力がますます賢くなるにつれて、山を改善します(私たちの見方)。

脚注

[1]ラテン語で「Caveat Empty」がよく知られているように、特定のraku機能、より一般的には特定のコードの絶対的および相対的なパフォーマンスは、システムの機能、その間の負荷などの要因により常に変動します。コードの実行、およびコンパイラーによる最適化。したがって、たとえば、システムが「空」の場合、コードの実行が速くなる可能性があります。または、別の例として、コンパイラが速くなるまで1〜3年待つと、rakudoのパフォーマンスの向上は引き続き有望に見えます



2
これをさらに最適化する方法はいくつかあります。この場合のslurpラッパーにすぎないIO::Path.slurpので、.IO.slurp代わりにを呼び出すと、ベンチマークでこれが約2%速くなります。でファイルを丸呑み:enc<latin1>すると、デフォルトのUTF-8エンコーディングの場合と同様に文字のバッファができますが、丸呑みされたファイルが実際にデコード時に実行している有効なUTF-8であるかどうかのチェックをスキップしますとにかくUTF-8文字列に変換します。これにより、これも約10%高速になります。
カイエピ

ご協力ありがとうございます!!! .decodeとその他のマイナーコードの変更により、処理する必要のある長い文字列ごとに約8秒削っています。ありがとう!!!
lisprogtor

こんにちは@lisprogtor「削る」という比喩は「数を非常に少量減らす」ことを意味します。長い文字列あたり約8秒短縮するまでの時間はどれくらいでしたか?削減は十分ですか?
レイフ

8秒は「削る」よりも間違いなく優れています。ありがとう!!!
lisprogtor
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.