Как управлять динамическим многомерным массивом в C

#c #dynamic #multidimensional-array

#c #динамический #многомерный массив

Вопрос:

Мне нужно выделить большой многомерный массив как char a[x][32][y] , а x * 32 * y составляет около 6 ~ 12G. (x, y определяются во время выполнения.)

Я придумываю способ, который нужно сделать char *a=malloc(x*32*y) и использовать *(a 32*y*i y*j k) для a[i][j][k] .

Однако это выглядит не так удобно по сравнению с a[i][j][k] .

Есть ли способ лучше?

Добавлено: это a[x][32][datlen] , где datlen определяется во время выполнения и x задается с учетом объема памяти.

Все данные в массиве будут новыми. И у меня есть математика с объемом памяти 16 или 32 ГБ для ее запуска.

Комментарии:

1. Рассмотрим макрос или встроенную функцию? Для такого большого набора данных рассмотрите возможность использования mmap и / или других методов увеличения локальности, которые могут отличаться от того же «плоского» массива.

2. Также, что a[X][N][Y] концептуально сопоставляется с? Бьюсь об заклад, вы могли бы оставить данные на диске, используя BDB, Sqlite или HDF5, и заставить вашу программу извлекать фрагменты соответствующего размера. Поскольку ваш вопрос остается в силе, мы только предполагаем 🙂

Ответ №1:

НЕВЕРНО: Вы все равно должны иметь возможность использовать синтаксис [i] [j] [k] при обращении к динамически выделяемой памяти.

ПРАВИЛЬНО: используйте макрос, чтобы, по крайней мере, упростить работу

 #define A(i,j,k) *(a 32*y*i y*j k)
A(1,2,3) would then do the right thing.
  

Комментарии:

1. Это невозможно, потому что размер измерений неизвестен во время компиляции — и даже с поддержкой во время выполнения, нельзя присвоить переменной типа char[x][32][y] . (Если только я не просто датирован C89, что является очень хорошей возможностью.)

2. @pst: Хм. Хороший момент. Это работает с одномерными массивами, но я недостаточно тщательно подумал об ограничениях, с которыми вы работаете. Я пересмотрел ответ, чтобы предложить макросы.

3. По-прежнему не будет работать, потому что макросы являются фиксированным определением. Вы не можете изменить это 32*y во время выполнения.

4. @Zan Lynx: Вы можете вычислить 32 * y во время выполнения. Макрос просто предоставляет текстовую подстановку параметров. Итак, A (1,2,3) преобразуется в *(a 32 * y * 1 y * 2 3), и система вычисляет это во время выполнения точно так же, как если бы вы написали этот код там. Это не следует путать с сокращением константного выражения компилятора (которое может произойти по результатам подстановки макроса, но не в этом случае).

5. @Seth: Допустим, вы решили, что массив должен быть перераспределен как 64 вместо 32? Ваш макрос не будет работать. Структура данных и функции, которые отслеживают текущие размеры массива, лучше.

Ответ №2:

Я сомневаюсь, что вы найдете систему, которая выделит вам непрерывную память такого размера *. Вам придется использовать какую-то стратегию разбиения на фрагменты.

Вам нужно спросить: «Каков ваш шаблон доступа к данным?»

Если это некоторый шаг (будь то 1D или 2D), используйте это, чтобы выбрать подходящее выделение памяти для каждого блока. Используйте структуру данных для представления каждого шага (это может быть просто структура, содержащая ваши символьные массивы).

Редактировать: Я не заметил вашего второго «вопроса» о доступе к вашему недавно обнаруженному непрерывному фрагменту памяти объемом 12 ГБ с использованием a[i][j][k] синтаксиса. Этого не произойдет ни в одном известном мне дистрибутиве C потребительского класса.

(*) и 640 кб должно быть достаточно памяти для любого.

Комментарии:

1. Например, если вы обращаетесь к X блокам char[32][Y] , затем выполните разбиение на части X .

2. Еще лучше, X куски (где X строго больше, чем 32 * Y), вероятно, являются хорошей ставкой. Тогда и только тогда, когда вы работаете над 32 * Y-й частью, а не с каким-то странным порядком упорядочивания столбцов (в стиле Fortran).

3. Интересно, почему непрерывная память является проблемой, если достаточно свободной памяти места подкачки. Я помню, что malloc (и все указатели пользовательского пространства) вернут виртуальный адрес, а ядро Linux будет иметь дело с физическим отображением.

4. malloc я обнаружил, что это не всегда удается сделать. Обычно в таких ситуациях я использовал mmap .

Ответ №3:

Поскольку это C, вы не можете обернуть все в удобный объект C .

Но я бы сделал что-то подобное. Разработайте серию функций, которые выделяют, обрабатывают и уничтожают этот ваш новый тип данных.

Чтобы прочитать или записать часть данных, вызовите функцию. Никогда не прикасайтесь к данным напрямую. На самом деле, если вы можете использовать void* дескриптор для своих данных и даже не помещать реальные типы данных во включенный файл заголовка, это лучшее, что можно сделать.

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

Теперь, когда я это написал, позвольте мне частично вернуть это обратно. Если вам нужна большая производительность, вы можете захотеть определить все функции во включенном файле заголовка как встроенные определения. Это позволит вашему компилятору удалить почти все служебные данные при вызове функций и активно оптимизировать.

Я признаю, что matrix_set(x, y, z, value) это не так красиво, как matrix[x][y][z] = value , но это будет работать так же хорошо.