c++ multithreading fortran fortran77 fortran-common-block

Fortran 77 bloques comunes en aplicaciones multiproceso C++



multithreading fortran77 (4)

Sí, no puedes usar áreas comunes con multihilo. Y no, no hay forma de evitar esto. Todas las áreas comunes son realmente combinadas por el enlazador en un solo bloque, y no hay forma de copiarlo entre subprocesos. Es un problema conocido en todas partes donde existe el código Fortran heredado. La solución más común es usar multiprocesamiento en lugar de multihilo.

Desarrollo un programa en C ++ que llama a una rutina Fortran 77. El programa principal de C ++ puede ejecutar multiproceso. Sin embargo, sucede que la rutina Fortran 77 oculta varios bloques comunes que se modifican en cada llamada dependiendo de sus argumentos.

Me temo que todos los bloques comunes pueden compartirse entre múltiples hilos y que los accesos concurrentes a estos bloques probablemente lo desordenarán todo.

  • Primera pregunta : ¿Estoy en lo cierto? ¿Los bloques comunes se compartirían entre múltiples hilos?

  • Segunda pregunta : ¿hay una manera simple de evitarlo? Reescribir las rutinas Fortran parece inalcanzable, estoy buscando una manera para que cada hilo tenga su propia copia de todos los bloques comunes (que no son grandes, deben ser rápidos de copiar). No sé si una opción de compilación sería útil o si OpenMP podría ayudarme.


Tiene razón en que los bloques comunes no son enhebrables. Son datos globales que le permiten declarar variables en cualquier unidad de ámbito que compartan la misma asociación de almacenamiento. El efecto es esencialmente el mismo si estuviera escribiendo en variables globales en C ++ con todos los problemas de sincronización de subprocesos que podrían causar.

Desafortunadamente, no creo que haya una manera simple de evitarlo. Si necesita mantener un enfoque de subprocesos múltiples, una idea que he visto arrojar en el pasado es mover todas las variables de un bloque común a un tipo definido por el usuario y pasar instancias de ese tipo a cualquier procedimiento que necesite acceso para ellos (una instancia por hilo). Esto implicaría cambios potencialmente costosos para implementar el código.

También necesitaría ver otros problemas de seguridad de subprocesos con el código Fortran (esta no es una lista exhaustiva):

  • Las unidades IO deben ser únicas por hilo, de lo contrario la entrada / salida del archivo no sería confiable
  • Cualquier variable con el atributo SAVE (implícita en las variables del módulo y en las variables inicializadas cuando se declara) es problemática (estas variables son persistentes entre las llamadas al procedimiento). La impolitness de este atributo también depende del compilador / estándar, lo que lo convierte en un problema potencial aún mayor.
  • declarar procedimientos con el atributo RECURSIVE - esto implica que la función es reingresante. Esto también se puede cumplir compilando con la opción compiladores openmp en lugar de cambiar el código.

Otra ruta que podría explorar es usar procesamiento múltiple o paso de mensajes para paralelizar su código en lugar de mutli-threading. Esto evita los problemas de seguridad del hilo con su código Fortran, pero presenta otro cambio potencialmente costoso en la arquitectura del código.

Ver también:


Gracias por sus respuestas, especialmente la pista sobre OpenMP, es factible. Hice un pequeño programa para estar completamente seguro. Consiste en una parte importante llamada en un programa principal de C ++ (que es mi problema):

las rutinas fortran 77 func.f :

subroutine set(ii, jj) implicit none include "func.inc" integer ii, jj integer OMP_GET_NUM_THREADS, OMP_GET_THREAD_NUM i = ii + 1 j = jj !$OMP CRITICAL print *, OMP_GET_THREAD_NUM(), OMP_GET_NUM_THREADS(), i, j !$OMP END CRITICAL return end subroutine func(n, v) implicit none include "func.inc" integer n, k integer v(n) do k = i, j a = k + 1 b = a * a c = k - 1 v(k) = b - c * c enddo return end

con el archivo include func.inc

integer i, j integer a, b, c common /mycom1/ i, j !$OMP THREADPRIVATE(/mycom1/) common /mycom2/ a, b, c !$OMP THREADPRIVATE(/mycom2/)

y finalmente el programa C ++ main.cpp :

#include<iostream> #include<sstream> #include<vector> using namespace std; #include<omp.h> extern "C" { void set_(int*, int*); void func_(int*, int*); }; int main(int argc, char *argv[]) { int nthread; { istringstream iss(argv[1]); iss >> nthread; } int n; { istringstream iss(argv[2]); iss >> n; } vector<int> a(n, -1); #pragma omp parallel num_threads(nthread) shared(a) { const int this_thread = omp_get_thread_num(); const int num_threads = omp_get_num_threads(); const int m = n / num_threads; int start = m * this_thread; int end = start + m; const int p = n % num_threads; for (int i = 0; i < this_thread; ++i) if (p > i) start++; for (int i = 0; i <= this_thread; ++i) if (p > i) end++; #pragma omp critical { cout << "#t " << this_thread << " : [" << start << ", " << end << "[" << endl; } set_(&start, &end); func_(&n, a.data()); } cout << "[ " << a[0]; for (int i = 1; i < n; ++i) cout << ", " << a[i]; cout << "]" << endl; ostringstream oss; for (int i = 1; i < n; ++i) if ((a[i] - a[i - 1]) != int(4)) oss << i << " "; if (! oss.str().empty()) cout << "<<!! Error occured at index " << oss.str() << " !!>>" << endl; return 0; }

  • Pasos de compilación (gcc versión 4.8.1):

    gfortran -c func.f -fopenmp g++ -c main.cpp -std=gnu++11 -fopenmp g++ -o test main.o func.o -lgfortran -fopenmp

  • Puede ejecutarlo de la siguiente manera:

    ./test 10 1000

    dónde

    • el primer entero (10) es la cantidad de hilos que desea,
    • el segundo (1000) es la longitud de un vector.

    El propósito de este programa es dividir este vector entre hilos y dejar que cada hilo llene una parte del mismo.

    El relleno del vector se realiza dentro de Fortran 77:

    • establecer rutina primero establece los límites inferior y superior gestionados por el hilo,
    • La rutina func luego llena el vector entre los límites anteriores.

Normalmente, si no hay errores y si no se comparten los bloques comunes de Fortran 77, el vector final debe llenarse con 4 * k valores, k pasando de 1 a 1000.

No pude ver el programa. Por el contrario, si elimino las directivas Fortran 77 OMP en func.inc , entonces los bloques comunes no son más privados y surgen muchos errores.

Entonces, para concluir, lo único que tengo que hacer para resolver mi problema inicial es agregar directivas OMP justo detrás de cualquier bloque común, lo que afortunadamente no es complicado ya que están todos reunidos en un archivo incluido (como mi prueba).

Espera que esto ayude.

Atentamente.