当我尝试访问由我自己的 OpenMP C++ 扩展返回的变量时,我在 python 解释器中遇到了分段违规。
我找到的所有解决方案都使用 ctypes 或 cython(我不能使用)。http://snatverk.blogspot.de/2012/03/c-parallel-modules-for-python.html展示了一个启用 OpenMP 的 python 扩展的小例子。尽管我尝试像示例中那样实现我的 for 循环,但它仍然不起作用。
我的代码扩展代码功能如下所示:
static PyObject *
matcher_match(PyObject *self, PyObject *args)
{
if(PyTuple_Size(args) != 2)
{
return NULL;
}
PyObject *names = PyTuple_GetItem(args, 0);
Py_ssize_t namesSize = PyList_Size(names);
PyObject *namesB = PyTuple_GetItem(args, 1);
Py_ssize_t namesBSize = PyList_Size(namesB);
PyObject *matchIdcs = PyList_New(namesSize);
Py_BEGIN_ALLOW_THREADS;
int i, j;
#pragma omp parallel for private(i, j)
for(i = 0; i < namesSize; i++)
{
for(j = 0; j < namesBSize; j++)
{
// test_pair_ij is a pure C function without callbacks into python
// it only uses the C++ STL like std::vector
float a = PyFloat_AsDouble(PyList_GetItem(names, i));
float b = PyFloat_AsDouble(PyList_GetItem(namesB, j));
bool res = test_pair_ij(a, b)
PyObject *matchVal;
if(res)
{
matchVal = Py_BuildValue("i", j);
}
else
{
matchVal = Py_BuildValue("i", -1);
}
PyList_SetItem(matchIdcs, i, matchVal);
}
}
Py_END_ALLOW_THREADS;
return matchIdcs;
}
函数 matcher_match() 接收两个列表,names 和 namesB。我检查名称和名称B(它们的浮点属性)的每个组合是否存在由函数test_pair_ij()指示的特定条件。该函数是一个纯 C(++) 实现,不会回调到 python。
C 扩展被调用:
from matcher import match
# some random lists for this example
names = ['123', '231', ...]
namesB = ['342', ...]
matchResult = match(names, namesB)
import pandas as pd
mr = pd.Series(matchResult)
mr.to_csv('matchResult.csv')
当列表名称和名称B 相当小时,代码运行正常。但是对于更大的列表,我无法再在 python 代码中访问 matchResult。当我尝试这样做时,我得到了分段违规(我猜这是在 python 解释器内部)。我已经在没有 openmp 的情况下重新编译了 C 扩展,它再次运行正常,即使是更大的列表。
我想问题是我从我的扩展访问的 python 变量的内存中的一些混乱。这可能与 GIL 有关,尽管我正在发布和获取它。在这种情况下,我是否需要将更多变量设为私有?对此还有其他想法吗?
编辑:修复函数 test_pair_ij 的调用参数。
编辑 2:存储 matchIdcs 的固定代码
答案:
代码正在释放 GIL 并调用PyList_SetItem(matchIdcs, i, matchVal); 正在修改不允许的 python 结构(请参阅http://docs.cython.org/src/userguide/external_C_code.html#releasing-the-gil)。