Я пытаюсь оптимизировать существующую библиотеку сортировки юникода (написанную в Erlang), переписав ее как реализацию NIF. Основной причиной является то, что сортировка - это интенсивная работа процессора.Unicode collation NIF работает медленнее, чем реализация Pure Erlang
Ссылка на реализацию: https://github.com/abhi-bit/merger
Unicode сортировки строк 1M с помощью чистого Erlang на основе приоритета очереди:
erlc *.erl; ERL_LIBS="..:$ERL_LIBS" erl -noshell -s perf_couch_skew main 1000000 -s init stop
Queue size: 1000000
12321.649 ms
Unicode сортировки строк 1M с помощью НИФ на основе биномиальной кучи:
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]
Я уверен, NIF реализация может быть быстрее, потому что у меня есть чистая реализация кода Unicode, основанная на бинарной куче, использующая динамический массив, и это намного быстрее.
$ 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 < -> C связи в NIF модуле? В этом случае замедление, вероятно, составляет 30x или более между чистой реализацией C и NIF.
- Какие инструменты могут быть полезны для отладки замещения NIF (как в данном случае)? Я попытался использовать
perf top
, чтобы увидеть вызов функции, верхние (показывали некоторые шестнадцатеричные адреса), поступали из «beam.smp». - Каковы возможные области, которые я должен посмотреть на оптимизацию NIF? Например: я слышал, что нужно перенести данные между Erlang на C и наоборот, есть ли еще такие области для рассмотрения?
Спасибо Стив за ваши предложения. Попытка получить полезную информацию из Erlang VM с поддержкой valgrind. Интересно, что на Linux-машине версия NIF выглядит быстрее, чем Erlang. – Abhi
Пара результатов NIF perf работает: (a) Количество данных, передаваемых между NIF и Erlang (наоборот), имеет большое значение (для большей передачи данных, то есть 100 КБ или более при вызове NIF, производительность может быть ужасной). (b) Контекстный коммутатор между Erlang <-> NIF не является дешевым, 1M-вызовы с 40-байтовыми данными (40MB-агрегат) занимают больше времени, чем 10K 40KB данных. – Abhi
Нет никакой реальной «передачи» данных между NIF и Erlang, в смысле копирования или такого. Я подозреваю, что вы видите тот факт, что ваши функции 'merger_nif_heap_put' и' merger_nif_heap_get' выполняют много распределений кучи и копирования, как я изначально указал. Вы должны исследовать альтернативные способы хранения и извлечения данных, которые не связаны с таким распределением и копированием. Например, 'merger_item_create' принимает два экземпляра' ErlNifBinary', выделяет новую память и копирует их там; вместо этого, почему бы просто не хранить их как экземпляры «ErlNifBinary»? –