所以我目前正在尝试为一些 2d ndarray 做类似 A**b 的事情,并为 Python 并行做一个 double b 。我想通过使用 OpenMP 的 C 扩展来做到这一点(是的,我知道,有 Cython 等,但在某些时候,我总是遇到那些“高级”方法的麻烦......)。
所以这里是我的 gaussian.so 的 gaussian.c 代码:
void scale(const double *A, double *out, int n) {
int i, j, ind1, ind2;
double power, denom;
power = 10.0 / M_PI;
denom = sqrt(M_PI);
#pragma omp parallel for
for (i = 0; i < n; i++) {
for (j = i; j < n; j++) {
ind1 = i*n + j;
ind2 = j*n + i;
out[ind1] = pow(A[ind1], power) / denom;
out[ind2] = out[ind1];
}
}
(A 是一个方形双矩阵,out 具有相同的形状,n 是行/列数)所以重点是更新一些对称距离矩阵 - ind2 是 ind1 的转置索引。
我使用gcc -shared -fopenmp -o gaussian.so -lm gaussian.c
. 我通过 Python 中的 ctypes 直接访问该函数:
test = c_gaussian.scale
test.restype = None
test.argtypes = [ndpointer(ctypes.c_double,
ndim=2,
flags='C_CONTIGUOUS'), # array of sample
ndpointer(ctypes.c_double,
ndim=2,
flags='C_CONTIGUOUS'), # array of sampl
ctypes.c_int # number of samples
]
只要我注释#pragma 行,“测试”功能就可以顺利运行 - 否则它会以错误号 139 结束。
A = np.random.rand(1000, 1000) + 2.0
out = np.empty((1000, 1000))
test(A, out, 1000)
当我将内部循环更改为仅打印 ind1 和 ind2 时,它并行运行平稳。当我只访问 ind1 位置并将 ind2 单独放置(甚至并行)时,它也可以工作!我在哪里搞砸了内存访问?我怎样才能解决这个问题?
谢谢你!
更新:嗯,我想这会遇到 GIL,但我还不确定......
更新:好的,我现在很确定,这是邪恶的 GIL 在这里杀死我,所以我更改了示例:
我现在有 gil.c:
#include <Python.h>
#define _USE_MATH_DEFINES
#include <math.h>
void scale(const double *A, double *out, int n) {
int i, j, ind1, ind2;
double power, denom;
power = 10.0 / M_PI;
denom = sqrt(M_PI);
Py_BEGIN_ALLOW_THREADS
#pragma omp parallel for
for (i = 0; i < n; i++) {
for (j = i; j < n; j++) {
ind1 = i*n + j;
ind2 = j*n + i;
out[ind1] = pow(A[ind1], power) / denom;
out[ind2] = out[ind1];
}
}
Py_END_ALLOW_THREADS
}
gcc -shared -fopenmp -o gil.so -lm gil.c -I /usr/include/python2.7 -L /usr/lib/python2.7/ -lpython2.7
这是使用和相应的 Python 文件编译的:
import ctypes
import numpy as np
from numpy.ctypeslib import ndpointer
import pylab as pl
path = '../src/gil.so'
c_gil = ctypes.cdll.LoadLibrary(path)
test = c_gil.scale
test.restype = None
test.argtypes = [ndpointer(ctypes.c_double,
ndim=2,
flags='C_CONTIGUOUS'),
ndpointer(ctypes.c_double,
ndim=2,
flags='C_CONTIGUOUS'),
ctypes.c_int
]
n = 100
A = np.random.rand(n, n) + 2.0
out = np.empty((n,n))
test(A, out, n)
这给了我
Fatal Python error: PyEval_SaveThread: NULL tstate
Process finished with exit code 134
现在不知何故,它似乎无法保存当前线程 - 但 API 文档在这里没有详细介绍,我希望在编写 C 函数时可以忽略 Python,但这似乎很混乱 :( 任何想法? 我觉得这很有帮助:GIL