既存の Unicode 照合ライブラリ (Erlang で記述) を NIF 実装として書き直して最適化しようとしています。主な理由は、照合が CPU を集中的に使用する操作であるためです。
実装へのリンク: https://github.com/abhi-bit/merger
Pure Erlang ベースの優先キューによる 1M 行の Unicode 照合:
erlc *.erl; ERL_LIBS="..:$ERL_LIBS" erl -noshell -s perf_couch_skew main 1000000 -s init stop
Queue size: 1000000
12321.649 ms
NIF ベースの二項ヒープによる 1M 行の Unicode 照合:
erlc *.erl; ERL_LIBS="..:$ERL_LIBS" erl -noshell -s perf_merger main 1000000 -s init stop
Queue size: 1000000
15871.965 ms
これは珍しいことです。おそらく最大 10 倍高速になると予想していました。
オンにしましたがeprof
、fprof
NIF モジュールに関してはあまり役に立ちません。以下はeprof
、主要な機能について述べたものです。
FUNCTION CALLS % TIME [uS / CALLS]
-------- ----- --- ---- [----------]
merger:new/0 1 0.00 0 [ 0.00]
merger:new/2 1 0.00 0 [ 0.00]
merger:size/1 100002 0.31 19928 [ 0.20]
merger:in/3 100000 3.29 210620 [ 2.11]
erlang:put/2 2000000 6.63 424292 [ 0.21]
merger:out/1 100000 14.35 918834 [ 9.19]
動的配列を使用したバイナリヒープに基づくユニコード照合の純粋な C 実装があり、はるかに高速であるため、NIF 実装を高速化できると確信しています。
$ make
gcc -I/usr/local/Cellar/icu4c/55.1/include -L/usr/local/Cellar/icu4c/55.1/lib min_heap.c collate_json.c kway_merge.c kway_merge_test.c -o output -licui18n -licuuc -licudata
./output
Merging 1 arrays each of size 1000000
mergeKArrays took 84.626ms
私がここに持っている具体的な質問:
- Erlang <-> NIF モジュールでの C 通信により、どれくらいの速度低下が予想されますか? この場合、純粋な C と NIF の実装の間で速度がおそらく 30 倍以上になります。
- この場合のように、NIF 関連のスローダウンをデバッグするのに役立つツールは何ですか? 関数呼び出しを確認するために使用
perf top
してみましたが、上位のもの (一部の 16 進アドレスが表示されていました) は「beam.smp」からのものでした。 - NIF を最適化するために検討すべき領域は何ですか? 例: Erlang から C へのデータ転送、およびその逆のデータ転送を最小限に抑える必要があると聞いたことがありますが、他に考慮すべき領域はありますか?