Python C 拡張機能のメモリ管理のドキュメントを読んでいますが、私が知る限りでmalloc
は、PyMem_Malloc
. Python ソース コードに公開されず、ガベージ コレクションされるオブジェクトに格納される配列を割り当てたいとします。を使用する理由はありますmalloc
か?
3 に答える
編集:混合PyMem_Malloc
およびPyObject_Malloc
修正。これらは 2 つの異なる呼び出しです。
PYMALLOC_DEBUG
マクロがアクティブ化されていない場合、PyMem_Malloc
は libc の のエイリアスであり、malloc()
特別なケースが 1 つあります:PyMem_Malloc
ゼロ バイトを割り当てるために呼び出すと、NULL 以外のポインターが返されますが、malloc(zero_bytes) は NULL 値を返すか、システム エラーが発生する可能性があります (ソース コード参照)。
/* malloc. nbytes==0 は非 NULL ポインターを返そうとすることに注意してください。
- 他のすべての現在有効なポインターから。これは不可能な場合があります。*/
また、pymem.h
ヘッダー ファイルに関する注意事項があります。
PyMem_ への呼び出しとプラットフォームの malloc/realloc/calloc/free への呼び出しを混在させないでください。たとえば、Windows では、異なる DLL が異なるヒープを使用することになり、PyMem_Malloc を使用すると、Python DLL が使用するヒープからメモリを取得します。独自の拡張機能で直接 free() を実行すると、大惨事になる可能性があります。代わりに PyMem_Free を使用すると、Python が確実にメモリを適切なヒープに戻すことができます。別の例として、PYMALLOC_DEBUG モードでは、Python はすべての PyMem_ および PyObject_ メモリ関数へのすべての呼び出しを、動的メモリ ブロックに追加のデバッグ情報を追加する特別なデバッグ ラッパーでラップします。システム ルーチンはそのようなものをどうするかわかりませんし、Python ラッパーはシステム ルーチンによって直接取得された生のブロックをどうするかわかりません。
次に、内部に Python 固有の調整がいくつかあります。この関数は、C 拡張機能だけでなく、やなどの Python プログラムの実行中にすべての動的割り当てに使用されます。PyMem_Malloc
PyObject_Malloc
100*234
str(100)
10 + 4j
>>> id(10 + 4j)
139721697591440
>>> id(10 + 4j)
139721697591504
>>> id(10 + 4j)
139721697591440
前のcomplex()
インスタンスは、専用プールに割り当てられた小さなオブジェクトです。
小さなオブジェクト (<256 バイト) の割り当ては、ブロック サイズごとに 1 つのプールが存在する 8 バイト アラインされたブロックのプールから行われるため、非常に効率的です。より大きな割り当て用のページとアリーナのブロックもあります.PyMem_Malloc
PyObject_Malloc
ソース コードに関するこのコメントは、呼び出しがどのようにPyObject_Malloc
最適化されるかを説明しています。
/*
* The basic blocks are ordered by decreasing execution frequency,
* which minimizes the number of jumps in the most common cases,
* improves branching prediction and instruction scheduling (small
* block allocations typically result in a couple of instructions).
* Unless the optimizer reorders everything, being too smart...
*/
プール、ページ、およびアリーナは、実行時間の長い Python プログラムの外部メモリの断片化を減らすことを目的とした最適化です。
Python のメモリ内部に関する完全な詳細ドキュメントについては、ソース コードを確認してください。
拡張機能が malloc やその他のシステム アロケータを使用してメモリを割り当てることはまったく問題ありません。これは、多くの種類のモジュールにとって正常であり、避けられません。他のライブラリをラップするほとんどのモジュールは、Python について何も知らないため、そのライブラリ内で発生するとネイティブ割り当てが発生します。(一部のライブラリでは、これを防ぐのに十分な割り当てを制御できますが、ほとんどのライブラリではできません。)
PyMem_Malloc の使用には重大な欠点があります。使用時には GIL を保持する必要があります。ネイティブ ライブラリは、多くの場合、CPU を集中的に使用する計算を行ったり、I/O などのブロックする可能性のある呼び出しを行ったりするときに、GIL を解放したいと考えています。割り当ての前に GIL をロックする必要があることは、非常に不便な場合とパフォーマンスの問題の間の場合があります。
メモリ割り当てに Python のラッパーを使用すると、Python のメモリ デバッグ コードを使用できます。ただし、Valgrind のようなツールの場合、その実際の価値は疑問です。
API で必要な場合は、これらの関数を使用する必要があります。たとえば、これらの関数で割り当てる必要があるポインタが API に渡された場合は、これらの関数で解放できます。それらを使用するそのような明確な理由がない限り、私は通常の割り当てに固執します。
MATLAB .mex関数を作成した経験から、mallocを使用するかどうかを決定する最大の要因は移植性だと思います。内部cデータ型のみを使用して有用な関数のロードを実行するヘッダーファイルがあり(Pythonオブジェクトの相互作用は不要なので、mallocを使用しても問題ありません)、そのヘッダーファイルを別のコードベースに移植する必要があることに突然気付いたとします。 Pythonとは何の関係もありません(おそらくそれは純粋にCで書かれたプロジェクトです)、mallocを使用することは明らかにはるかに移植性の高いソリューションです。
しかし、純粋にPython拡張であるコードの場合、私の最初の反応は、ネイティブc関数がより高速に実行されることを期待することです。これを裏付ける証拠はありません:)