10


4

背景: C’s memchrとほぼ同等の機能の純粋なD言語実装を作成しようとしていますが、代わりに配列とインデックスを使用しています。ポインタ その理由は、std.stringがコンパイル時の関数評価で機能するようにするためです。 なじみのないw / Dの人たちのために、特定の制限が満たされるならば、関数はコンパイル時に評価されることができます。 1つの制限は、ポインタを使用できないことです。 もう1つは、C関数を呼び出したりインラインアセンブリ言語を使用したりできないことです。 コンパイル時に文字列ライブラリを機能させることは、一部のコンパイル時のコード生成ハックにとって有用です。

質問: memchrはボンネットの下でどのように速く動作するように動作しますか? Win32では、単純なループを使用して純粋なDで作成できたものは、境界チェックの無効化、ループの展開など、明らかに最適化された手法でさえ少なくとも2倍遅くなります。 文字列内の文字を見つけるのと同じくらい簡単なものに対して、どのような種類の非自明なトリックが利用可能ですか?

5 Answer


12


GNU libcのソースを見てみることをお勧めします。 ほとんどの関数については、汎用的な最適化されたCバージョンの関数と、できるだけ多くのサポートされているアーキテクチャ向けに最適化されたアセンブリ言語のバージョンの両方が含まれ、マシン固有のトリックを利用します。

x86-64 SSE2バージョンは、http://www.felixcloutier.com/x86/PCMPEQB:PCMPEQWの結果を組み合わせたものです:PCMPEQD.html [+ pcmpeqb +]データのキャッシュライン全体(一度に4つの16Bベクトル)で、早期終了の + pmovmskb + / + test + / `+ jcc +`のオーバーヘッドを償却します。

gccとclangは現在、 `+ if()break +`早期終了条件でループを自動ベクトル化することができないため、明白なC実装から単純なバイト単位のasmを作成します。


7


ちなみに、MSVCインストールのオプション部分として、MSVCランタイムライブラリのソースコードの大部分が利用可能です(したがって、それを見ることができます)。


5


これは、FreeBSDの(BSDライセンスの)http://svn.freebsd.org/viewvc/base/stable / 7 / lib / libc / string / memchr.c?view = markup[memchr.c]からのmemchr()です。 。 FreeBSDのオンラインソースコードブラウザは、実績のあるBSDライセンスのコード例の良い参考資料です。

void * memchr(s、c、n)const void * s; unsigned char c; size_t n; {if(n!= 0){const unsigned char * p = s;

do {if(* p == c)return((void *)(p  -  1)); while(--n!= 0); (NULL)を返します。 }


2


memsetやmemcpyのようなmemchrは一般的にかなり少量のマシンコードに縮小されます。 あなたはそのようなスピードを 類似のアセンブリコードをインライン化することなしに再現することはできないでしょう。 実装において考慮すべき一つの大きな問題は data alignmentです。

1つの genericテクニックをあなたが使うことができるかもしれないはの最後にhttp://en.wikipedia.org/wiki/Sentinel_(computer_science)[sentinel]を挿入することです。検索されている文字列は、確実に検索されます。 それはあなたがループの内側からループの後への文字列の終わりのテストを移動することを可能にします。


0


GNU libcは間違いなく*アセンブリ*バージョンのmemchr()を使用しています(一般的なLinuxディストリビューションでは)。 これが、信じられないほど速いのが理由です。

たとえば、11Gbファイルの行数を数えると( "wc -l"のように)、GNU libcの* assembly バージョンのmemchr()では 2.5 秒かかります。 しかし、例えば、FreeBSDからのmemchr() C実装*でmemchr()アセンブリ呼び出しを置き換えた場合、速度は* 30 *秒程度に低下します。

これは、memchr()を1つのcharを次々に比較するwhileループに置き換えるのと同じです。