Хранение 3D массива в куче в качестве элемента структуры

#c #multidimensional-array #struct #heap-memory #new-operator

Вопрос:

Недавно я начал работать с C для численных вычислений, где я хочу использовать a Struct Operators для хранения 3D-полей в ходе моделирования. Я создаю 3D-массивы в куче с помощью

 const unsigned int RES = 256;
auto arr3D = new double [RES][RES][RES];
 

потому что, судя по тому, что я протестировал, этот подход быстрее, чем использование Boost_multiarr, собственного тензора или вложенных векторов.
До сих пор это хорошо работало с моим минималистичным Test.cpp но когда я пытаюсь реализовать эти же 3D-массивы в качестве членов my Struct Operators , я больше не могу использовать auto команду:

 const unsigned int RES = 256;

struct Operators {
public:
    std::complex<double>*** wfc;         // edited, see 'spitconsumers' comment

    Operators(Settings amp;set) {        // just another structure used by Operators
        wfc = new std::complex<double> [RES][RES][RES];

        // ...Initializing wfc using Settings

};
 

В этом случае я не нашел способа объявить wfc , чтобы я не получал ошибок типа

ошибка: не удается преобразовать «std::сложный (*)[256][256]’ в «std::комплекс***» в задании

Поэтому мой вопрос заключается в том, как правильно объявить 3D-массив wfc и возможно ли вообще/полезно поддерживать такой подход к структуре. Было бы вообще быстрее получить доступ к wfc[i][j][k] if wfc , если бы он не был членом структуры? (Мне придется сделать это ~10^6 раз)

Заранее спасибо!

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

1. Я бы пропустил [][][] синтаксис и просто использовал один вектор. Элемент [z*RES*RES y*RES x] даст вам эквивалент [z][y][x] .

Ответ №1:

Сообщение об ошибке возвращает правильное объявление, std::complex<double>(*wfc)[RES][RES]; .

 const unsigned int RES = 256;
struct Settings {};
struct Operators {
public:
    std::complex<double>(*wfc)[RES][RES];         // edited, see 'spitconsumers' comment

    Operators(Settingsamp; set) {        // just another structure used by Operators
        wfc = new std::complex<double>[RES][RES][RES];

        // ...Initializing wfc using Settings
        // Setting the last element
        wfc[254][254[254] = 42;

    };
}
 

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

1. Спасибо за решение, это было именно то, что я искал! Однако я нахожу, что решение splitconsumer в два раза быстрее, когда я измеряю время, необходимое для выполнения операций над формой T[ix][iy][iz] = std::pow(P[ix][iy][iz], 2) - n_L; с циклами for для каждого i,j,k . Связано ли это с выделением памяти?

2. @Azure27 Звучит так, как будто скомпилированный код вычисляет каждое местоположение при каждом доступе, что было бы довольно медленным, поскольку для получения смещения требуется два умножения и 3 добавления. Это было бы плохой производительностью оптимизатора. Убедитесь, что вы используете-O3 Для других проблем, связанных с перекрестным доступом, таких как умножение массивов, подход к массиву указателей обычно медленнее, поскольку математика процессоров в регистрах ЦП выполняется быстрее, чем доступ к указателям.

Ответ №2:

В вашем сообщении об ошибке компилятор сообщает вам, что new complex [RES][RES][RES] возвращает тип std::complex (*)[256][256] .

double wfc должно быть std::complex*** wfc .

Типы членов класса должны быть известны, и, поскольку вы не инициализируете переменную-член во время ее определения, компилятор не может определить тип переменной, поэтому auto она не работает как тип члена класса, когда вы не назначаете ей что-то немедленно. Но вы присваиваете значение непосредственно переменной в своей тестовой программе, позволяя компилятору вывести тип там.

Кроме того, new оператор возвращает указатель на массив неинициализированных std::complex<double>** указателей. Вам нужно вызвать new все неинициализированные указатели.

 std::complex<double>*** wfc;

wfc = new std::complex<double>** [RES];

for(int i = 0; i < RES; i  ) {
    wfc[i] = new std::complex<double>* [RES];

    for(int j = 0; j < RES; j  ) {
        wfc[i][j] = new std::complex<double> [RES];

        for(int k = 0; k < RES; k  ) {
            wfc[i][j][k] = /* insert default value here */;
        }
    }
}
 

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

1. Спасибо, в этом есть большой смысл! Замена double с std::complex<double>*** , я теперь получаю ошибку > ошибка: невозможно преобразовать ‘комплекс ( )[256][256]’ {ака ‘станд::комплекс<double> ( )[256][256]’} в комплексе***’ {ака ‘станд::комплекс<double>***’} в назначении ВЛК = новый комплекс [ресурс][ресурс][рез];

2. new оператор возвращает указатель на массив неинициализированных указателей. Вам нужно запустить поисковый запрос, вызывающий new все неинициализированные указатели. Я отредактирую свой ответ, чтобы ваша проблема была полностью решена (надеюсь).

3. для цикла*. Досадная опечатка.

4. Большое спасибо, этот подход отлично работает! Хотя это не так коротко, как решение Дугса,я нахожу в своих тестах производительности,что оно в два раза быстрее в отношении операций с элементами, таких как T[ix][iy][iz] = std::pow(P[ix][iy][iz], 2) - n_L; для каждого i, j, k. Связано ли это с тем, как выделяется память?