2010-12-02 10 views
5

El caso de prueba siguiente se queda sin memoria en máquinas de 32 bits (throwing std :: bad_alloc) en el ciclo siguiente al mensaje "post MT section" cuando se usa OpenMP, sin embargo, si #pragmas para OpenMP está comentado, el código se ejecuta hasta el final, así que parece que cuando la memoria se asigna en subprocesos paralelos, no se libera correctamente y, por lo tanto, se queda sin memoria.Fuga de memoria al utilizar OpenMP

La pregunta es si hay algún problema con la asignación de memoria y el código de eliminación a continuación o ¿es esto un error en gcc v4.2.2 o OpenMP? También probé gcc v4.3 y obtuve el mismo error.

int main(int argc, char** argv) 
{ 
    std::cout << "start " << std::endl; 

    { 
      std::vector<std::vector<int*> > nts(100); 
      #pragma omp parallel 
      { 
        #pragma omp for 
        for(int begin = 0; begin < int(nts.size()); ++begin) { 
          for(int i = 0; i < 1000000; ++i) { 
            nts[begin].push_back(new int(5)); 
          } 
        } 
      } 

    std::cout << " pre delete " << std::endl; 
      for(int begin = 0; begin < int(nts.size()); ++begin) { 
        for(int j = 0; j < nts[begin].size(); ++j) { 
          delete nts[begin][j]; 
        } 
      } 
    } 
    std::cout << "post MT section" << std::endl; 
    { 
      std::vector<std::vector<int*> > nts(100); 
      int begin, i; 
      try { 
       for(begin = 0; begin < int(nts.size()); ++begin) { 
        for(i = 0; i < 2000000; ++i) { 
          nts[begin].push_back(new int(5)); 
        } 
       } 
      } catch (std::bad_alloc &e) { 
        std::cout << e.what() << std::endl; 
        std::cout << "begin: " << begin << " i: " << i << std::endl; 
        throw; 
      } 
      std::cout << "pre delete 1" << std::endl; 

      for(int begin = 0; begin < int(nts.size()); ++begin) { 
        for(int j = 0; j < nts[begin].size(); ++j) { 
          delete nts[begin][j]; 
        } 
      } 
    } 

    std::cout << "end of prog" << std::endl; 

    char c; 
    std::cin >> c; 

    return 0; 
} 
+0

Cuando ejecuto esto en Windows creado con el compilador de Intel mis asignaciones comienzan a fallar en el primer ciclo debido al límite de 2gb para un proceso de 32 bits. ¿Es posible que la sobrecarga de OpenMP simplemente empuje su proceso sobre cualquier límite en su plataforma? – Scott

+0

@Scott Danahy Intente cambiar el caso de prueba para cortar todas las asignaciones a la mitad, esta prueba funcionó con un límite de 4 GB. – WilliamKF

Respuesta

3

Encontré este problema en otro lugar visto sin OpenMP pero solo usando pthreads. El consumo de memoria adicional cuando multihilo parece ser un comportamiento típico para el asignador de memoria estándar. Al cambiar al asignador Hoard, el consumo de memoria adicional desaparece.

0

¿Por qué utiliza int* como el miembro del vector interior? Eso es muy derrochador: tiene 4 bytes (sizeof(int), estrictamente) de datos y 2 o 3 veces más de la estructura de control del montón para cada entrada vector. Pruebe esto simplemente usando vector<int> y vea si funciona mejor.

No soy un experto en OpenMP pero este uso parece extraño en su asimetría: rellena los vectores en sección paralela y los borra en código no paralelo. No puedo decirle si eso está mal, pero "se siente" mal.

+0

@Steve Townsend Este caso de prueba está diseñado para mostrar el problema, no para ser un código real utilizado. Una matriz de cinco entradas en cada entrada ayuda a consumir la memoria para demostrar la fuga. Hacer que el código sea más eficiente en cuanto a la memoria provocaría que la falla no se retrasara hasta que se consumiera más memoria. – WilliamKF

+0

@WilliamKF - Ya veo. Creo que necesitas un gurú OpenMP para comentar en ese momento. La pregunta se refiere a * cualquier * problema con la asignación y eliminación de memoria, y para mí parecía que RAII sería preferible. Estoy agregando +1 a tu q, ya que estoy interesado en la respuesta. –

+0

Dado que el código es para la demostración del problema, se siente un poco incómodo, pero no es raro que la limpieza y el postprocesamiento se realicen fuera de las construcciones de OpenMP. –

3

Cambiar el primer bucle OpenMP de 1000000 a 2000000 causará el mismo error. Esto indica que el problema de falta de memoria es con el límite de pila OpenMP.

intente configurar el límite de la pila OpenMP a unlimit en bash con

ulimit -s unlimited 

También puede cambiar el entorno OpenMP OMP_STACKSIZE variable y ajustarlo a 100 MB o más.

ACTUALIZACIÓN 1: I cambiar el primer bucle para

{ 
    std::vector<std::vector<int*> > nts(100); 
    #pragma omp for schedule(static) ordered 
    for(int begin = 0; begin < int(nts.size()); ++begin) { 
     for(int i = 0; i < 2000000; ++i) { 
      nts[begin].push_back(new int(5)); 
     } 
    } 

    std::cout << " pre delete " << std::endl; 
    for(int begin = 0; begin < int(nts.size()); ++begin) { 
     for(int j = 0; j < nts[begin].size(); ++j) { 
      delete nts[begin][j] 
     } 
    } 
} 

Entonces, consigo un error de memoria en i = 1574803 en el hilo principal.

ACTUALIZACIÓN 2: Si está utilizando el compilador Intel, puede agregar lo siguiente al principio de su código y solucionará el problema (siempre que tenga suficiente memoria para la sobrecarga adicional).

std::cout << "Previous stack size " << kmp_get_stacksize_s() << std::endl; 
kmp_set_stacksize_s(1000000000); 
std::cout << "Now stack size " << kmp_get_stacksize_s() << std::endl; 

Actualización 3: Para completar, como se ha mencionado por otro miembro, si va a realizar algún cálculo numérico, lo mejor es realizar una asignación previa de todo en un solo nuevo flotador [1000000] en lugar de utilizar OpenMP hacer 1000000 asignaciones. Esto se aplica a la asignación de objetos también.

+0

Cuando cambia el primer ciclo a 2000000, ¿cuál es el tamaño total asignado para el proceso cuando no asigna memoria nueva? – Scott

+0

i = 1574803 cuando se bloquea. Vea mi ACTUALIZACIÓN 1. –

+0

@Dat Chu So sin usar el compilador de Intel (es decir, usando gcc) la configuración de env var OMP_STACKSIZE es el camino a seguir porque el kmp_set_stacksize_s() no está disponible en gcc? – WilliamKF