Программа OpenMP с потоками C останавливается при вызове скрипта Python (с использованием matplotlib) в параллельной области

У меня есть многопоточная программа на C с использованием OpenMP. В разделе кода parallel for выполняется системный вызов скрипта Python, который отображает данные, сгенерированные в этой конкретной итерации цикла. У меня проблема, когда программа внезапно останавливается в одном и том же месте после тысяч итераций и часов работы. После запуска ps -A, чтобы увидеть, какие процессы выполняются в стойле, я заметил n-много экземпляров python (где n = количество потоков), что заставило меня поверить, что что-то происходит со скриптом python. Я переключился с системного вызова на скрипт Python на встроенный Python, надеясь, что это может решить проблему. Сейчас я вижу, что запуск скрипта python выдает предупреждение:

OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.

Я не использую в своем коде ничего, связанного с BLAS, но после проб и ошибок я обнаружил, что это создается matplotlib, который зависит от numpy, который, как я понимаю, в свою очередь, использует OpenBLAS. Я подозреваю, что это может быть виновником возможного срыва, но я не уверен, как это исправить. Я попытался установить OpenBLAS со страницы GitHub и сделать это с флагом USE_OPENMP=1, как описано здесь:

Как заставить openBLAS работать с openMP?

Это, я полагаю, неудивительно, не решило проблему. Я подозреваю, что мне придется переделать любой источник BLAS, который использует matplotlib? В любом случае я привел минимальный пример, воспроизводящий нежелательное поведение. Код OpenMP C находится здесь:

#include <sys/wait.h>
#include <python2.7/Python.h>
#include "omp.h"

int NUM_THREADS = 8;        // Default number of threads for OpenMP

int main(int argc, char * argv[])
{
   # ifdef _OPENMP
   printf("Compiled by an OpenMP-compliant implementation.\n");
   # endif

   omp_set_dynamic(0);
   omp_set_num_threads(NUM_THREADS);

   int nThreads = 0;

   #pragma omp parallel
   {

   #pragma omp master
   nThreads = omp_get_num_threads();

   #pragma omp for
   for (int i = 0; i < 8; i++)  // Loop through number of samples
   {
     // Create directories to store and run the python script
     char system_buffer[300] = "\0";
     snprintf(system_buffer, sizeof(system_buffer), "mkdir -p %d", i+1);
     int systemRet = system(system_buffer);
     if(systemRet == -1)
     {
       // The system method failed
     }

     // Copy the Python Script to the working directory
     char system_buffer_py[300] = "\0";
     snprintf(system_buffer_py, sizeof(system_buffer_py), "cp test.py %d", i+1);
     int systemRet_py = system(system_buffer_py);
     if(systemRet_py == -1)
     {
       // The system method failed
     }

     int pid;

     // Child
     if ((pid = fork()) == 0)
     {
       int argc = 0;
       char* argv[1];
       argv[0] = NULL;

       char python_script[300] = "\0";
       snprintf(python_script, sizeof(python_script), "%d/test.py", i+1);

       FILE *stream = fopen(python_script, "r");

       Py_SetProgramName(argv[0]);
       Py_Initialize();
       PySys_SetArgv(argc, argv);
       PyRun_AnyFile(stream, python_script);
       Py_Finalize();

       fclose(stream);

       exit(0);
     } 
     // Parent
     else
     {
       int status;
       waitpid(pid, &status, 0);
     }
   }

   }//end of: pragma omp parallel

   if  (nThreads == NUM_THREADS) 
   {
     printf("The expected number of threads, %d, were used.\n", NUM_THREADS);
   }
   else 
   {
     printf("Expected %d OpenMP threads, but %d were used.\n", NUM_THREADS, nThreads);
   }

   return(0);
}

Это можно скомпилировать с помощью:

gcc -Wall -O3 -fopenmp test.c -o test -L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpython2.7 -ldl -export-dynamic -lm

Сценарий Python находится здесь и должен находиться в том же каталоге, что и код C:

import os
import matplotlib.pyplot as plt

plot_name = os.path.dirname(__file__) + '/test.png'

plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.savefig(plot_name)

Результат запуска кода C:

Compiled by an OpenMP-compliant implementation.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.
The expected number of threads, 8, were used.

Таким образом, это предупреждение выдается 3 раза для каждого потока (с использованием 8 потоков). Любой вклад в решение этой проблемы будет принят с благодарностью.


person Leigh K    schedule 28.03.2019    source источник
comment
Смешение тем и fork() возможно, но чрезвычайно сложно. По сути, после запуска нескольких потоков вы не можете использовать какие-либо асинхронно-небезопасные функции после fork() до тех пор, пока не exec(). Вы, конечно, не можете запустить интерпретатор Python в дочернем процессе un-exec()ed.   -  person EOF    schedule 29.03.2019
comment
Достаточно справедливое замечание. Первоначально я просто использовал system(), которая по сути является fork() и exec() с накладными расходами оболочки. Консенсус, который я читаю, заключается в том, что fork(), за которым следует немедленный exec(), по существу является потокобезопасным, что я могу сделать с помощью скрипта python, вызывающего matplotlib. Я подозреваю, что проблема с OpenBLAS останется ...   -  person Leigh K    schedule 29.03.2019
comment
Кроме того, стоит отметить еще одну вещь: я также распараллелил эту программу, используя MPI, а не OpenMP, который, очевидно, вообще не использует потоки, а просто несколько экземпляров программы. Тот же срыв произошел на том же месте. Я попробую встроенный подход Python с этой версией, чтобы увидеть, появляется ли подобное предупреждение из скрипта Python.   -  person Leigh K    schedule 29.03.2019


Ответы (1)


Зная, что ссылка не может быть ответом, но поскольку я никогда не пытался смешивать потоки и fork(). Эта статья может помочь.

person C.Y    schedule 02.04.2019