轉載請註明出處 node
注意,這裏只是給出一個總結,具體性能須要根據實際環境和須要來肯定mysql
霸爺指出,新的erlang虛擬機有不少調優啓動參數,從此如今這個方面深挖一下。web
1. 進程標誌設置:sql
消息和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),防止特殊進程被其它常見進程強制執行reductionsjson
進程調度器綁定: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、mochijson二、lexx等,儘管使用binary,可是它們是一個字節一個字節匹配的,性能會退化到list的水平,應該儘可能將其nif化;
對於一些小操做,反而應該去bif化、去nif化、去port driver化,由於進入erlang內部函數的執行代價也不小;
已知的性能瓶頸:re、xmerl、mochijson二、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指令數