【協程原理】 - cPython的VM真變態

taowen發表於2014-09-15

kilim在JVM上實現了協程,其實現看起來挺容易的:http://www.malhar.net/sriram/kilim/thread_of_ones_own.pdf
在cPython上是否能夠複製其技法呢?粗看上去,是很容易的,甚至比JVM更好實現:

一個更加牛B的事情是,有人已經用@goto裝飾器的方式在Python上實現了GOTO了
這個是Python 2版本的:http://code.activestate.com/recipes/576944-the-goto-decorator/
這個是Python 3版本的:https://github.com/cdjc/goto

這兩個實現很好的展示瞭如何在Python中做類似JVM上ASM庫做的事情,位元組碼增強。

===============
但是,cPython的VM對於frame狀態的建模使得在cPython上實現kilim,比JVM要難得多。在cPython某個指令執行的那一刻,有五個部分的狀態

  • builtins
  • globals
  • locals
  • value_stack
  • block_stack

前三個都是python裡做動態執行的常客,沒啥困難的。而且frame透過inpsect可以很輕易的獲得locals的值。而後兩者是非常困難的,我們可以來看一段我隨便寫的程式碼

for i in range(1):
    print(i)

生成的bytecode是

      0 SETUP_LOOP         30 (to 33) 
      3 LOAD_GLOBAL         0 (0) 
      6 LOAD_CONST          1 (1) 
      9 CALL_FUNCTION       1 (1 positional, 0 keyword pair) 
     12 GET_ITER        
>>   13 FOR_ITER           16 (to 32) 
     16 STORE_FAST          0 (0) 
     19 LOAD_GLOBAL         1 (1) 
     22 LOAD_FAST           0 (0) 
     25 CALL_FUNCTION       1 (1 positional, 0 keyword pair) 
     28 POP_TOP         
     29 JUMP_ABSOLUTE      13 
>>   32 POP_BLOCK       
>>   33 LOAD_CONST          0 (0) 
     36 RETURN_VALUE 

我們可以看到一個簡單的迴圈,產生了一對SETUP_LOOP和POP_BLOCK,這個是操作block_stack的。以及GET_ITER和FOR_ITER這是操作value_stack的。如果我們想要直接跳入到print(i)這一行,那麼就要恢復當時的block_stack,以及當時的value_stack。而且要特別注意GET_ITER的含義是把棧頂的值標識為ITER,使得FOR_ITER可以使用,所以還不是簡單地把value_stack恢復就可以了

GET_ITER
Implements TOS = iter(TOS).

這篇部落格把python的frame內部狀態講得非常清楚:http://tech.blog.aknin.name/tag/block-stack/
以上可以解釋為什麼沒有人在python裡搞位元組碼的trick了,因為這個VM太變態。

相關文章