OpenMP / Pybind11: доступ к объекту python в течение l oop возвращает ошибку интернированной строки - PullRequest
1 голос
/ 12 января 2020

Я пытаюсь использовать OpenMP в списке python объектов с помощью Pybind11 в C ++. Я преобразую этот список в std :: vector из Python объектов (как объяснено в в этом посте ), а затем пытаюсь получить к ним параллельный доступ для l oop. Однако при вызове атрибутов любого python объекта в векторе для l oop я получаю сообщение об ошибке:

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)

Мои вопросы: Что такое удаление ошибки интернированной строки? и как этого избежать в OpenMP?

Я прочитал здесь , что проблема связана с копией строки, поэтому я попытался сослаться на строку с указателем, но это не помогло. Кроме того, проблема не связана с проблемой преобразования в 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

Ответы [ 2 ]

3 голосов
/ 12 января 2020

Вы не можете надежно вызвать любой Python C -API-код (который лежит в основе pybind11) без удержания Global Interpreter Lock (GIL). Передача GIL в OpenMP l oop для каждого доступа в каждом потоке будет эффективно сериализовать l oop, но теперь с дополнительными издержками, поэтому он будет медленнее, чем запускать его последовательно.

Что касается интернированных строк: интерпретатор Python сохраняет общие неизменяемые объекты, такие как определенные строки и маленькие целые числа, чтобы предотвратить их повторное создание. Такие общие строки называются «интернированными», и обычно это происходит под капотом (хотя вы можете добавить свою собственную, используя PyString_InternFromString / PyUnicode_InternFromString). Поскольку они являются одноэлементными объектами (это их цель, в конце концов), только один поток должен создавать / удалять их.

1 голос
/ 13 января 2020

Мне удалось найти решение, но я думаю, что просто выполняю однопотоковую работу с несколькими потоками. Я использовал #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; 
    }

И это работает

РЕДАКТИРОВАТЬ

Я контролировал время выполнения с помощью и без распараллеливания и то же самое

...