2

現在、Linuxマシン(Ubuntu 12.04.1 LTS)と新しいMac(OS X 10.7.4)の間でコードを移植しようとしていますが、Pythonのctypesモジュールを使用してC標準ライブラリにアクセスすると混乱する動作が発生します。 Mac。

問題を説明するために、以下は最小限の例です。

import ctypes as C
import numpy as np

libc = C.CDLL("/usr/lib/libc.dylib")   #/usr/lib/libc.so.6 on ubuntu

np.arange(10,dtype="ubyte").tofile("test.bin") # create some test data

buffer_array = np.empty(10,dtype="ubyte") # create a reading buffer

buffer_array_c = np.ctypeslib.as_ctypes(buffer_array) # get the ctypes version of the buffer 

c_file = libc.fopen("test.bin","r") # open the file through libc   

libc.fread(buffer_array_c, 1, 10, c_file) # read from the file

libc.fclose(c_file)

print "Desired output:"
print np.fromfile("test.bin",dtype="ubyte")
print
print "Actual output:"
print buffer_array

Linuxでは、これは期待どおりに機能し、次のようになります。

Desired output:
[0 1 2 3 4 5 6 7 8 9]

Actual output:
[0 1 2 3 4 5 6 7 8 9]

ただし、Macでは、「セグメンテーション違反:11」が発生します。

私はこれを少し実験して、fopen呼び出しを次のものと交換しました:

py_file = open("test.bin","r")

c_file = C.pythonapi.PyFile_AsFile(C.py_object(py_file))

これはLinuxでも機能しますが、Macでは機能しません。

問題は、c_fileを使用してfreadを呼び出すことから生じていると思います。ファイルを開くための最小限のC関数を記述してから、以前に割り当てられたバッファーを使用してfreadを呼び出すと、コードは期待どおりに実行されます。

私は通常Macユーザーではないので、問題は明らかかもしれませんが、どんな助けでも非常に役に立ちます。

参考までに、私は以下を使用しています。

Python 2.7.3、Numpy 1.4.0、Ctypes 1.1.0

編集:

これにコンテキストを与えるために、私は非常に大きなバイナリファイル(〜40-200 GB)をPythonに1つずつ読み込む高速メソッドを試しています。コメント提供者が以下で指摘しているように、標準ライブラリのfreadおよびfwrite関数に直接アクセスすることによるパフォーマンスの向上は実際にはありません。これは本当ですが、私はその理由について混乱しています。numpy.fromfileを使用して大きなファイルをチャンクで読み取る場合、読み取るたびに新しいメモリ割り当てを作成しませんか?

解決:

この問題は、ファイルハンドルのストレージの64ビット/32ビットの違いに起因しているようです。解決策は、使用する前に各c関数のrestypeとargtypesを明示的に設定することです。

つまり、64ビットマシンでは、C.CDLL呼び出しの後にこれを配置します。

lib.fopen.restype = C.c_long
lib.fread.argtypes = [C.c_void_p, C.c_size_t, C.c_size_t, C.c_long]
lib.fclose.argtypes = [C.c_long]

32ビットマシンを使用している場合:

lib.fopen.restype = C.c_int
lib.fread.argtypes = [C.c_void_p, C.c_size_t, C.c_size_t, C.c_int]
lib.fclose.argtypes = [C.c_int]
4

1 に答える 1

6

32 ビットの Ubuntu と 64 ビットの OS/X を試していますか? 問題は、libc.fopen() のバージョンが C の「int」を返すことだと思います。これはほとんどの場合 32 ビット値ですが、実際の fopen() はポインターを返します。そのため、64 ビット オペレーティング システムでは、取得した c_file は 32 ビット整数に切り捨てられます。32 ビット オペレーティング システムでは、32 ビット整数を fread() と fclose() に戻すことができ、再びポインターとして解釈されるため、とにかく動作します。これを修正するには、libc.fopen() の restype を宣言する必要があります。

(私が推奨できるのは、より健全なデフォルトを持つ ctypes の代替手段としてCFFIだけですが、もちろん、著者の 1 人である私は部分的です :-)

于 2012-11-16T01:41:10.173 に答える