Python3.12 Ошибки C-API с openMP

Вот небольшая программа на C++, встраивающая Python.

Он работает с Python 3.11.6, но с Python 3.12.0 происходит сбой:

#include <iostream>
#include "omp.h"
#include "Python.h"

int main()
{
    Py_Initialize();
    
    #pragma omp parallel
    {
        #pragma omp single
        {
            std::cout << "One character:"<<std::endl;
            PyObject *nameobj1 = PyUnicode_FromString("a");
            std::cout << nameobj1 << std::endl;
            Py_DECREF(nameobj1);
            
            std::cout << "Two characters:"<<std::endl;
            PyObject *nameobj2 = PyUnicode_FromString("aa");
            std::cout << nameobj2 << std::endl;
            Py_DECREF(nameobj2);
        }
    }
    
    Py_Finalize();
}

Компиляция и запуск с версией 3.11:

$ g++ pytest.cpp `python3.11-config --ldflags --cflags` -lpython3.11 -fopenmp
$ ./a.out 
One character:
0x730a12d466e0
Two characters:
0x730a121f33f0

Компиляция и запуск с версией 3.12:

$ g++ pytest.cpp `python3.12-config --ldflags --cflags` -lpython3.12 -fopenmp
$ ./a.out 
One character:
0x734752e48a08
Two characters:
Segmentation fault (core dumped)

Изменилось ли что-то в Python 3.12, что не позволяет использовать PyUnicode_FromString более чем с одним символом в openMP? Есть ли обходной путь?

Примечания:

  • г++ 13.2.0
  • 2 потока openMP
  • это действительно работает, когда не пользуешься -fopenmp
  • Вот обратная трассировка с использованием gdb:
#0  0x00007ffff77f3f80 in _PyInterpreterState_GET () at ../Include/internal/pycore_pystate.h:118
#1  get_state () at ../Objects/obmalloc.c:866
#2  _PyObject_Malloc (ctx=<optimized out>, nbytes=43) at ../Objects/obmalloc.c:1563
#3  0x00007ffff782b509 in PyUnicode_New (maxchar=<optimized out>, size=2) at ../Objects/unicodeobject.c:1208
#4  PyUnicode_New (size=2, maxchar=<optimized out>) at ../Objects/unicodeobject.c:1154
#5  0x00007ffff7837081 in unicode_decode_utf8 (s=<optimized out>, size=2, error_handler=_Py_ERROR_UNKNOWN, errors=0x0, consumed=0x0)
    at ../Objects/unicodeobject.c:4647
#6  0x0000555555555422 in main._omp_fn.0(void) () at pytest.cpp:19
#7  0x00007ffff7f6b48e in gomp_thread_start (xdata=<optimized out>) at ../../../src/libgomp/team.c:129
#8  0x00007ffff6e97b5a in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#9  0x00007ffff6f285fc in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

Я только что узнал, что замена #pragma omp single на #pragma omp master работает! Я думаю, это обходной путь, но для меня это не имеет смысла.

fffred 21.03.2024 14:36

Судя по ответу ниже, такое поведение имеет смысл. Главный поток владеет блокировкой и поэтому может получить доступ к GIL без ошибок. Использование single выбирает случайный поток, который в данный момент может не владеть блокировкой.

Joachim 22.03.2024 10:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем коде есть ошибка, вы никогда не получаете GIL внутри дочерних потоков, вы должны получить GIL при создании или удалении (или изменении) любого объекта Python (за некоторыми исключениями в части изменения), ваш код просто не аварийно завершился в python3.11, но происходит сбой в python3.12

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

Чтобы получить и удалить GIL, используйте PyGILState_Ensure и PyGILState_Release соответственно.

Вам также необходимо удалить GIL из основного потока перед параллельным разделом, чтобы избежать взаимоблокировок.

Я думаю, что самым большим изменением является Per Interpreter GIL, который был добавлен в python3.12, который переместил больше состояний в раздел threadlocal, что привело к сбою вашего кода. До этого изменения ваш код был неправильным, но он не давал сбоев.

Спасибо за это. Означает ли это, что внутри параллельного раздела невозможно вызывать функции Py? Могу ли я заблокировать GIL перед параллельным разделом, а затем удалить его после параллельного раздела?

fffred 21.03.2024 14:54

@fffred, вы можете выполнять вызовы внутри параллельного раздела, просто тот, кто выполняет этот вызов, должен заблокировать gil перед выполнением вызова и разблокировать его, когда вызов закончится, в основном только один поток может использовать объекты Python одновременно, вы можете создайте несколько интерпретаторов в одном процессе, используя API подинтерпретаторов, но вы не можете передавать объекты Python между этими интерпретаторами.

Ahmed AEK 21.03.2024 14:58

@fffred, я думаю, что ваш код специально ломается в python3.12 из-за каждого интерпретатора-gil, который был добавлен в python3.12, это привело к самым большим изменениям в локальных данных потока в интерпретаторе.

Ahmed AEK 21.03.2024 15:05

Еще раз спасибо за эти объяснения. К сожалению, это очень сложно проверить, поскольку это кажется случайным.

fffred 21.03.2024 15:07

Другие вопросы по теме