zoukankan      html  css  js  c++  java
  • [Erlang0009][OTP] 高效指南 进程

    原文链接:http://www.erlang.org/doc/efficiency_guide/processes.html

    错误之处欢迎指正。

    8 进程

     

    8.1 创建Erlang进程

     

    相比操作系统的线程和进程来说,Erlang的进程更为轻量。

     

    一个新创建的进程在non-SMP、不支持HiPE的虚拟机上占用内存309words。(支持SMP和HiPE的话,内存占用会翻倍。)这个数字可以这样来得到:

    Erlang (BEAM) emulator version 5.6 [async-threads:0] [kernel-poll:false]
    
    Eshell V5.6  (abort with ^G)
    1> Fun = fun() -> receive after infinity -> ok end end.
    #Fun<...>
    2> {_,Bytes} = process_info(spawn(Fun), memory).
    {memory,1232}
    3> Bytes div erlang:system_info(wordsize).
    309

    这里包括233words的堆空间(也包括栈)。垃圾回收器会按需增加堆的大小。


    进程的主循环必须是尾递归。否则,在进程退出之前,栈空间会不同的增长。

    loop() -> 
      receive
         {sys, Msg} ->
             handle_sys_msg(Msg),
             loop();
         {From, Msg} ->
              Reply = handle_msg(Msg),
              From ! Reply,
              loop()
      end,
      io:format("Message is processed~n", []).

    io:format/2永远不会被调用,但是在递归执行loop/0的过程中,它的地址每次都会推到栈里去。正确的尾递归版本的函数应该是这样的:

    DO
    
       loop() -> 
          receive
             {sys, Msg} ->
                handle_sys_msg(Msg),
                loop();
             {From, Msg} ->
                Reply = handle_msg(Msg),
                From ! Reply,
                loop()
        end.

    初始的堆大小

    默认的233words的初始堆空间在一个有成百上千乃至上万的Erlang系统中是非常保守的。垃圾回收器会按需调整堆空间的大小。

     

    在一个较少进程组成的系统中,用erl的+h选项或者在每个进程启动的时候使用spawn_opt/4的min_heap_size选项都可以增加最小堆的大小以提高性能。

     

    这里也存在两面性的问题:首先,虽然垃圾回收器可以增大堆空间,但这个过程是一步一步增加的,这比在进程被创建时直接创建一个较大的堆空间要代价更高。但是,如果进程的堆空间比其存储的数据多出很多,垃圾回收器也会回收空间;设置最小堆空间来避免这个问题。

     

    注意:虚拟机可能会用更大的内存。而且因为垃圾回收器不经常光顾,大的二进制数据可能会存留更长的时间。

     

    在一个很多进程组成的系统里,时间很短的计算工作可以指派给一个新的进程,分配的最少堆空间可以多一些。当进工作完成时,把计算结果发送给另一个进程然后退出。如果最小堆空间能够被准确计算,那么进程就不比做任何垃圾回收操作。但在没有经过适当评测时这类优化不应被使用。

     

    8.2 进程消息

     

    进程之间的所有消息数据的传递都是拷贝,除了同一个节点的refc binary(见《二进制的构造和匹配(1)》)。

     

    当消息发送到另一个节点的进程时,会先编码成Erlang External Format,然后通过TCP/IP套接字来发送。接受的节点会解码消息,并发送到相应的进程。

     

    常量池

     

    Erlang terms常量现在被保存在常量池里;每一个被加载的模块都有自己的池。下面的函数:

    DO (in R12B and later)
    
    days_in_month(M) ->
        element(M, {31,28,31,30,31,30,31,31,30,31,30,31}).

    不会在每次调用的时候都创建元组,(只有在下次垃圾回收之后才会重新创建),元组会一直保存在模块的常量池里。

     

    但是如果常量被发送给另一个进程(或者储存在ETS表中),它就会被拷贝。原因是,运行时系统必须能够持续追踪所有常量的引用,以便能够正确卸载包含常量的代码。(当代码被卸载时,常量会被拷贝到引用它们的进程的堆上。)常量的拷贝或许会在以后的发布版本中被淘汰。

     

    被抛弃的共享

     

    共享的子项将不再被保存,当一个项被发送给另一个进程,或在调用spawn创建进程时作为初始化参数被传递,或是存储在ETS表中。这是一个优化。大部分应用不要带着共享子项发送消息。

     

    下面是共享子项被创建的例子:

    kilo_byte() ->
        kilo_byte(10, [42]).
    
    kilo_byte(0, Acc) ->
        Acc;
    kilo_byte(N, Acc) ->
        kilo_byte(N-1, [Acc|Acc]).

    kilo_byte/0创建了一个深列表。如果我们调用list_to_binary/1,我们可以将这个列表转换成1024字节的二进制:

    1> byte_size(list_to_binary(efficiency_guide:kilo_byte())).
    1024

    用erts_debug:size/1 BIF,我们可以看到这个列表占有22words的堆空间:

    2> erts_debug:size(efficiency_guide:kilo_byte()).
    22

    用erts_debug:flat_size/1 BIF,我们能够计算出如果共享部分被忽略的话这个列表的大小。这也是当它被发送给其他进程或存在ETS中的大小:

    3> erts_debug:flat_size(efficiency_guide:kilo_byte()).
    4094

    我们可以核实共享部分会被丢弃,如果把数据插入ETS表:

    4> T = ets:new(tab, []).
    17
    5> ets:insert(T, {key,efficiency_guide:kilo_byte()}).
    true
    6> erts_debug:size(element(2, hd(ets:lookup(T, key)))).
    4094
    7> erts_debug:flat_size(element(2, hd(ets:lookup(T, key)))).
    4094

    当数据通过ETS表来传递,erts_debug:size/1和erts_debug:flat_size/1返回相同的大小,都不包括共享的部分。

    在未来的Erlang/OTP发布版本中,我们可能会实现保存共享部分的方法。我们不打算把保存共享作为默认的功能,因为那将对绝大多数的Erlang应用不利。

     

    8.3 SMP虚拟机

     

    SMP虚拟机会给运行了多个Erlang调度线程(与核的个数相同)的多核或多CPU计算机带来好处。每个调度线程都会像未开SMP的虚拟机调度线程一样来调度进程。

     

    想要通过SMP虚拟机在性能上获益,你的应用必须在绝大多数情况下跑在多个进程上。否则Erlang虚拟机还是只能在同一时间运行一个Erlang进程。但同时你必须承担锁的额外代价。尽管我们极力减少锁的代价,但它不可能降为零。

     

    那些看起来是并发的基准经常都是顺序的。例如estone基准,它是完全顺序的。就是最普通的环基准的实现;通常当一个进程被激活时,其他进程在receive状态中等待。

     

    percept应用可被用来测试你的应用,看是否有并发的潜力

  • 相关阅读:
    ASP.NET Web API 框架研究 Self Host模式下的消息处理管道
    ASP.NET Web API 框架研究 Web Host模式下的消息处理管道
    ASP.NET Web API 框架研究 核心的消息处理管道
    ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道
    ASP.NET Web API 框架研究 ASP.NET Web API 路由
    ASP.NET Web API 框架研究 ASP.NET 路由
    ASP.NET Web API 入门 (API接口、寄宿方式、HttpClient调用)
    MVVM模式
    RESTful Web API 理解
    C# 函数式编程及Monads.net库
  • 原文地址:https://www.cnblogs.com/liangjingyang/p/2708546.html
Copyright © 2011-2022 走看看