2016-01-01 65 views
2

我試圖通過將其重寫爲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之間傳輸,反之亦然,是否還有更多這樣的領域需要考慮?

回答

3

調用NIF的開銷很小。當Erlang運行時加載一個加載NIF的模塊時,它會用模擬器指令修補模塊的波束代碼以調用NIF。在調用實現NIF的C函數之前,指令本身只執行少量的設置。這不是導致您的性能問題的區域。

分析NIF與分析任何其他C/C++代碼非常相似。從你的Makefile看來,你在OS X上開發了這個代碼。在該平臺上,假設你已經安裝了XCode,你可以使用Instruments application和CPU Samples工具來查看你的代碼花費的大部分時間。在Linux上,您可以使用callgrind tool of valgrindErlang emulator built with valgrind support來測量您的代碼。

什麼,你會發現,如果你使用這些工具在你的代碼,例如,perf_merger:main/1花費大量的時間在merger_nif_heap_get,這又花費在CollateJSON的時間顯着量。這個功能好像叫做convertUTF8toUCharcreateStringFromJSON。你的NIF似乎也執行了很多內存分配。這些是您應該專注於加速代碼的領域。

+0

謝謝Steve的建議。嘗試從Vallangr支持的Erlang VM中獲取有用的信息。有趣的是,在Linux機器上,NIF版本比Erlang更快。 – Abhi

+0

來自NIF perf的一些發現運行:(a)NIF和Erlang(反之亦然)之間傳輸的數據量很大(對於大數據傳輸,即每NIF呼叫100KB或更多,性能可怕)。 (b)Erlang <->之間的上下文切換NIF不便宜,具有40字節數據(40MB聚合)的1M調用比10K 40KB數據需要更長的排序時間。 – Abhi

+0

在複製或其他方面,NIF和Erlang之間沒有真正的「數據傳輸」。正如我最初指出的那樣,我懷疑你看到的是'merger_nif_heap_put'和'merger_nif_heap_get'函數執行大量堆分配和複製的事實。您應該研究存儲和檢索不涉及太多分配和複製的數據的備選方法。例如,'merger_item_create'需要兩個'ErlNifBinary'實例,分配新的內存,並在那裏複製;相反,爲什麼不把它們存儲爲'ErlNifBinary'實例呢? –