OpenMP / Pybind11:访问 python for 循环中的对象 returns 驻留字符串错误
OpenMP / Pybind11: Accessing python object in for loop returns interned string error
我正在尝试通过在 C++ 中使用 Pybind11 在 python 对象列表上使用 OpenMP。我将此列表转换为 std::vector 个 Python 对象(如 中所述),然后尝试在并行化的 for 循环中访问它们。但是,在 for 循环中调用向量中任何 python 对象的属性时,出现错误:
Fatal Python error: deletion of interned string failed
Thread 0x00007fd282bc7700 (most recent call first):
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
我的问题是:什么是删除interned string错误?以及如何在 OpenMP 中避免它?
我读到 here 问题与字符串的副本有关,所以我尝试用指针引用该字符串,但没有帮助。此外,问题并非来自 Pybind 中的转换问题,因为如果我删除 #pragma omp 子句,代码将完美运行。
如有任何帮助,我们将不胜感激。
C++代码
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <omp.h>
#include <chrono>
#include <thread>
namespace py = pybind11;
py::object create_seq(
py::object self
){
std::vector<py::object> dict = self.cast<std::vector<py::object>>();
#pragma omp parallel for
for(unsigned int i=0; i<dict.size(); i++) {
dict[i].attr("attribute") = 2;
}
return self;
}
PYBIND11_MODULE(error, m){
m.doc() = "pybind11 module for iterating over generations";
m.def("create_seq", &create_seq,
"the function which creates a sequence");
}
Python代码
import error
class test():
def __init__(self):
self.attribute = None
if __name__ == '__main__':
dict = {}
for i in range(50):
dict[i] = test()
pop = error.create_seq(list(dict.values()))
编译:
g++ -O3 -Wall -shared -std=c++14 -fopenmp -fPIC `python3 -m pybind11 --includes` openmp.cpp -o error.so
如果不持有全局解释器锁 (GIL),您将无法可靠地调用 any Python C-API 代码(它是 pybind11 的基础)。在 OpenMP 循环中为每个线程的每次访问处理 GIL 将有效地序列化循环,但现在增加了开销,因此它会比 运行 首先串行地慢。
至于 interned 字符串:Python 解释器保存常见的不可变对象,例如某些字符串和小整数,以防止它们被一遍又一遍地创建。这种常见的字符串被称为 "interned",这通常发生在引擎盖下(尽管您可以使用 PyString_InternFromString
/PyUnicode_InternFromString
添加自己的字符串)。由于这些是设计上的单例对象(毕竟这是它们的目的),所以只有一个线程应该 create/delete 它们。
我找到了解决方案,但我认为我只是在使用多线程进行单线程工作。我使用了按以下方式订购的#pragma omp:
std::vector<py::object> dict = self.cast<std::vector<py::object>>();
#pragma omp parallel for ordered schedule(dynamic)
for(unsigned int i=0; i<dict.size(); i++) {
py::object genome = dict[i];
std::cout << i << std::endl;
#pragma omp ordered
genome.attr("fitness")=2;
}
这行得通
编辑
我控制了有和没有并行化的执行时间,它是一样的
我正在尝试通过在 C++ 中使用 Pybind11 在 python 对象列表上使用 OpenMP。我将此列表转换为 std::vector 个 Python 对象(如
Fatal Python error: deletion of interned string failed
Thread 0x00007fd282bc7700 (most recent call first):
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
我的问题是:什么是删除interned string错误?以及如何在 OpenMP 中避免它?
我读到 here 问题与字符串的副本有关,所以我尝试用指针引用该字符串,但没有帮助。此外,问题并非来自 Pybind 中的转换问题,因为如果我删除 #pragma omp 子句,代码将完美运行。
如有任何帮助,我们将不胜感激。
C++代码
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <omp.h>
#include <chrono>
#include <thread>
namespace py = pybind11;
py::object create_seq(
py::object self
){
std::vector<py::object> dict = self.cast<std::vector<py::object>>();
#pragma omp parallel for
for(unsigned int i=0; i<dict.size(); i++) {
dict[i].attr("attribute") = 2;
}
return self;
}
PYBIND11_MODULE(error, m){
m.doc() = "pybind11 module for iterating over generations";
m.def("create_seq", &create_seq,
"the function which creates a sequence");
}
Python代码
import error
class test():
def __init__(self):
self.attribute = None
if __name__ == '__main__':
dict = {}
for i in range(50):
dict[i] = test()
pop = error.create_seq(list(dict.values()))
编译:
g++ -O3 -Wall -shared -std=c++14 -fopenmp -fPIC `python3 -m pybind11 --includes` openmp.cpp -o error.so
如果不持有全局解释器锁 (GIL),您将无法可靠地调用 any Python C-API 代码(它是 pybind11 的基础)。在 OpenMP 循环中为每个线程的每次访问处理 GIL 将有效地序列化循环,但现在增加了开销,因此它会比 运行 首先串行地慢。
至于 interned 字符串:Python 解释器保存常见的不可变对象,例如某些字符串和小整数,以防止它们被一遍又一遍地创建。这种常见的字符串被称为 "interned",这通常发生在引擎盖下(尽管您可以使用 PyString_InternFromString
/PyUnicode_InternFromString
添加自己的字符串)。由于这些是设计上的单例对象(毕竟这是它们的目的),所以只有一个线程应该 create/delete 它们。
我找到了解决方案,但我认为我只是在使用多线程进行单线程工作。我使用了按以下方式订购的#pragma omp:
std::vector<py::object> dict = self.cast<std::vector<py::object>>();
#pragma omp parallel for ordered schedule(dynamic)
for(unsigned int i=0; i<dict.size(); i++) {
py::object genome = dict[i];
std::cout << i << std::endl;
#pragma omp ordered
genome.attr("fitness")=2;
}
这行得通
编辑
我控制了有和没有并行化的执行时间,它是一样的