web-dev-qa-db-ja.com

Cythonで、特にnogil内で辞書を使用する

辞書を持っています

my_dict = {'a':[1,2,3], 'b':[4,5] , 'c':[7,1,2])

Cython nogil関数内でこの辞書を使用したいと思います。だから、私はそれを次のように宣言しようとしました

cdef dict cy_dict = my_dict 

この段階までは結構です。

次に、my_dictのキーを反復処理する必要があります。値がリストにある場合は、それを反復処理します。 Pythonで、次のように非常に簡単です:

 for key in my_dict:
      if isinstance(my_dict[key], (list, Tuple)):
          ###### Iterate over the value of the list or Tuple
          for value in list:
               ## Do some over operation.

しかし、Cythonの内部では、nogilの内部にも同じことを実装したいと思います。 pythonオブジェクトはnogil内では許可されていないので、私はすべてここで立ち往生しています。

with nogil:
    #### same implementation of the same in Cython

誰か私を助けてくれますか?

14
Seja Nair

本当に賢明な唯一のオプションは、残念ながらGILが必要であることを受け入れることです。 C++マップを含むあまり賢明でないオプションもありますが、特定のケースに適用するのは難しい場合があります。

with gil:を使用してGILを再取得できます。ここには明らかなオーバーヘッドがあります(GILを使用するパーツは並行して実行できず、GILを待機する遅延がある可能性があります)。ただし、辞書操作がCythonコードの大きな部分の小さなチャンクである場合、これはそれほど悪くないかもしれません。

with nogil:
  # some large chunk of computationally intensive code goes here
  with gil:
    # your dictionary code
  # more computationally intensive stuff here

他のあまり賢明でないオプションは、C++マップを使用することです(他のC++標準ライブラリデータ型と一緒に)。 Cythonはこれらをラップして自動的に変換できます。サンプルデータに基づいて簡単な例を示すには:

from libcpp.map cimport map
from libcpp.string cimport string
from libcpp.vector cimport vector
from cython.operator cimport dereference, preincrement

def f():
    my_dict = {'a':[1,2,3], 'b':[4,5] , 'c':[7,1,2]}
    # the following conversion has an computational cost to it 
    # and must be done with the GIL. Depending on your design
    # you might be able to ensure it's only done once so that the
    # cost doesn't matter much
    cdef map[string,vector[int]] m = my_dict

    # cdef statements can't go inside no gil, but much of the work can
    cdef map[string,vector[int]].iterator end = m.end()
    cdef map[string,vector[int]].iterator it = m.begin()

    cdef int total_length = 0

    with nogil: # all  this stuff can now go inside nogil   
        while it != end:
            total_length += dereference(it).second.size()
            preincrement(it)

    print total_length

(これをlanguage='c++'でコンパイルする必要があります)。

これの明らかな欠点は、dict内のデータ型が事前にわかっている必要があることです(任意のPythonオブジェクトにすることはできません)。ただし、任意の=を操作できないため、 Pythonとにかくかなり制限されているnogilブロック内のオブジェクト。

26
DavidW