gen_server2 - OTP gen_server优化版(更正)

rabbitmq 的代码中包含一个gen_server2 , 其对Erlang OTP中的gen_server进行了一些优化.


gen_server.erl

loop(Parent, Name, State, Mod, hibernate, Debug) ->
    proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]);
loop(Parent, Name, State, Mod, Time, Debug) ->
    Msg = receive
	      Input ->
		    Input
	  after Time ->
		  timeout
	  end,
    decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false).



gen_server2.erl

loop(Parent, Name, State, Mod, Time, Queue, Debug) ->
    receive
        Input -> loop(Parent, Name, State, Mod,
                      Time, queue:in(Input, Queue), Debug)
    after 0 ->
            case queue:out(Queue) of
                {{value, Msg}, Queue1} ->
                    process_msg(Parent, Name, State, Mod,
                                Time, Queue1, Debug, Msg);
                {empty, Queue1} ->
                    receive
                        Input ->
                            loop(Parent, Name, State, Mod,
                                 Time, queue:in(Input, Queue1), Debug)
                    after Time ->
                            process_msg(Parent, Name, State, Mod,
                                        Time, Queue1, Debug, timeout)
                    end
            end
    end.



gen_server2中在主循环中,使用queue保存msg, 需要处理消息时,首先从queue中取出一个消息进行处理,如果queue为空,则使用receive接受消息, 随后处理,如果指定时间内没有任何消息,则处理timeout消息.

将消息保存在queue中, 减小了process内置msg queue的长度, 当我们需要处理消息时,首先从外部的queue中获取消息, 而不用遍历整个内置的msg queue, 当process的负载很大, 也就是消息队列中消息较多时,使用这种方法性能提升很大. 这里其实采用了一个分治的方法,将一个大的消息队列,切分成2个小的消息队列.

gen_server2中有一个不是很完善的地方,就是没有处理 Mod:init/1 返回{ok, State, hibernate}的情况,这个可以参考gen_server结合sys进行实现.

最后看看两者性能比较, 引用Joe' Bogmerle 所做的测试:
gen_server2 - OTP gen_server优化版(更正)_第1张图片

可以看出gen_server2性能有明显提升!


Update: 感谢yufeng的提醒,原谅我的不求甚解.

gen_server2, 其首先从inner msg quue中匹配任意(ANY)一条消息, 随后添加都外部的quue队列中. 随后继续进入Loop循环, 因此, 其实际的做法是首先将所有的消息从inner msg queue中取出,然后放入queue队列, 当inner msg quue没有任何消息时, 也就是after 0 的情况, 然后从queue中取出(queue:out/1)一个消息,交给process_msg进行处理. 如果queue为空,则进行第二次将inner msg导入到quue中的过程.


这样做gen_server2使所有的msg经历了一个从inner msg queue到外部queue的过程, 其减少了虚拟机内部的资源消耗, 但是这些消息都存储到外部的queue, 总体来说内存还是没有什么太大的变化.


最大的不同是:

gen_server2 收到任何一条消息放到外部的queue中, 当inner msg quue为空后,才进行消息处理, 继续循环.

gen_server 收到任何一条消息后,立即进行处理, 处理完成后继续循环.


我没有亲自做测试, 至于上面merle相关的测试, 唯一的解释就是, gen_server2拥有一个比较"空闲"的inner msg queue, 对于其性能提升具有某种好处. 回头我会具体测试一下.


 

你可能感兴趣的:(虚拟机,Hibernate,erlang,Blog)