2016-10-28 4 views
37

Я хочу сохранить большой вектор d-мерных точек (d фиксированный и маленький: < 10).Векторное хранилище в C++

Если я определяю Point как vector<int>, я думаю, что vector<Point> будет хранить в каждой позиции указатель на точку.

Но если определить Point как объект фиксированного размера, как: std::tuple<int,int,...,int> или std::array<int, d>, будет магазин программа все точки в непрерывной памяти или будет дополнительный уровень косвенности остаются?

Если ответ заключается в том, что массивы избегают дополнительной косвенности, может ли это оказать большое влияние на производительность (местонахождение эксплойта в кэше) при сканировании vector<Point>?

+3

стандартный вектор класс должен быть в значительной степени совместимы с массивами, что означает, что данные, которые он выделяет хранятся в непрерывном куске памяти, так же, как массив. Если у вас есть 'std :: vector ', тогда весь объект 'Point' будет храниться смежно. Если в классе «Point» есть указатели (прямо или косвенно (например, когда у него есть вектор)), то не все данные будут сохранены. –

+3

Да, я бы пошел маршрут кортежа или лучше, просто используйте обычную структуру C-стиля (я все еще нахожу кортеж, раздражающий использование, т. Е. Std :: get () на самом деле не все, что интуитивно понятно). – Robinson

ответ

52

Если вы определяете Point как имеющие непрерывное хранение данных (например, struct Point { int a; int b; int c; } или с помощью std::array), то std::vector<Point> будет хранить Point с в смежных ячейках памяти, поэтому макет памяти будет:

p0.a, p0.b, p0.c, p1.a, p1.b, p1.c, ..., p(N-1).a, p(N-1).b, p(N-1).c 

На с другой стороны, если вы определяете Point как vector<int>, то vector<Point> имеет компоновку vector<vector<int>>, которая не смежные, как vector магазинов указателей для динамически распределенной памяти. Таким образом, у вас есть смежность для одиночныйPoint s, но не для всей конструкции.

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

+4

Является ли один более эффективным, чем другой, зависит от варианта использования. Если вам нужно вставить некоторые данные, то может быть намного быстрее просто перемещаться по некоторым указателям, чем копировать все данные. – Meixner

+1

В общем, _measuring_ - это хорошо. Тем не менее, с поддержкой кэширования структур, как правило, отлично подходит для производительности; основанные на узлах структуры, как правило, плохо для перфоманса, так как они недружественны. Обратите внимание, что стоимость пейджинга высока. Вместо этого современные процессоры любят доступ к * смежным * ячейкам памяти (что делает prefetcher счастливым). –

3

Для указанного значения d (< 10), определяя Point, как vector<int> будет почти в два раза полное использование памяти с помощью std::vector<Point> и принесет практически никаких преимуществ.

6

vector будет хранить все, что ваш тип содержит в непрерывной памяти. Так что да, если это array или tuple, или, возможно, даже лучше, настраиваемый тип, это позволит избежать косвенности.

Производительность, как всегда, вы должны ее измерить. Не спекулируйте. По крайней мере, что касается сканирования.

Однако при создании этих точек, безусловно, будет огромный прирост производительности, потому что вы избежите ненужных распределений памяти для каждого vector, который хранит точку. И распределение памяти обычно очень дорого в C++.

1

Поскольку измерение фиксировано, я предлагаю вам пойти с шаблоном, который использует измерение в качестве параметра шаблона. Что-то вроде этого:

template <typename R, std::size_t N> class ndpoint 
{ 
public: 
    using elem_t= 
    typename std::enable_if<std::is_arithmetic<R>::value, R>::type; 

    static constexpr std::size_t DIM=N; 

    ndpoint() = default; 

    // e.g. for copying from a tuple 
    template <typename... coordt> ndpoint(coordt... x) : elems_ {static_cast<R>(x)...} { 
    } 
    ndpoint(const ndpoint& other) : elems_() { 
    *this=other; 
    } 

    template <typename PointType> ndpoint(const PointType& other) : elems_() { 
    *this = other; 
    } 

    ndpoint& operator=(const ndpoint& other) { 
    for(size_t i=0; i<N; i++) { 
     this->elems_[i]=other.elems_[i]; 
    } 
    return *this; 
    } 

    // this will allow you to assign from any source which defines the 
    // [](size_t i) operator 
    template <typename PointT> ndpoint& operator=(const PointT& other) { 
    for(size_t i=0; i<N; i++) { 
     this->elems_[i]=static_cast<R>(other[i]); 
    } 
    } 

    const R& operator[](std::size_t i) const { return this->elems_[i]; } 

    R& operator[](std::size_t i) { return this->elems_[i]; } 

private: 
    R elems_[N]; 
}; 

Затем используйте std::vector<ndpoint<...>> для набора точек для лучшей производительности.

+0

Как это отличается/лучше по сравнению с std :: vector >? –

+0

«Как это лучше, чем std :: vector >?" Контролируйте операции, которые вы можете выполнить с/ndpoint (например, добавьте метод 'norm', или' distanceTo' и т. Д.). По производительности, это то же самое. Просто не используйте кортеж или вектор в качестве хранилища для ваших n-й точки. –

0

Единственный способ убедиться, что ваши данные структурированы на 100%, - это полностью реализовать собственную обработку памяти.

Однако существует множество библиотек, которые реализуют матрицы и операции с матрицами, которые вы можете проверить. Некоторые из них документируют информацию о непрерывной памяти, изменении формы и т. Д. (Например, OpenCV Mat).

Обратите внимание, что в целом вы не можете доверять, что массив точек будет смежным. Это происходит из-за выравнивания, заголовок блока распределения и т.д. Для примера рассмотрим

struct Point { 
    char x,y,z; 
}; 

Point array_of_points[3]; 

Теперь, если вы пытаетесь «перекроить», то есть, итерацию между точечными элементами релейной защиты на том, что точки находятся рядом в контейнере - чем это, скорее всего, на провал:

(char *)(&array_of_points[0].z) != (char *)(&array_of_points[1].x) 

 Смежные вопросы

  • Нет связанных вопросов^_^