Это мой маленький большой вопрос о контейнерах, в частности, массивах.
Я пишу физический код, который в основном манипулирует большим ( > 1 000 000) набором "частиц" (с координатами 6 double
). Я ищу лучший способ (с точки зрения производительности) реализовать класс, который будет содержать контейнер для этих данных, и который предоставит примитивы манипуляции для этих данных (например, экземпляр, operator[]
и т.д.).
Существует несколько ограничений на использование этого набора:
- его размер считывается из файла конфигурации и не изменяется во время выполнения.
- его можно рассматривать как большой двумерный массив из N (например, 1 000 000) строк и 6 столбцов (каждый из которых хранит координату в одном измерении).
- массив обрабатывается в большом цикле, к каждой "частице/линии" обращаются, и вычисление происходит с его координатами, а результаты сохраняются для этой частицы и т.д. для каждой частицы и т.д. для каждого итерация большой петли.
- новые элементы не добавляются и не удаляются во время выполнения
Первый вывод, поскольку доступ к элементам по существу осуществляется путем доступа к каждому элементу один за другим с помощью []
, я думаю, что я должен использовать обычный динамический массив.
Я изучил несколько вещей, и я хотел бы получить ваше мнение о том, что может дать мне лучшие результаты.
Как я понимаю, нет преимущества использовать динамически выделенный массив вместо std::vector
, поэтому такие вещи, как double** array2d = new ..., loop of new, etc
, исключаются.
Так что неплохо использовать std::vector<double>
?
Если я использую std::vector
, должен ли я создать двумерный массив вроде std::vector<std::vector<double> > my_array
, который может быть проиндексирован как my_array[i][j]
, или это плохая идея, и было бы лучше использовать std::vector<double> other_array
и использовать его с other_array[6*i+j]
.
Может быть, это может обеспечить лучшую производительность, тем более, что количество столбцов фиксировано и известно с самого начала.
Если вы считаете, что это лучший вариант, можно ли обернуть этот вектор таким образом, чтобы к нему можно было обращаться с помощью оператора индекса, определенного как other_array[i,j] // same as other_array[6*i+j]
без накладных расходов (например, вызов функции при каждом доступе)?
Другим вариантом, который я использую до сих пор, является использование Blitz, в частности blitz::Array
:
typedef blitz::Array<double,TWO_DIMENSIONS> store_t;
store_t my_store;
Где доступны мои элементы: my_store(line, column);
.
Я думаю, что нет никакого преимущества использовать Blitz в моем случае, потому что я обращаюсь к каждому элементу один за другим, и что Блиц был бы интересен, если бы я использовал операции непосредственно с массивом (например, с матричным умножением), которым я не являюсь.
Считаете ли вы, что Blitz в порядке, или это бесполезно в моем случае?
Это те возможности, которые я рассмотрел до сих пор, но, возможно, лучший из них, я еще один, поэтому не стесняйтесь предлагать мне другие вещи.
Большое спасибо за вашу помощь по этой проблеме!
Edit:
Из очень интересных ответов и комментариев ниже хорошее решение выглядит следующим образом:
- Используйте структуру
particle
(содержащую 6 двойников) или статический массив из 6 удвоений (это позволяет избежать использования двумерных динамических массивов) - Используйте
vector
илиdeque
этой структуры или массиваparticle
. Затем полезно перемещать их с помощью итераторов, и это позволит впоследствии перейти от одного к другому.
Кроме того, я могу использовать Blitz::TinyVector<double,6>
вместо структуры.