我試圖通過將其重寫爲NIF實現來優化現有的unicode歸類庫(用Erlang編寫)。最主要的原因是因爲整理是CPU密集型操作。unicode歸類NIF比純Erlang實現運行速度慢
鏈接到執行:https://github.com/abhi-bit/merger
經由純二郎基於優先級隊列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]
我敢肯定,NIF的實現可能做得更快,因爲我使用動態數組基於二進制堆的純Unicode實現了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
具體的問題,我在這裏:
- ,是因爲二郎<的預期多少放緩 - 一個NIF模塊> I2C通信?在這種情況下,純C和NIF實現之間的放緩可能是30倍或更多
- 哪些工具可用於調試與NIF相關的放緩(如本例中)?我嘗試使用
perf top
來查看函數調用,最頂層的(一些十六進制地址顯示)來自「beam.smp」。 - 我應該看看優化NIF的可能區域是什麼?例如:我聽說應該讓數據在Erlang和C之間傳輸,反之亦然,是否還有更多這樣的領域需要考慮?
謝謝Steve的建議。嘗試從Vallangr支持的Erlang VM中獲取有用的信息。有趣的是,在Linux機器上,NIF版本比Erlang更快。 – Abhi
來自NIF perf的一些發現運行:(a)NIF和Erlang(反之亦然)之間傳輸的數據量很大(對於大數據傳輸,即每NIF呼叫100KB或更多,性能可怕)。 (b)Erlang <->之間的上下文切換NIF不便宜,具有40字節數據(40MB聚合)的1M調用比10K 40KB數據需要更長的排序時間。 – Abhi
在複製或其他方面,NIF和Erlang之間沒有真正的「數據傳輸」。正如我最初指出的那樣,我懷疑你看到的是'merger_nif_heap_put'和'merger_nif_heap_get'函數執行大量堆分配和複製的事實。您應該研究存儲和檢索不涉及太多分配和複製的數據的備選方法。例如,'merger_item_create'需要兩個'ErlNifBinary'實例,分配新的內存,並在那裏複製;相反,爲什麼不把它們存儲爲'ErlNifBinary'實例呢? –