Archive

Author Archive

R14A实现了EEP31,添加了binary模块

May 21st, 2010 Comments off

Erlang的binary数据结构非常强大,而且偏向底层,在作网络程序的时候,很方便的能够和二进制协议对应起来。但是由于这个数据结构加入erlang语言的时间不是很长,相关的配套模块不是很多。 在binary的匹配,替换,修改就显的非常麻烦。 于是有了EEP31 。 R14A昨天已经实现了这个功能, 在stdlib下添加了个binary模块。 这个模块大部分功能是由BIF实现的, 同时充分考虑了CPU使用的公平性,源码大部分在erl_bif_binary.c下。 还添加了个gurad函数: binary_part进一步方便我们写匹配条件。

我们在源码里面发现了以下注释:
/*
* The native implementation functions for the module binary.
* Searching is implemented using aither Boyer-More or Aho-Corasick
* depending on number of searchstrings (BM if one, AC if more than one).
* Native implementation is mostly for efficiency, nothing
* (except binary:referenced_byte_size) really *needs* to be implemented
* in native code.
*/

这个模块兼顾了效率和方便性,使用起来就大大简化了代码的复杂度,有福气了。

Categories: Erlang探索 Tags: , , , , ,

R14A添加新指令优化Ref消息的接收

May 19th, 2010 1 comment

Erlang的惯用法之一就是在消息匹配的时候,如果需要唯一性,通常会通过make_ref搞个唯一的Ref来作为消息的一部分来匹配。这个惯用法用在gen_server:call或者demonitor这样的使用频度很高的函数里面。由于erlang的消息匹配是再消息队列里面挨个遍历来匹配,特别是消息队列特别长的时候,会有很大的性能瓶颈。于是新的优化出现了,以下是编译器beam_receive.erl里面的解释,写的很清楚:

%%%                                                                                                                                                                    
%%% In code such as:                                                                                                                                                   
%%%                                                                                                                                                                    
%%%    Ref = make_ref(),        %Or erlang:monitor(process, Pid)                                                                                                       
%%%      .                                                                                                                                                             
%%%      .                                                                                                                                                             
%%%      .                                                                                                                                                             
%%%    receive                                                                                                                                                         
%%%       {Ref,Reply} -> Reply                                                                                                                                         
%%%    end.                                                                                                                                                            
%%%                                                                                                                                                                    
%%% we know that none of the messages that exist in the message queue                                                                                                  
%%% before the call to make_ref/0 can be matched out in the receive                                                                                                    
%%% statement. Therefore we can avoid going through the entire message                                                                                                 
%%% queue if we introduce two new instructions (here written as                                                                                                        
%%% BIFs in pseudo-Erlang):                                                                                                                                            
%%%                                                                                                                                                                    
%%%    recv_mark(SomeUniqInteger),                                                                                                                                     
%%%    Ref = make_ref(),                                                                                                                                               
%%%      .                                                                                                                                                             
%%%      .                                                                                                                                                             
%%%      .                                                                                                                                                             
%%%    recv_set(SomeUniqInteger),                                                                                                                                      
%%%    receive                                                                                                                                                         
%%%       {Ref,Reply} -> Reply                                                                                                                                         
%%%    end.                                                                                                                                                            
%%%                                                                                                                                                                    
%%% The recv_mark/1 instruction will save the current position and                                                                                                     
%%% SomeUniqInteger in the process context. The recv_set                                                                                                               
%%% instruction will verify that SomeUniqInteger is still stored                                                                                                       
%%% in the process context. If it is, it will set the current pointer                                                                                                  
%%% for the message queue (the next message to be read out) to the                                                                                                     
%%% position that was saved by recv_mark/1.                                                                                                                            
%%%    
%%% The remove_message instruction must be modified toinvalidate                                                                                                      
%%% the information stored by the previous recv_mark/1, in case there                                                                                                  
%%% is another receive executed between the calls to recv_mark/1 and                                                                                                   
%%% recv_set/1.                                                                                                                                                        
%%%                                                                                                                                                                    
%%% We use a reference to a label (i.e. a position in the loaded code)                                                                                                 
%%% as the SomeUniqInteger.                                                                                                                                            
%%%                                                   

结论: 点滴优化成就高性能。

非阻塞connect的一个细节

May 18th, 2010 4 comments

昨天听zhuzhaoyuan说的一个connect细节. 通常我们connect的时候都是非阻塞的, 在connect调用后把句柄挂到poll去, 等poll通知可写的时候, 我们就认为connect成功了. 但是在linux平台下实际上不一定成功, 具体的要用socket get_opt来检查下出错码来决定.

以下是从man 2 connnect摘抄的:

EINPROGRESS
The socket(2,7,n) is non-blocking and the connection cannot be com-
pleted immediately. It is possible to select(2,7,2 select_tut)(2) or poll(2) for
completion by selecting the socket(2,7,n) for writing. After select(2,7,2 select_tut)
indicates writability, use getsockopt(2) to read(2,n,1 builtins) the SO_ERROR
option at level SOL_SOCKET to determine whether connect com-
pleted successfully (SO_ERROR is zero) or unsuccessfully
(SO_ERROR is one of the usual error(8,n) codes listed here, explain-
ing the reason for the failure).

我们看下erlang的inet_drv是如何处理的.
./erts/emulator/drivers/common/inet_drv.c:

        {
            int error = 0;      /* Has to be initiated, we check it */
            unsigned int sz = sizeof(error); /* even if we get -1 */
            int code = sock_getopt(desc->inet.s, SOL_SOCKET, SO_ERROR,
                                   (void *)&error, &sz);

            if ((code < 0) || error) {
                desc->inet.state = TCP_STATE_BOUND;  /* restore state */
                ret = async_error(INETP(desc), error);
                goto done;
            }
        }

结论: 细节决定品质.

中国地图(俺去过的地方)

May 12th, 2010 1 comment


create your own China map

持续更新!

Categories: 生活 Tags:

How to Build a Debug Enabled Erlang RunTime System

May 7th, 2010 1 comment

很多朋友在问如何调试Erlang的驱动代码等等,其实otp源码下的INSTALL.md写的很清楚, 摘抄下:
How to Build a Debug Enabled Erlang RunTime System
————————————————–

After completing all the normal building steps described above a debug
enabled runtime system can be built. To do this you have to change
directory to `$ERL_TOP/erts/emulator`.
注:一定要注意这句话, 假设你现在在otp源码目录下,正常编译好了, export ERL_TOP=`pwd` 然后进入erts/emulator目录下

In this directory execute:

$ make debug FLAVOR=$FLAVOR

where `$FLAVOR` is either `plain` or `smp`. The flavor options will
produce a beam.debug and beam.smp.debug executable respectively. The
files are installed along side with the normal (opt) versions `beam.smp`
and `beam`.

To start the debug enabled runtime system execute:

$ $ERL_TOP/bin/cerl -debug

The debug enabled runtime system features lock violation checking,
assert checking and various sanity checks to help a developer ensure
correctness. Some of these features can be enabled on a normal beam
using appropriate configure options.

There are other types of runtime systems that can be built as well
using the similar steps just described.

$ make $TYPE FLAVOR=$FLAVOR

where `$TYPE` is `opt`, `gcov`, `gprof`, `debug`, `valgrind`, or `lcnt`.
These different beam types are useful for debugging and profiling
purposes.

祝玩的开心!

Categories: Erlang探索 Tags: , ,

erts_modified_timings是如何起作用的

May 3rd, 2010 Comments off

我们先看下man erl

+T Level:

Enables modified timing and sets the modified timing level. Currently valid range is 0-9. The timing of the runtime system will
change. A high level usually means a greater change than a low level. Changing the timing can be very useful for finding timing
related bugs.

Currently, modified timing affects the following:

Process spawning:
A process calling spawn, spawn_link, spawn_monitor, or spawn_opt will be scheduled out immediately after completing the
call. When higher modified timing levels are used, the caller will also sleep for a while after being scheduled out.

Context reductions:
The amount of reductions a process is a allowed to use before being scheduled out is increased or reduced.

Input reductions:
The amount of reductions performed before checking I/O is increased or reduced.

NOTE: Performance will suffer when modified timing is enabled. This flag is only intended for testing and debugging. Also note
that return_to and return_from trace messages will be lost when tracing on the spawn BIFs. This flag may be removed or changed
at any time without prior notice.

我们可以知道这个选项是用于修改VM的进程和IO调度的时间,以及延迟spawn的执行时间, 使得和时间相关的问题,容易得到暴露,达到发现问题的目的。 但是Level是如何对于参数的呢?下面这个表格能回答问题了.
erts/emulator/beam/erl_init.c

 132ErtsModifiedTimings erts_modified_timings[] = {                                                    
 133    /* 0 */     {make_small(0), CONTEXT_REDS, INPUT_REDUCTIONS},                            
 134    /* 1 */     {make_small(0), 2*CONTEXT_REDS, 2*INPUT_REDUCTIONS},                               
 135    /* 2 */     {make_small(0), CONTEXT_REDS/2, INPUT_REDUCTIONS/2},                               
 136    /* 3 */     {make_small(0), 3*CONTEXT_REDS, 3*INPUT_REDUCTIONS},                               
 137    /* 4 */     {make_small(0), CONTEXT_REDS/3, 3*INPUT_REDUCTIONS},                               
 138    /* 5 */     {make_small(0), 4*CONTEXT_REDS, INPUT_REDUCTIONS/2},                               
 139    /* 6 */     {make_small(1), CONTEXT_REDS/4, 2*INPUT_REDUCTIONS},                               
 140    /* 7 */     {make_small(1), 5*CONTEXT_REDS, INPUT_REDUCTIONS/3},                               
 141    /* 8 */     {make_small(10), CONTEXT_REDS/5, 3*INPUT_REDUCTIONS},                              
 142    /* 9 */     {make_small(10), 6*CONTEXT_REDS, INPUT_REDUCTIONS/4}                               
 143};                                                                                                 
 

注意延时是以毫秒为单位的。

比如说: erl +T 8 那么spawn延时10ms, IO处理时间加大到3倍, 有利于快速处理IO事件。

Categories: Erlang探索 Tags: , ,

erts_ use_sender_punish未公开的特性

May 3rd, 2010 Comments off

我们知道erlang的VM调度是根据reds的,每个进程初始的时候分配2000个reds, 一旦这个reds用完了,进程就被挂起,放到队列去排队,等待下一次调度。OTP R13B04下一个进程给另外一个进程发送消息,是需要扣除发送者一定的reds, 这样看起来更公平。因为古语说杀敌一千, 自损八百。 但是Erlang有个未公开的选项来避开这种情况:erl +snsp
我们来看代码:
emulator/beam/erl_init.c

1131 else if (sys_strcmp("nsp", sub_param) == 0)
1132                erts_use_sender_punish = 0;

emulator/beam/bif.c

  /* send to local process */
	erts_send_message(p, rp, &rp_locks, msg, 0);
        if (!erts_use_sender_punish)
            res = 0;
        else {
#ifdef ERTS_SMP
            res = rp->msg_inq.len*4;
            if (ERTS_PROC_LOCK_MAIN & rp_locks)
                res += rp->msg.len*4;
#else
            res = rp->msg.len*4;
#endif
        }

正常情况下 扣除的reds是 接收者队列长度的4倍。

结论: 我们如果需要批量发送的场合,可以使用这个选项。

Categories: Erlang探索 Tags: ,