运行 使用 cudaMallocManaged 很快就会内存不足

Running out of memory very quick with cudaMallocManaged

我有一个 C 代码,我想将该代码转换为与 CUDA 一起使用。

解释完整的问题会非常复杂和冗长,这就是其中的一部分,我遇到了问题。

现在的问题是:我需要创建四棵AVL树(树中插入的数据是从文件中读取的(实际文件,较小的文件,有255000行,但最多可以有1200万行行)。在每棵对应的树上插入四个值后,不同树的每个节点都会有一个不同节点的列表(每棵树一个不同)但首先我需要解决我遇到的问题。

问题如下,如果我创建其中三个没有问题,但是如果我创建四个,CUDA 会报错"Out of memory"。

注意,有 410 行代码,函数 cudaMallocManaged(...) 的内存预留在行:90(函数 main)和 164、176 和 190(函数 auxCrearIndiceAVL)。所以我认为它有错误,但如果是这样我就看不到了。

我也在 Windows 上使用 Visual Studio 的计算机,配备两个 NVIDIA 680 GTX (2 GB) 和 32 GB 内存。因此,对于这 4 个结构,我认为这些结构有足够的内存。我不知道是否需要激活 CUDA 设置或 ...

上的任何选项

提前感谢所有看到这篇文章的人。曼努埃尔·路易斯·阿斯纳尔

代码如下:

#include "cuda_runtime.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define TRUE 1
#define FALSE 0

#define CHECK(r) {_check((r), __LINE__);}

/* Estructuras y tipos */

enum { IZQUIERDO, DERECHO };

typedef struct _nodo_especial {
    double m2i;
    double m1o;
    double m2o;
    struct _tipo_nodo *dato_next_index;
    struct _nodo_especial *siguiente;
    struct _nodo_especial *next_index;
} nodoEspecial;

typedef nodoEspecial* pNodoEspecial;


typedef struct _tipo_nodo {
    double dato;
    int FE;
    struct _nodo_especial *list;
    struct _tipo_nodo *derecho;
    struct _tipo_nodo *izquierdo;
    struct _tipo_nodo *padre;
} tipoNodo;

typedef tipoNodo* pNodo;
typedef tipoNodo* Arbol;

typedef struct REGcrowd {
    double m1i, m2i, m1o, m2o, x, y, color; // crowding
} EstrellaCrowding;

typedef EstrellaCrowding* pEstrellaCrowding;
typedef EstrellaCrowding* ListaEstrellasCrowding;

typedef struct _nodo_recubrimiento {
    double dato;
    pNodo enlace;
} nodoRecubrimiento;

typedef nodoRecubrimiento* pNodoRecubrimiento;
typedef nodoRecubrimiento* ListaRecubrimiento;


typedef struct _indices {
    Arbol indiceM1i, indiceColor, indiceX, indiceY;
    int numeroEstrellasIndice;
    int numeroNodosM1i, numeroNodosColor, numeroNodosX, numeroNodosY;
    ListaRecubrimiento listaRecubrimiento;
} tipoIndice;

typedef tipoIndice* Indice;
typedef tipoIndice* pIndice;

pNodo auxCrearIndiceAVL(Arbol *a, double dat, int *contador);


__global__ void kernel(Indice indice);
void _check(cudaError_t r, int line);
void check_memory_cuda(void);
__host__ __device__ void InOrden(Arbol a, int *contador);


/* Funciones de equilibrado */
void Equilibrar(Arbol *a, pNodo nodo, int rama, int nuevo);
void RSI(Arbol *raiz, pNodo nodo);
void RSD(Arbol *raiz, pNodo nodo);
void RDI(Arbol *raiz, pNodo nodo);
void RDD(Arbol *raiz, pNodo nodo);

int Vacio(Arbol r);

int main(int argc, char *argv[]) {

    check_memory_cuda();

    //Indice indice = (pIndice)malloc(sizeof(tipoIndice));
    Indice indice;
    CHECK(cudaMallocManaged(&indice, sizeof(Indice)));
    indice->indiceM1i = NULL;
    indice->indiceColor = NULL;
    indice->indiceX = NULL;
    indice->indiceY = NULL;
    indice->listaRecubrimiento = NULL;
    indice->numeroEstrellasIndice = 0;
    indice->numeroNodosM1i = 0;
    indice->numeroNodosColor = 0;
    indice->numeroNodosX = 0;
    indice->numeroNodosY = 0;

    FILE *fcrowd;
    int i;
    EstrellaCrowding estrellaCrowding;
    fcrowd = fopen(argv[1], "r");
    for (i = 0; !feof(fcrowd); i++) {

        fscanf(fcrowd, "%lf %lf %lf %lf %lf %lf", &estrellaCrowding.m1i, &estrellaCrowding.m2i, &estrellaCrowding.m1o, &estrellaCrowding.m2o, &estrellaCrowding.x, &estrellaCrowding.y);

        estrellaCrowding.color = estrellaCrowding.m1i - estrellaCrowding.m2i;

        pNodo auxM1i = auxCrearIndiceAVL(&indice->indiceM1i, estrellaCrowding.m1i, &indice->numeroNodosM1i);
        pNodo auxColor = auxCrearIndiceAVL(&indice->indiceColor, estrellaCrowding.color, &indice->numeroNodosColor);
        pNodo auxX = auxCrearIndiceAVL(&indice->indiceX, estrellaCrowding.x, &indice->numeroNodosX);
        //pNodo auxY = auxCrearIndiceAVL(&indice->indiceY, estrellaCrowding.y, &indice->numeroNodosY);
    }
    fclose(fcrowd);

    check_memory_cuda();

    printf("Imprimiendo el recorrido en InOrden del arbol M1i CPU\n");
    int contador = 0;
    InOrden(indice->indiceM1i, &contador);

    printf("El numero de nodos del arbol M1i es CPU: %d\n", indice->numeroNodosM1i);
    printf("Raiz arbol M1i CPU: %lf\n", indice->indiceM1i->dato);

    printf("El numero de nodos del arbol Color es CPU: %d\n", indice->numeroNodosColor);
    printf("Raiz arbol Color CPU: %lf\n", indice->indiceColor->dato);

    printf("El numero de nodos del arbol X es CPU: %d\n", indice->numeroNodosX);
    printf("Raiz arbol X CPU: %lf\n", indice->indiceX->dato);

    //printf("El numero de nodos del arbol Y es CPU: %d\n", indice->numeroNodosY);
    //printf("Raiz arbol Y CPU: %lf\n", indice->indiceY->dato);

    kernel<<<1, 1>>>(indice);
    cudaDeviceSynchronize();


    return 0;
}

pNodo auxCrearIndiceAVL(Arbol *a, double dat, int *contador) {

    pNodo padre = NULL;
    pNodo actual = *a;

    /* Buscar el dato en el arbol, manteniendo un puntero al nodo padre */
    while (!Vacio(actual) && dat != actual->dato) {
        padre = actual;
        if (dat < actual->dato)
            actual = actual->izquierdo;
        else if (dat > actual->dato)
            actual = actual->derecho;
    }

    /* Si se ha encontrado el elemento, regresar sin insertar */
    if (!Vacio(actual))
        return actual;
    /* Si padre es NULL, entonces el arbol estaba vacio, el nuevo nodo sera el nodo raiz */
    else if (Vacio(padre)) {
        //(*a) = (Arbol)malloc(sizeof(tipoNodo));
        CHECK(cudaMallocManaged(&(*a), sizeof(tipoNodo), cudaMemAttachGlobal));
        (*a)->dato = dat;
        (*a)->izquierdo = (*a)->derecho = NULL;
        (*a)->padre = NULL;
        (*a)->FE = 0;
        (*a)->list = NULL;
        (*contador)++;
        return (*a);
    }
    /* Si el dato es menor que el que contiene el nodo padre, lo insertamos en la rama izquierda */
    else if (dat < padre->dato) {
        //actual = (Arbol)malloc(sizeof(tipoNodo));
        CHECK(cudaMallocManaged(&actual, sizeof(tipoNodo), cudaMemAttachGlobal));
        padre->izquierdo = actual;
        actual->dato = dat;
        actual->izquierdo = actual->derecho = NULL;
        actual->padre = padre;
        actual->FE = 0;
        actual->list = NULL;
        Equilibrar(a, padre, IZQUIERDO, TRUE);
        (*contador)++;
        return actual;
    }
    /* Si el dato es mayor que el que contiene el nodo padre, lo insertamos en la rama derecha */
    else {  /*if (dat > padre->dato) */
        //actual = (Arbol)malloc(sizeof(tipoNodo));
        CHECK(cudaMallocManaged(&actual, sizeof(tipoNodo), cudaMemAttachGlobal));
        padre->derecho = actual;
        actual->dato = dat;
        actual->izquierdo = actual->derecho = NULL;
        actual->padre = padre;
        actual->FE = 0;
        actual->list = NULL;
        Equilibrar(a, padre, DERECHO, TRUE);
        (*contador)++;
        return actual;
    }
}

__global__ void kernel(Indice indice) {

    printf("Imprimiendo el recorrido en InOrden del arbol M1i GPU\n");
    int contador = 0;
    InOrden(indice->indiceM1i, &contador);

    printf("El numero de nodos del arbol M1i es GPU: %d\n", indice->numeroNodosM1i);
    printf("Raiz arbol M1i GPU: %lf\n", indice->indiceM1i->dato);

    printf("El numero de nodos del arbol Color es GPU: %d\n", indice->numeroNodosColor);
    printf("Raiz arbol Color GPU: %lf\n", indice->indiceColor->dato);

    printf("El numero de nodos del arbol X es GPU: %d\n", indice->numeroNodosX);
    printf("Raiz arbol X GPU: %lf\n", indice->indiceX->dato);

    //printf("El numero de nodos del arbol Y es GPU: %d\n", indice->numeroNodosY);
    //printf("Raiz arbol Y GPU: %lf\n", indice->indiceY->dato);
}

void _check(cudaError_t r, int line) {
    if (r != cudaSuccess) {
        printf("CUDA error on line %d: %s\n", line, cudaGetErrorString(r), line);
        exit(0);
    }
}

void check_memory_cuda(void) {
    size_t free, total;
    cudaMemGetInfo(&free, &total);
    printf("CUDA Memory:\n\tFree ---> %llu\n\tTotal ----> %llu\n", free, total);
}

/* Recorrido de arbol en inorden, aplicamos la funcion func, que tiene
el prototipo:
void func(double*);
*/
__host__ __device__ void InOrden(Arbol a, int *contador)
{
    if (a->izquierdo) InOrden(a->izquierdo, contador);
    printf("%d ---> %lf\n", *contador, a->dato);
    (*contador)++;
    if (a->derecho) InOrden(a->derecho, contador);
}

/* Equilibrar árbol AVL partiendo del nodo nuevo */
void Equilibrar(Arbol *a, pNodo nodo, int rama, int nuevo)
{
    int salir = FALSE;

    /* Recorrer camino inverso actualizando valores de FE: */
    while (nodo && !salir) {
        if (nuevo)
        if (rama == IZQUIERDO) nodo->FE--; /* Depende de si añadimos ... */
        else                  nodo->FE++;
        else
        if (rama == IZQUIERDO) nodo->FE++; /* ... o borramos */
        else                  nodo->FE--;
        if (nodo->FE == 0) salir = TRUE; /* La altura de las rama que
                                         empieza en nodo no ha variado,
                                         salir de Equilibrar */
        else if (nodo->FE == -2) { /* Rotar a derechas y salir: */
            if (nodo->izquierdo->FE == 1) RDD(a, nodo); /* Rotación doble  */
            else RSD(a, nodo);                         /* Rotación simple */
            salir = TRUE;
        }
        else if (nodo->FE == 2) {  /* Rotar a izquierdas y salir: */
            if (nodo->derecho->FE == -1) RDI(a, nodo); /* Rotación doble  */
            else RSI(a, nodo);                        /* Rotación simple */
            salir = TRUE;
        }
        if (nodo->padre)
        if (nodo->padre->derecho == nodo) rama = DERECHO; else rama = IZQUIERDO;
        nodo = nodo->padre; /* Calcular FE, siguiente nodo del camino. */
    }
}

/* Rotación doble a derechas */
void RDD(Arbol *raiz, Arbol nodo)
{
    pNodo Padre = nodo->padre;
    pNodo P = nodo;
    pNodo Q = P->izquierdo;
    pNodo R = Q->derecho;
    pNodo B = R->izquierdo;
    pNodo C = R->derecho;

    if (Padre)
    if (Padre->derecho == nodo) Padre->derecho = R;
    else Padre->izquierdo = R;
    else *raiz = R;

    /* Reconstruir árbol: */
    Q->derecho = B;
    P->izquierdo = C;
    R->izquierdo = Q;
    R->derecho = P;

    /* Reasignar padres: */
    R->padre = Padre;
    P->padre = Q->padre = R;
    if (B) B->padre = Q;
    if (C) C->padre = P;

    /* Ajustar valores de FE: */
    switch (R->FE) {
    case -1: Q->FE = 0; P->FE = 1; break;
    case 0:  Q->FE = 0; P->FE = 0; break;
    case 1:  Q->FE = -1; P->FE = 0; break;
    }
    R->FE = 0;
}

/* Rotación doble a izquierdas */
void RDI(Arbol *a, pNodo nodo)
{
    pNodo Padre = nodo->padre;
    pNodo P = nodo;
    pNodo Q = P->derecho;
    pNodo R = Q->izquierdo;
    pNodo B = R->izquierdo;
    pNodo C = R->derecho;

    if (Padre)
    if (Padre->derecho == nodo) Padre->derecho = R;
    else Padre->izquierdo = R;
    else *a = R;

    /* Reconstruir árbol: */
    P->derecho = B;
    Q->izquierdo = C;
    R->izquierdo = P;
    R->derecho = Q;

    /* Reasignar padres: */
    R->padre = Padre;
    P->padre = Q->padre = R;
    if (B) B->padre = P;
    if (C) C->padre = Q;

    /* Ajustar valores de FE: */
    switch (R->FE) {
    case -1: P->FE = 0; Q->FE = 1; break;
    case 0:  P->FE = 0; Q->FE = 0; break;
    case 1:  P->FE = -1; Q->FE = 0; break;
    }
    R->FE = 0;
}

/* Rotación simple a derechas */
void RSD(Arbol *a, pNodo nodo)
{
    pNodo Padre = nodo->padre;
    pNodo P = nodo;
    pNodo Q = P->izquierdo;
    pNodo B = Q->derecho;

    if (Padre)
    if (Padre->derecho == P) Padre->derecho = Q;
    else Padre->izquierdo = Q;
    else *a = Q;

    /* Reconstruir árbol: */
    P->izquierdo = B;
    Q->derecho = P;

    /* Reasignar padres: */
    P->padre = Q;
    if (B) B->padre = P;
    Q->padre = Padre;

    /* Ajustar valores de FE: */
    P->FE = 0;
    Q->FE = 0;
}

/* Rotación simple a izquierdas */
void RSI(Arbol *a, pNodo nodo)
{
    pNodo Padre = nodo->padre;
    pNodo P = nodo;
    pNodo Q = P->derecho;
    pNodo B = Q->izquierdo;

    if (Padre)
    if (Padre->derecho == P) Padre->derecho = Q;
    else Padre->izquierdo = Q;
    else *a = Q;

    /* Reconstruir árbol: */
    P->derecho = B;
    Q->izquierdo = P;

    /* Reasignar padres: */
    P->padre = Q;
    if (B) B->padre = P;
    Q->padre = Padre;

    /* Ajustar valores de FE: */
    P->FE = 0;
    Q->FE = 0;
}

/* Comprobar si un árbol es vacío */
int Vacio(Arbol r)
{
    return r == NULL;
}

昨天晚上和晚上我在做一些 cudaMallocManaged(...) 测试,在那之前我认为统一内存正在使用设备内存,当它在设备中用完 space 时,它们就会启动使用主机内存。

事实并非如此,使用 CUDA 6 统一内存系统 cudaMallocManaged(...),我们只能分配设备拥有的内存。

非常感谢 ArchaeaSoftware 的回答

感谢全民阅读,祝大家尽情享受 CUDA

伙计们,待会见。 曼纽尔