Выпуск GIL в PyBind11 для многопоточности в Python с OpenMP

Я использую Pybind11 и пытаюсь использовать в нем OpenMP. Я вызываю функцию C++ из Python, используя интерпретатор PyBind и GIL, затем я вычисляю многопоточный цикл for с OpenMP в C++, в котором я вызываю в каждом потоке функцию Python. Для этого я сначала ограничиваю область GIL с помощью py::gil_scoped_release release; до параллельной области, а затем восстанавливаю ее за пределами параллельной области с помощью `py::gil_scoped_acquire Acquire;

Проблема внутри многопоточного цикла for. Мне нужно создать интерпретатор для каждого потока. Мой вопрос: как это сделать?

Сначала я подумал об использовании py::scoped_interpreter guard{}, но в документации сказано, что:

Создание двух параллельных охранников scoped_interpreter является фатальной ошибкой. То же самое происходит с >вызовом initialize_interpreter() во второй раз после того, как интерпретатор уже был инициализирован.

Не используйте необработанные функции API CPython Py_Initialize и Py_Finalize, так как они неправильно обрабатывают время жизни внутренних данных pybind11.

Второй момент представляет собой настоящую проблему, так как документации больше, но только с использованием Python C-API, а не с объектами Pybind 1 2 3

Я просмотрел документацию, но она не решает именно мою проблему, потому что не не вызывать Python в многопоточности. Я также просмотрел этот 4, но интерпретатор вызывается из С++, а не из Python

Любая подсказка будет высоко оценена, так как я довольно долго был заблокирован по этой проблеме. Вот пример кода для иллюстрации проблемы.

py::object create_seq(
  py::object self
  ){

  std::vector<py::object> dict = self.cast<std::vector<py::object>>();

  py::gil_scoped_release release;

  #pragma omp parallel for ordered schedule(dynamic)
  for(unsigned int i=0; i<dict.size(); i++) {
     ?? WHAT HAPPENS HERE ?? 
    std::cout << i << std::endl;
    #pragma omp ordered
    dict[i].attr("attribute") = open.attr("parallel")().cast<int>();
    }

  py::gil_scoped_acquire acquire;

  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

def func():
    return 2

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

person Joachim    schedule 14.01.2020    source источник
comment
Мне это кажется неправильным: так, как вы написали этот код, у вас есть только gstate со всеми потоками, записывающими в него. Если PyGILState_Ensure правильно блокирует, это может сработать, но я бы не стал на это полагаться.   -  person Wim Lavrijsen    schedule 15.01.2020
comment
Я написал код так, как вы рекомендуете, но я действительно не понимаю, почему это занимает больше времени, чем без распараллеливания. Поскольку Py_BEGIN_ALLOW_THREADS освобождает GIL, а затем PyGILState_Ensure просто гарантирует, что GIL удерживается для каждого потока. (Возможно, я удалю этот вопрос и перепишу новый, так как вопрос уже не тот)   -  person Joachim    schedule 16.01.2020
comment
и это проблема распараллеливания или проблема гиперпоточности? Вы получаете то же самое с гиперпоточностью и без нее?   -  person camelccc    schedule 16.01.2020
comment
@camelccc это проблема гиперпоточности, так как код работает, но я получаю одинаковый результат с гиперпоточностью и без нее с еще большим временем выполнения с гиперпоточностью   -  person Joachim    schedule 16.01.2020
comment
Вы выполняете всю работу под замком, т.е. только один поток активен в любой момент времени. Как он должен быть быстрее, чем последовательный?   -  person Sergei    schedule 16.01.2020
comment
@Sergei Но разве Py_BEGIN_ALLOW_THREADS точно не освобождает блокировку?   -  person Joachim    schedule 16.01.2020
comment
PyGILState_Ensure получает блокировку   -  person Sergei    schedule 16.01.2020
comment
@Sergei Да, хорошо, но если он находится в параллельном регионе, он просто получает блокировку потока, работающего параллельно с другими. Мой способ понять это так же, как наличие GIL в потоках, работающих параллельно. Таким образом, не должно быть никаких проблем с этим, поскольку полученные объекты Python принадлежат каждому потоку и не используются совместно между потоками. Я не знаю, ясно ли мое объяснение, скажите, пожалуйста, если нет.   -  person Joachim    schedule 16.01.2020
comment
Нет, есть только один GIL   -  person Sergei    schedule 16.01.2020
comment
Нет, один GIL на процесс. Период.   -  person Sergei    schedule 16.01.2020
comment
@Сергей, извини, я ошибся. Я имел в виду: есть ли способ создать несколько GIL для нескольких потоков, работающих параллельно, а затем активировать блокировку для каждого потока?   -  person Joachim    schedule 16.01.2020
comment
Давайте продолжим обсуждение в чате.   -  person Joachim    schedule 16.01.2020
comment
Во-первых, спасибо всем за ваши ответы, источник проблемы становится все яснее. Я обновил свой вопрос тем, что я узнал и прочитал сегодня днем   -  person Joachim    schedule 16.01.2020