greenlet implemenation

5964 ワード

greenletはpython coroutineライブラリであり、threadよりも細粒度の同時制御を提供します.threadは通常カーネルオブジェクトであり、coroutineは通常ユーザーモードでのみ動作するため、スレッド切り替え時のオーバーヘッドは存在しません.coroutineとthreadを使用して同時化を実現すると、より大きな同時性が得られます.threadはprocessよりも軽量ですが、結局オーバーヘッドがあり、システムに多くのthreadが実行されている場合、オペレーティングシステムがcontext switchの実行に忙しいため、システムの実行速度を牽引します.
pythonでは、GIL(Global Interpreter Lock)の問題に関連する完全な同時実行ではないため、threadから得られる同時性は想像以上に低いと述べています.GILについては、Understanding the Python GILを参照してください.
グリーンレットの実装について簡単に説明します.
switch to a new greenlet stack overview
  high  +----------------+    ^    |    old data    |    |    +----------------+    |    |    g_switch    |    |    |----------------|    |    |  dummy marker  |    |    |----------------| <------- stack_stop  _    |    |   other data   |                      /    |    +----------------+                       |    |    |  g_initialstub |                       |    |    +----------------+                       |    |    |  g_switchstack |                       |    |    +----------------+                      /__ greenlet stack    |    |   slp_switch   |                      /    |    |----------------|                       |    |    |   stsizediff   |                       |    |    |----------------|                       |    |    |    stackref    |                       |    |    +----------------+ <------- stack_start _/    |    | slp_save_state |    |    +----------------+    |    |     g_save     |   low   +----------------+
slp_を呼び出すsave_state target greenlet stackを保存した後、slp_switchは1を返し、制御フローはg_に戻るinitialstub.
PyEveal_が呼び出されますCallObjectWithKeywordsは、greenletのrun関数を呼び出し、この関数から戻るとparent greenletに切り替わります.通常、greenletを作成するときにparentを指定しない場合は、デフォルトは現在実行されているgreenlet:ts_です.current (green_new). 切り替え時にg_を呼び出すswitchは、runの戻り値をswitchに返します.
新しいgreenletを起動すると、新しいgreenletのstackは必ず既存のgreenletの下にあります.
running in a greenlet B, which is started by greenlet A, and try to switch to greenlet A. stack overview
   high  +---------------+     ^    |    old data   |     |    +---------------+     |    |    g_switch   |     |    |---------------|     |    |     marker    |     |    +---------------+     |    | g_initialstub |     |    +---------------+     |    |    call run   |     |    +---------------+     |    |    ... ...    |     |    +---------------+     |    |   g_switch    |     |    |---------------|     |    |     marker    |     |    +---------------+     |    | g_initialstub |     |    +---------------+     |    |    call run   |     |    +---------------+     |    |    ... ...    |     |    +---------------+     |    |    g_switch   |     |    +---------------+     |    | g_switchstack |     |    +---------------+     |    |   slp_switch  |     |    |---------------|     |    |   stsizediff  |     |    |---------------|     |    |    stackref   |     |    +---------------+     |    | slp_save_state|     |    =================    low
すでに実行されているgreenletに切り替えるとslp_save_stateはもっと多くのものをsaveします.もとは新しいgreenletに切り替える、save prev greenlet(通常はcalling greenlet)の状態だけが必要である.現在、複数のcalling greenletが実行されているため、これらのgreenletsを順次saveする必要があります.
グリーンレットAに切り替えると、A.stack_stop < ts_current.stack_startの場合、restore Aのstackの後、中に空きが発生します.
例えばA->B->C,C switch to A,このときstackにはAのみ,その後A switch to C,このときCにはsaveのstack_copyはAのstackと空きギアを生成し、この空きギアは元のBによるものです.
 
return from a greenlet呼び出し完了slp_save_state後、すでに実行されているgreenletに切り替えますので、呼び出します.
    stsizediff = ts_target->stack_start - (char*)stackref

ここでstackの を します.target greenletにジャンプした 、 たちのtargetのstackは しい があります.stackrefを えていますか?この はslp_に び されています.switchの 、espの 、つまりslp_save_stateのスタック、 はsaveからslp_switchまでです.
に く
    mov     eax, stsizediff
    add     esp, eax
    add     ebp, eax

ここでespとebpをtarget greenlet び しslp_に するswitchの .
その 、slp_を び します.restore_state
    memcpy(g->stack_start, g->stack_copy, g->stack_saved);
    PyMem_Free(g->stack_copy);
    g->stack_copy = NULL;
    g->stack_saved = 0;

ここでgはtarget greenletであり、ここでheapのstack copyを の のrestoreに すると、heapのstackがfree、すなわち、 のgreenlet(ts_current)であれば、 ずstack_copy.
slp_からswitchが った 、thread stateを し、frameをターゲットのtop_に けました.frameは、pythonのipポインタを する を します.
ここで なのはslp_save_stateとslp_restore_stateは じ にあります.saveのstackの にはslpが まれているからですswitchのstack frameは、もちろんrestoreの 、この で を しなければなりません.
 
switch to self
greenlet.getcurrentは のgreenletを ることができ、greenletを に す はありません.switch to selfの 、 はno-opです.
 
び しg_switch 、 に り わるのでPyGreenlet_ACTIVEはtrueを し、g_に ります.switch stack、その slp_に りますswitch、ここでslp_が び されます.save_state.
    while (ts_current->stack_stop < target_stop)
    {
        /* ts_current is entierely within the area to free */
        if (g_save(ts_current, ts_current->stack_stop))
            return -1;  /* XXX */
        ts_current = ts_current->stack_prev;
    }
    if (ts_current != ts_target)
    {
        if (g_save(ts_current, target_stop))
            return -1;  /* XXX */
    }

なぜならts_current = ts_targetなのでwhileとifには らないので、stateはsaveされません.これは しいです. のgreenletのstack_copy = NULL.
その slp_に みますrestore_state
    if (ts_current->stack_stop == g->stack_stop)
        g->stack_prev = ts_current->stack_prev;
    else
        g->stack_prev = ts_current;

なぜならts_current = g = ts_targetだからここはno-op
にg_に るswitchstack
    tstate->frame = ts_target->top_frame;

ここでthread stateのframeを するか、no-opか、 にg_に るswitch、 はswitchパラメータを し、 します.