Это мой маленький большой вопрос о контейнерах, в частности, массивах.
Я пишу физический код, который в основном манипулирует большим (> 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 было бы интересно, если бы я использовал операции непосредственно над массивом (например, умножение матриц), а я нет. 1050 *
Как вы думаете, с Blitz все в порядке или в моем случае бесполезно?
Это те возможности, которые я рассмотрел до сих пор, но, возможно, лучшая, я все еще другая, поэтому не стесняйтесь предлагать мне другие вещи.
Большое спасибо за помощь в решении этой проблемы!
Edit:
Из очень интересных ответов и комментариев ниже, хорошее решение выглядит следующим образом:
- Использовать структуру
particle
(содержащую 6 двойных) или статический массив из 6 двойных (это позволяет избежать использования двумерных динамических массивов)
- Используйте
vector
или deque
этой particle
структуры или массива. Тогда хорошо обходить их с помощью итераторов, и это позволит позже перейти от одного к другому.
Кроме того, я также могу использовать Blitz::TinyVector<double,6>
вместо структуры.