web-dev-qa-db-fra.com

Stockage vectoriel en C ++

Je souhaite stocker un grand vecteur de points de dimension d (d fixe et petit: <10).

Si je définis un Point comme vector<int>, Je pense qu'un vector<Point> stockerait dans chaque position un pointeur sur un point.

Mais si définissez un Point comme un objet de taille fixe comme: std::Tuple<int,int,...,int> ou std::array<int, d>, le programme stockera-t-il tous les points dans la mémoire contiguë ou le niveau supplémentaire d'indirection restera-t-il?

Dans le cas où la réponse est que les tableaux évitent l'indirection supplémentaire, cela pourrait-il avoir un impact important sur les performances (localité d'exploitation du cache) lors de l'analyse du vector<Point>?

39
Joseph Stack

Si vous définissez votre Point comme ayant un stockage de données contigu (par exemple struct Point { int a; int b; int c; } ou en utilisant std::array), puis std::vector<Point> stockera les Points dans des emplacements de mémoire contigus, donc votre disposition de mémoire sera:

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

En revanche, si vous définissez Point comme vector<int>, puis un vector<Point> a la disposition de vector<vector<int>>, qui est pas contigu, car vector stocke pointeurs dans la mémoire allouée dynamiquement. Vous avez donc une contiguïté pour singlePoints, mais pas pour toute la structure.

La première solution est beaucoup plus efficace que la seconde (car les processeurs modernes adorent accéder aux emplacements de mémoire contigus).

52
Mr.C64

vector stockera tout ce que votre type contient dans la mémoire contiguë. Donc oui, si c'est un array ou un Tuple, ou probablement encore mieux, un type personnalisé, cela évitera l'indirection.

En termes de performances, comme toujours, vous devez les mesurer. Ne spéculez pas. Au moins en ce qui concerne la numérisation.

Cependant, il y aura certainement un énorme gain de performances lorsque vous créerez ces points en premier lieu, car vous éviterez des allocations de mémoire inutiles pour chaque vector qui stocke un point. Et les allocations de mémoire sont généralement très coûteuses en C++.

7
Sergei Tachenov

Pour ladite valeur de d (<10), définissant Point comme vector<int> doublera presque l'utilisation totale de la mémoire de std::vector<Point> et n'apportera presque aucun avantage.

4
Leon

Comme la dimension est fixe, je vous suggère de choisir un modèle qui utilise la dimension comme paramètre de modèle. Quelque chose comme ça:

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];
};

Utilisez ensuite un std::vector<ndpoint<...>> pour une collection de points pour de meilleures performances.

1
Adrian Colomitchi

La seule façon d'être sûr à 100% de la structure de vos données est d'implémenter entièrement la gestion de votre propre mémoire.

Cependant, il existe de nombreuses bibliothèques qui implémentent des matrices et des opérations matricielles que vous pouvez vérifier. Certains ont des informations documentées sur la mémoire contiguë, le remodelage, etc. (par exemple OpenCV Mat).

Notez qu'en général, vous ne pouvez pas croire qu'un tablea de points sera contigu. Cela est dû à l'alignement, à l'en-tête du bloc d'allocation, etc.

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

Point array_of_points[3];

Maintenant, si vous essayez de `` remodeler '', c'est-à-dire d'itérer entre les éléments Point en se relayant sur le fait que les points sont adjacents dans le conteneur - il est le plus susceptible d'échouer:

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