Как динамически выделить большой 3-мерный массив? - PullRequest
0 голосов
/ 12 апреля 2020

Оперативная память моего компьютера имеет 32 ГБ доступной памяти. Я хочу определить массив размером 1500 *1500* 500. Как мне определить массив Dynami c?

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <openacc.h>
#include <time.h>
#include <string.h>
#include <cuda_runtime_api.h>

void main(void) {
    #define NX 1501
    #define NY 1501
    #define NZ 501
    int  i, j, k, l, m, dt, nstop;
    double  comp;
    dt = 5;
    nstop = 5
    static double ex[NX][NY][NZ] = { 0. }, ey[NX][NY][NZ] = { 0. }, ez[NX][NY][NZ] = { 0. };
    static double hx[NX][NY][NZ] = { 1. }, hy[NX][NY][NZ] = { 0. }, hz[NX][NY][NZ] = { 1. };
    static double t, comp;
    FILE *file;

    file = fopen("point A hm=0.csv", "w"); /* Output data file name */

    t = 0.;

    for (l = 0; l < nstop; l++) {
        for (i = 0; i < NX - 1; i++) {
            for (j = 1; j < NY - 1; j++) {
                for (k = 1; k < NZ - 1; k++) {
                    ex[i][j][k] =  2 * ey[i][j][k]
                                 + 3 * (hz[i][j][k] - hx[i][j - 1][k])
                                 - 5 * (hy[i][j][k] - 2 * hz[i][j][k - 1]);
                }
            }
        }
        comp = ((double)(l + 1) / nstop) * 100.;
        printf("Computation: %4.3f %% completed \r", comp);
        fprintf(file, "%e, %e \n", t * 1e6, -ex[1200][950][20] + ex[1170][950][20]) / 2.);
        t = t + dt;
    }

    fclose(file);
} 

Ответы [ 3 ]

1 голос
/ 13 апреля 2020

В вашей постановке задачи должна быть ошибка:

  • формула для вычисления ex[i][j][k] зависит только от значений из других массивов с таким же индексом i для первого измерения. Поскольку вы выводите только значение -ex[1200][950][20] + ex[1170][950][20]) / 2., вам нужно только вычислить значения для i=1200 и i=1170, и нет необходимости выделять столько памяти.
  • , кроме того, вычисленные значения в ex одинаковы для всех значений l. Не нужно пересчитывать на каждой итерации.
  • наконец, учитывая инициализацию массивов, все значения ex для первого индекса, отличного от 0, равны нулю, поэтому вывод вычисляется тривиально: 0.0 .

Более серьезно, если начальные значения являются маленькими целыми числами, результаты, по-видимому, требуют только 32-разрядной целочисленной арифметики, что уменьшило бы требования к памяти на 50%. Тем не менее, это все равно будет превышать максимальный размер для статически размещенных объектов в вашей системе. Вы должны распределить эти 3D-матрицы динамически следующим образом:

    double (*ex)[NY][NZ] = calloc(NX, sizeof(*ex));

Предполагая, что ваш код более сложен, чем размещенный пример, который, кстати, содержит несколько опечаток, препятствующих компиляции, вот как будет выглядеть модифицированный код:

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

int main(void) {
#define NX 1501
#define NY 1501
#define NZ 501
    int  i, j, k, l, dt, nstop;
    double comp;
    dt = 5;
    nstop = 5;
    double (*ex)[NY][NZ] = calloc(NX, sizeof(*ex));
    if (ex == NULL) { fprintf(stderr, "allocation failed for ex\n"); exit(1); }
    double (*ey)[NY][NZ] = calloc(NX, sizeof(*ey));
    if (ey == NULL) { fprintf(stderr, "allocation failed for ey\n"); exit(1); }
    double (*ez)[NY][NZ] = calloc(NX, sizeof(*ez));
    if (ez == NULL) { fprintf(stderr, "allocation failed for ez\n"); exit(1); }
    double (*hx)[NY][NZ] = calloc(NX, sizeof(*hx));
    if (hx == NULL) { fprintf(stderr, "allocation failed for hx\n"); exit(1); }
    double (*hy)[NY][NZ] = calloc(NX, sizeof(*hy));
    if (hy == NULL) { fprintf(stderr, "allocation failed for hy\n"); exit(1); }
    double (*hz)[NY][NZ] = calloc(NX, sizeof(*hz));
    if (hz == NULL) { fprintf(stderr, "allocation failed for hz\n"); exit(1); }
    hx[0][0][0] = 1.;
    hz[0][0][0] = 1.;
    // probably many more initializations missing
    double t;
    FILE *file;

    file = fopen("point A hm=0.csv", "w"); /* Output data file name */
    if (file == NULL) { fprintf(stderr, "cannot create output file\n"); exit(1); }

    t = 0.;

    for (l = 0; l < nstop; l++) {
        for (i = 0; i < NX - 1; i++) {
            for (j = 1; j < NY - 1; j++) {
                for (k = 1; k < NZ - 1; k++) {
                    ex[i][j][k] =  2 * ey[i][j][k]
                        + 3 * (hz[i][j][k] - hx[i][j - 1][k])
                        - 5 * (hy[i][j][k] - 2 * hz[i][j][k - 1]);
                }
            }
        }
        comp = ((double)(l + 1) / nstop) * 100.;
        printf("Computation: %4.3f %% completed \r", comp);
        fprintf(file, "%e, %e \n", t * 1e6, (-ex[1200][950][20] + ex[1170][950][20]) / 2.);
        t = t + dt;
    }
    fclose(file);
    free(ex);
    free(ey);
    free(ez);
    free(hx);
    free(hy);
    free(hz);
    return 0;
}
0 голосов
/ 13 апреля 2020

Есть несколько вариантов. Если вам нужно выделить всю структуру памяти одновременно, вы, вероятно, захотите выделить указатель на указатель на массив int[500] (int (**)[500]), а не выделять указатель-на-указатель-на-указатель int (int ***) - хотя оба они технически верны.

( примечание: Я использовал int в примере поэтому просто измените тип a на double, чтобы удовлетворить ваши потребности)

Чтобы приблизиться к выделению для указатель на указатель на массив int[500], начните с вашего указателя и выделите 1500 указателей, например,

#define Z 500
#define X 1500
#define Y X

int main (void) {

    int (**a)[Z] = NULL;            /* pointer to pointer to array of int[500] */

    if (!(a = malloc (X * sizeof *a))) {    /* allocate X pointers to (*)[Z] */
        perror ("malloc-X (**)[Z]");
        return 1;
    }

. На данный момент у вас есть 1500 указателей на массив int[500]. Вы можете l oop каждого указанного выше указателя, выделяя 1500 * sizeof (int[500) и назначая начальный адрес каждому блоку, выделенному одному из указателей, например,

    for (int i = 0; i < X; i++)                     /* for each pointer */
        if (!(a[i] = malloc (Y * sizeof **a))) {    /* alloc Y * sizeof int[Z] */
            perror ("malloc-YZ (*)[Z]");
            return 1;
        }

Теперь вы можете адресовать каждое целое число в вашем распределении как a[x][y][z]. Затем, чтобы освободить выделенную память, вы просто free() в обратном порядке, например,

    for (int i = 0; i < X; i++)
        free (a[i]);                        /* free allocated blocks */
    free (a);                               /* free pointers */

Короткий пример, который выполняет это и записывает значение в каждый индекс, может быть:

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

#define Z 500
#define X 1500
#define Y X

int main (void) {

    int (**a)[Z] = NULL;            /* pointer to pointer to array of int[500] */

    if (!(a = malloc (X * sizeof *a))) {    /* allocate X pointers to (*)[Z] */
        perror ("malloc-X (**)[Z]");
        return 1;
    }
    puts ("pointers allocated");

    for (int i = 0; i < X; i++)                     /* for each pointer */
        if (!(a[i] = malloc (Y * sizeof **a))) {    /* alloc Y * sizeof int[Z] */
            perror ("malloc-YZ (*)[Z]");
            return 1;
        }
    puts ("all allocated");

    for (int i = 0; i < X; i++)             /* set mem to prevent optimize out */
        for (int j = 0; j < Y; j++)
            for (int k = 0; k < Z; k++)
                a[i][j][k] = i * j * k;

    puts ("freeing memory");
    for (int i = 0; i < X; i++)
        free (a[i]);                        /* free allocated blocks */
    free (a);                               /* free pointers */
}

Пример использования / вывода - запуск по времени

$ time ./bin/malloc_1500x1500x500
pointers allocated
all allocated
freeing memory

real    0m1.481s
user    0m0.649s
sys     0m0.832s

Использование памяти / проверка ошибок

Это 4,5 ГБ выделенной памяти и используется ( предупреждение: вы будете переключаться на 8G или меньше в зависимости от того, что еще у вас работает, если вы запускаете valgrind)

$ valgrind ./bin/malloc_1500x1500x500
==7750== Memcheck, a memory error detector
==7750== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7750== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==7750== Command: ./bin/malloc_1500x1500x500
==7750==
pointers allocated
all allocated
freeing memory
==7750==
==7750== HEAP SUMMARY:
==7750==     in use at exit: 0 bytes in 0 blocks
==7750==   total heap usage: 1,502 allocs, 1,502 frees, 4,500,013,024 bytes allocated
==7750==
==7750== All heap blocks were freed -- no leaks are possible
==7750==
==7750== For counts of detected and suppressed errors, rerun with: -v
==7750== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Посмотрите вещи и дайте мне знать, если у вас вопросы.

0 голосов
/ 12 апреля 2020

В C (как кажется, ваш код) вы можете, например, использовать тройной указатель и malloc():

#define NX 1501
#define NY 1501
#define NZ 501

int*** p_a = malloc(sizeof(double**) * NX);

for (int i = 0; i < NX; i++)
{
    p_a[i] = malloc(sizeof(double*) * NY)

    for (int j = 0; j < NY; j++)
        p_a[i][j] = malloc(sizeof(double) * NZ);
}

Более эффективным способом было бы использование одного указателя и используйте размер каждого измерения при вызове malloc() сразу:

double* p_a = malloc(sizeof(*p_a) * (NX * NY * NZ));

В C ++ наиболее распространенным и эффективным способом является использование std::vector для динамическое выделение массива:

#define NX 1501
#define NY 1501
#define NZ 501

std::vector<std::vector<std::vector<double>>> a(NX, vector<vector<double>>(NY, vector<double>(NZ)));

Обратите внимание, что размер объекта double на большинстве современных платформ 8 байт. Значит, когда вы хотите достичь того, чего хотите, вам нужно как минимум 8 *1500* 1500 * 500 = 9000000000 байт = около 8,3 Гбайт для каждого 3D-массива, чтобы выделить его. Вы определяете 6 из них, поэтому 49,8 Гбайт требуется для распределения только тех массивов , которые не предоставляются вашими системами, поскольку вы сказали, что в вашей системе доступно 32 Гбайт.

...