天天看點

【轉載】erlang 程序的 hibernate

簡單的了解:  

hibernate 可以令處于 idle 狀态的 erlang 程序馬上進行 gc,因為程序處于 receive 狀态下是不會 gc 的。

在性能調優時發現,gc 對于消息發送速度的影響還是非常大的。

關于 erlang gc 問題也可以參看這個 

recently, as part of rabbitmq server development, we ran into an interesting issue regarding erlang’s per-process garbage collection. if a process is idle — not doing any work at all, simply waiting for an external event — then its garbage-collector will not run until it starts working again. the solution is to hibernate idle processes, which causes a very aggressive garbage-collection run and puts the process into a suspended state, from which it will wake when a message next arrives.

是以檢視 rabbitmq 源代碼可以看到裡面的 gen_server/fsm 等程序統統都會使用 hibernate 。 

      下面解釋下如何讓程序 hibernate ,這裡需要注意的是一個程序 hiberate 後,會清空 stack 棧,也就是說之前的調用關系全部沒有了,也就是說hibernate 所在的函數永遠不會傳回,待有新消息時,程序從 hibernate(m,f,a) 裡指定的 m:f 處開始運作。 

gen_server 和 gen_fsm 都提供了 hibernate 的方式: 

第一種:  在 gen_server 或 gen_fsm 回調接口傳回時,指定 hibernate 。 

<a href="http://my.oschina.net/moooofly/blog/282668#">?</a>

1

<code>{next_state, nstatename, nstatedata, time1}</code>

實際這個 time1 可以指定為 hibernate,則重新回到 main loop 時會直接 hibernate 。 

這種方法可能使你的程序頻繁 hibernate 又被喚醒,效率不是很好。 

正确的方式應該是,當你預期你的程序一段時間内不會收到消息才 hibernate 。 

是以推薦下面的方法,直接看下示例代碼: 

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

<code>%% ====================================================================</code>

<code>%% interface function</code>

<code>join</code><code>(userid)-&gt;</code>

<code>    </code><code>io:</code><code>format</code><code>(</code><code>"join userid=~p~n"</code><code>,[userid]),</code>

<code>    </code><code>gen_fsm:sync_send_event(?module, {</code><code>join</code><code>,userid}).</code>

<code>start()-&gt;</code>

<code>    </code><code>gen_fsm:send_event(?module,start).</code>

<code>%% callback function</code>

<code>wait_player({</code><code>join</code><code>,userid},_from, state) -&gt;</code>

<code>    </code><code>print_inner_data(state),</code>

<code>    </code><code>put(p,get(p)+1),</code>

<code>    </code><code>ets:insert(state</code><code>#state.players,{p,userid}),</code>

<code>    </code><code>count=state</code><code>#state.count+1,</code>

<code>    </code><code>if</code>

<code>        </code><code>count =:= 3 -&gt;</code>

<code>            </code><code>io:</code><code>format</code><code>(</code><code>"jump to next phrase -&gt; start~n"</code><code>),</code>

<code>            </code><code>{reply,next_phrase,wait_start,state,5000};</code>

<code>        </code><code>true</code><code>-&gt;</code>

<code>            </code><code>newstate=state</code><code>#state{count=count},</code>

<code>       </code><code>{reply,stand_still,wait_player,newstate,5000}</code>

<code>    </code><code>end.</code>

<code>wait_player(timeout,state)-&gt;</code>

<code>    </code><code>io:</code><code>format</code><code>(</code><code>"timeout happened when wait_player,let's hibernate~n"</code><code>),</code>

<code>    </code><code>proc_lib:hibernate(gen_fsm, enter_loop, [?module, [], wait_player,state]).</code>

<code>wait_start(start,state)-&gt;</code>

<code>    </code><code>io:</code><code>format</code><code>(</code><code>"recv start event ~n"</code><code>),</code>

<code>    </code><code>{next_state,wait_start,state,5000};</code>

<code>wait_start(timeout,state)-&gt;</code>

<code>    </code><code>io:</code><code>format</code><code>(</code><code>"timeout happened when wait_start,let's hibernate~n"</code><code>),</code>

<code>    </code><code>proc_lib:hibernate(gen_fsm, enter_loop, [?module, [], wait_start,state]).</code>

<code>start_link()-&gt;</code>

<code>    </code><code>gen_fsm:start_link({</code><code>local</code><code>,?module}, ?module, [] ,[]).</code>

<code>init([])-&gt;</code>

<code>    </code><code>put(p,0),</code>

<code>    </code><code>{ok,</code>

<code>        </code><code>wait_player,</code>

<code>        </code><code>#state</code>

<code>        </code><code>{</code>

<code>            </code><code>count=0,</code>

<code>            </code><code>players=ets:new(players,[bag])</code>

<code>        </code><code>},</code>

<code>        </code><code>5000</code>

<code>    </code><code>}.</code>

<code>print_inner_data(state)-&gt;</code>

<code>io:</code><code>format</code><code>(</code><code>"inner data: dict=~p,ets=~p~n"</code><code>,[get(p),ets:tab2list(state</code><code>#state.players)]).</code>

運作:  

<code>2&gt; {ok,pid}=fsm:start_link().</code>

<code>{ok,&lt;0.39.0&gt;}</code>

<code>3&gt; process_info(pid,total_heap_size).</code>

<code>{total_heap_size,233}</code>

<code>4&gt; fsm:</code><code>join</code><code>(123).</code>

<code>join</code> <code>userid=123</code>

<code>inner data: dict=0,ets=[]</code>

<code>stand_still</code>

<code>timeout happened when wait_player,</code><code>let</code><code>'s hibernate</code>

<code>5&gt; process_info(pid,total_heap_size).</code>

<code>{total_heap_size,33}</code>

<code>6&gt; fsm:</code><code>join</code><code>(456).</code>

<code>join</code> <code>userid=456</code>

<code>inner data: dict=1,ets=[{p,123}]</code>

      總體上,是讓程序在 main_loop 裡先 timeout ,也就是證明一段時間内都沒有消息到達,然後 gen_fsm 程序是發送 {'gen_event',timeout} 消息處理 timeout 事件的,是以要在每個 mod:statename 處都加個處理 timeout 的分支,來進行 timeout 處理,也就是 hibernate 掉自己,重新進入的是 enter_loop 函數,這個東西很有用呐,大家可以檢視源碼,我就不多說了。 

另外,示例中可以看到,hibernate 會清空 stack,但是不會影響你的程序字典和 ets 等其它東西,可以放心用了。