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に切り替えますので、呼び出します.
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パラメータを し、 します.