转载请注明出处 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指令数