erlang程序優化點的總結(轉)
註意,這裏只是給出一個總結,具體性能需要根據實際環境和需要來確定
霸爺指出,新的erlang虛擬機有很多調優啟動參數,今後現在這個方面深挖一下。
1. 進程標誌設置:
消息和binary內存:erlang:process_flag(min_bin_vheap_size, 1024*1024),減少大量消息到達或處理過程中產生大量binary時的gc次數
堆內存:erlang:process_flag(min_heap_size, 1024*1024),減少處理過程中產生大量term,尤其是list時的gc次數
進程優先級:erlang:process_flag(priority, high),防止特殊進程被其它常見進程強制執行reductions
進程調度器綁定:erlang:process_flag(scheduler, 1),當進程使用了port時,還需要port綁定支持,防止進程在不同調度器間遷移引起性能損失,如cache、跨numa node拷貝等,當進程使用了port時,主要是套接字,若進程與port不在一個scheduler上,可能會引發嚴重的epoll fd鎖競爭及跨numa node拷貝,導致性能嚴重下降
2. 虛擬機參數:
+S X:X :啟用調度器數量,多個調度器使用多線程,有大量鎖爭用
-smp disable :取消smp,僅使用單線程,16個-smp_disabled虛擬機性能高於+S 16:16
+sbt db :將scheduler綁定到具體的cpu核心上,再配合erlang進程和port綁定,可以顯著提升性能,但是如果綁定錯誤,反而會有反效果
3. 消息隊列:
消息隊列長度對性能的影響主要體現在以下兩個方面:進程binary堆的gc和進程內消息匹配,前者可以通過放大堆內存來減少gc影響,後者需要謹慎處理。
若進程在處理消息時是通過消息匹配方式取得消息,同時又允許其它進程無限制投遞消息到本進程,此時會引發災難,匹配方式取得消息會引發遍歷進程消息隊列,如果此時仍然有其它進程投遞消息,會導致進程消息隊列暴漲,遍歷過程也將增大代價,引發惡性循環。已知模式有:在gen_server中使用file:write(raw模式)或gen_tcp:send等,這些操作都是erlang虛擬機內部通過port driver實現的,均有內部receive匹配接收,對於這些操作,最好的辦法是將其改寫為nif,直接走進程堆進行操作,次之為將file:write或gen_tcp:send改寫為兩階段,第一階段為port_command,第二階段由gen_server接收返回結果,這種異步化可能有些正確性問題,對於gen_tcp:send影響不大,因為網絡請求本身要麽同步化要麽異步化,都需要內部的確認機制;對於file:write影響較大,file:write的錯誤通常為目錄不存在或磁盤空間不足,確保這兩個錯誤不造成影響即可,同時如果進程的其它部分需要使用file的其它操作,必須首先清空之前file:write產生的所有file的port消息,否則有可能產生消息序列紊亂的問題。
對於套接字的接口調用,可以參考rabbitmq的兩階段套接字發送方法,而對於文件接口調用,可以參考riak的bitcask引擎將文件讀寫封裝為nif的方法
4. 內存及ets表:
ets表可以用於進程間交換大數據,或充當緩存,以及復雜匹配代理等,其性能頗高,並發讀寫可達千萬級qps,並有兩個並發選項,在建立表時設置,分別是{write_concurrency, true} | {read_concurrency, true},以允許ets的並發讀寫
使用ets表可以繞過進程消息機制,從而在一定程度上提高性能,並將編程模式從面向消息模式變為面向共享內存模式
5. CPU密集型操作:
erlang執行流程的問題:
1. 其指令都是由其虛擬機執行的,一條指令可能需要cpu執行3-4條指令,一些大規模的匹配或遍歷操作會嚴重影響性能;
2. 其bif調用執行過程類似於操作系統的系統調用,需要對傳入參數進行轉換,在大量小操作時損失性能較為嚴重
3. 其port driver流程較為繁冗復雜,需要經歷大量的回調等,一般的小功能操作,不要通過port driver實現
建議:
字符串匹配不要通過list進行,最好通過binary;單字節匹配,尤其是語法解析,如xmerl、mochijson2、lexx等,盡管使用binary,但是它們是一個字節一個字節匹配的,性能會退化到list的水平,應該盡量將其nif化;
對於一些小操作,反而應該去bif化、去nif化、去port driver化,因為進入erlang內部函數的執行代價也不小;
已知的性能瓶頸:re、xmerl、mochijson2、lexx、erlang:now、calendar:local_time_to_universal_time_dst等
6. 數據結構:
減少遍歷,盡量使用API提供的操作
由於各種類型的變量實際可以當做c的指針,因此erlang語言級的操作並不會有太大代價
lists:reverse為c代碼實現,性能較高,依賴於該接口實現的lists API性能都不差,避免list遍歷,[||]和foreach性能是foldl的2倍,不在非必要的時候遍歷list
dict:find為微秒級操作,內部通過動態hash實現,數據結構先有若幹槽位,後根據數據規模變大而逐步增加槽位,fold遍歷性能低下
gb_trees:lookup為微秒級操作,內部通過一個大的元組實現,iterator+next遍歷性能低下,比list的foldl還要低2個數量級
其它常用結構:queue,set,graph等
7. 計時器:
erlang的計時器timer是通過一個唯一的timer進程實現的,該進程是一個gen_server,用戶通過timer:send_after和timer:apply_after在指定時間間隔後收到指定消息或執行某個函數,每個用戶的計時器都是一條記錄,保存在timer的ets表timer_tab中,timer的時序驅動通過gen_server的超時機制實現。若同時使用timer的用戶過多,則tiemr將響應不過來,成為瓶頸。
更好的方法是使用erlang的原生計時器erlang:send_after和erlang:start_timer,它們把計時器附著在進程自己身上。
8. 尾調用和尾遞歸:
尾調用和尾遞歸是erlang函數式語言最強大的優化,盡量保持函數尾部有尾調用或尾遞歸
9. 文件預讀,批量寫,緩存:
這些方式都是局部性的體現:
預讀:讀空間局部性,文件提供了read_ahead選項
批量寫:寫空間局部性
對於文件寫或套接字發送,存在若幹級別的批量寫:
1. erlang進程級:進程內部通過list緩存數據
2. erlang虛擬機:不管是efile還是inet的driver,都提供了批量寫的選項delayed_write|delay_send,
它們對大量的異步寫性能提升很有效
3. 操作系統級:操作系統內部有文件寫緩沖及套接字寫緩沖
4. 硬件級:cache等
緩存:讀寫時間局部性,讀寫空間局部性,主要通過操作系統系統,erlang虛擬機沒有內部的緩存
10.套接字標誌設置:
延遲發送:{delay_send, true},聚合若幹小消息為一個大消息,性能提升顯著
發送高低水位:{high_watermark, 128 * 1024} | {low_watermark, 64 * 1024},輔助delay_send使用,delay_send的聚合緩沖區大小為high_watermark,數據緩存到high_watermark後,將阻塞port_command,使用send發送數據,直到緩沖區大小降低到low_watermark後,解除阻塞,通常這些值越大越好,但erlang虛擬機允許設置的最大值不超過128K
發送緩沖大小:{sndbuf, 16 * 1024},操作系統對套接字的發送緩沖大小,在延遲發送時有效,越大越好,但有極值
接收緩沖大小:{recbuf, 16 * 1024},操作系統對套接字的接收緩沖大小
11. 序列化/反序列化:
通常情況下,為了簡化實現,一般將erlang的term序列化為binary,傳遞到目的地後,在將binary反序列化為term,這通常涉及到兩個操作:
term_to_binary及binary_to_term,這兩個操作性能消耗極為嚴重,應至多只做一次,減少甚至消除它們是最正確的,例如直接構造binary進行跨虛擬機數據交換;
但對比與其它的序列化和反序列化方式,如利用protobuf等,term_to_binary和binary_to_term的性能是高於這些方式的,畢竟是erlang原生格式,對於力求簡單的應用,其序列化和反序列化方式推薦term_to_binary和binary_to_term
12. 並發化
在一些場景下,如web請求、數據庫請求、分布式文件系統等,單個接入接口已經不能滿足性能需求,需要有多個接入接口,多個數據通道,等等,這要求所有請求處理過程必須是無狀態的,或者狀態更改同步進入一個公共存儲,而公共存儲也必須是支持並發處理的,如並發數據庫、類hdfs、類dynamo存儲等,若一致性要求較高,最好選用並發數據庫,如mysql等,若在此基礎上還要求高可用,最好選擇同步多結點存儲,
mnesia、zk都是這方面的典型;若不需要較高的一致性,類hdfs、類dynamo這類no sql存儲即可滿足
13. hipe
將erlang匯編翻譯成機器碼,減少一條erlang指令對應的cpu指令數
轉自:http://wqtn22.iteye.com/blog/1820587
erlang程序優化點的總結(轉)