Vectorii STL cu spațiu de stocare neinitializat?

Scriu o buclă interioară care trebuie să plaseze struct s în spațiul de stocare învecinat. Nu știu câte dintre aceste struct s vor fi anticipate. Problema mea este că vectorul al STL iniționează valorile sale la 0, deci indiferent de ce fac, suport costul inițializării plus costul de setare a membrilor struct la valorile lor.

Există vreo modalitate de a preveni inițializarea sau există un container asemănător STL acolo, cu un spațiu de stocare contiguu redimensionabil și elemente neinitializate?

(Sunt sigur că această parte a codului trebuie să fie optimizată și sunt sigur că inițializarea este un cost semnificativ.)

De asemenea, vedeți comentariile mele de mai jos pentru a clarifica momentul inițializării.

UNIC COD:

void GetsCalledALot(int* data1, int* data2, int count) {
    int mvSize = memberVector.size()
    memberVector.resize(mvSize + count);//causes 0-initialization

    for (int i = 0; i < count; ++i) {
        memberVector[mvSize + i].d1 = data1[i];
        memberVector[mvSize + i].d2 = data2[i];
    }
}
0
O altă clarificare: nu este faptul că constructorul inițializează valorile la 0. Este vorba de redimensionarea apelurilor de inserare, ceea ce face.
adăugat autor Jim Hunziker, sursa
Notă - utilizarea rezervei() nu este o soluție, deoarece nu puteți accesa în mod legal datele care se găsesc în locații() și mai sus.
adăugat autor Jim Hunziker, sursa
NOTĂ: nu puteți accesa oricum datele neinitializate. Aceeași problemă pentru vectorul din trecut .end() și membrii neinitializați ai unui T []. Dar cu un vector, sunt șanse ca codul de depanare să vă spună asta acum. Codul matricei va eșua în tăcere pe PC-ul clienților.
adăugat autor MSalters, sursa
De asemenea, consultați stackoverflow.com/q/7218574/1969455 pentru o abordare alocată. (bun pentru tipurile POD)
adăugat autor Matthäus Brandl, sursa
Ne puteți da și declarația structului? Mulțumiri... :-)
adăugat autor paercebal, sursa
Aceasta este o întrebare bună. Pentru unele aplicații, este important să realizăm că vectorul std :: inițializează întotdeauna elementele sale, chiar dacă acestea sunt date simple (POD).
adăugat autor nobar, sursa

14 răspunsuri

Deci, aici este problema, redimensionarea se numește insert, care face o copie de construcție dintr-un element implicit construit pentru fiecare dintre elementele nou adăugate. Pentru a obține acest lucru la costul de 0, trebuie să scrieți propriul constructor implicit ȘI constructorul dvs. de copiere propriu ca și funcții goale. Făcând asta la constructorul dvs. de copiere este o idee foarte rău , deoarece va întrerupe algoritmii de realocare internă a vectorului std :: vector.

Rezumat: Nu veți putea să faceți acest lucru cu vectorul std :: vector.

0
adăugat
Aceasta este problema reală. std :: vectorul ar trebui să realizeze că nu trebuie să facă nici o inițializare dacă T are un constructor implicit trivial. Vă mulțumim că ați subliniat că constructorul de copiatoare este ceea ce face munca inutilă aici.
adăugat autor Eric Hein, sursa

Pentru a clarifica răspunsurile de rezervă (): trebuie să utilizați rezerva() împreună cu push_back (). În acest fel, constructorul implicit nu este apelat pentru fiecare element, ci mai degrabă pentru constructorul de copiere. Încă mai suporți pedeapsa de instalare a structului pe stivă și apoi copiați-o pe vector. Pe de altă parte, este posibil ca, dacă utilizați

vect.push_back(MyStruct(fieldValue1, fieldValue2))

compilatorul va construi noua instanță direct în memoria care se află în raport cu vectorul. Depinde de cât de inteligent este optimizatorul. Trebuie să verificați codul generat pentru a afla.

0
adăugat
Se pare că optimizatorul pentru gcc, la nivelul O3, nu este suficient de inteligent pentru a evita copierea.
adăugat autor Jim Hunziker, sursa

C ++ 0x adaugă un nou șablon al funcției membru emplace_back la vector (care se bazează pe șabloane variadic și redirecționare perfectă)

memberVector.emplace_back(data1[i], data2[i]);
0
adăugat

În C ++ 11 (și boost) puteți utiliza versiunea de matrice unique_ptr pentru a aloca o matrice neinitializată. Acest lucru nu este destul de un container stl, dar este încă memorie gestionată și C ++ - ish care va fi suficient de bun pentru multe aplicații.

auto my_uninit_array = std::unique_ptr(new mystruct[count]);
0
adăugat

Utilizați metoda std :: vector :: reserve (). Nu va redimensiona vectorul, dar va aloca spațiul.

0
adăugat

Err ...

încercați metoda:

std::vector::reserve(x)

Acesta vă va permite să rezervați suficientă memorie pentru elementele x fără a inițializa orice (vectorul dvs. este încă gol). Astfel, nu va exista o realocare până când nu va trece peste x.

Al doilea punct este că vectorul nu va inițializa valorile la zero. Testați codul în depanare?

După verificarea pe g ++, următorul cod:

#include 
#include 

struct MyStruct
{
   int m_iValue00 ;
   int m_iValue01 ;
} ;

int main()
{
   MyStruct aaa, bbb, ccc ;

   std::vector aMyStruct ;

   aMyStruct.push_back(aaa) ;
   aMyStruct.push_back(bbb) ;
   aMyStruct.push_back(ccc) ;

   aMyStruct.resize(6) ;//[EDIT] double the size

   for(std::vector::size_type i = 0, iMax = aMyStruct.size(); i < iMax; ++i)
   {
      std::cout << "[" << i << "] : " << aMyStruct[i].m_iValue00 << ", " << aMyStruct[0].m_iValue01 << "\n" ;
   }

   return 0 ;
}

oferă următoarele rezultate:

[0] : 134515780, -16121856
[1] : 134554052, -16121856
[2] : 134544501, -16121856
[3] : 0, -16121856
[4] : 0, -16121856
[5] : 0, -16121856

Initializarea pe care ai vazut-o a fost probabil un artefact.

[EDIT] După comentariul despre redimensionare, am modificat codul pentru a adăuga linia de redimensionare. Redimensionarea numește în mod eficient constructorul implicit al obiectului din interiorul vectorului, dar dacă constructorul implicit nu face nimic, atunci nimic nu este inițializat ... Încă mai cred că a fost un artefact (am reușit pentru prima oară să aibă întreg vectorul zerouzat cu următorul cod:

aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;

Asa de... : - /

[EDIT 2] Ca deja oferita de Arkadiy, solutia este de a folosi un constructor inline care sa ia parametrii doriti. Ceva asemănător cu

struct MyStruct
{
   MyStruct(int p_d1, int p_d2) : d1(p_d1), d2(p_d2) {}
   int d1, d2 ;
} ;

Aceasta va fi probabil introdusă în codul dvs.

Dar, oricum, ar trebui să studiezi codul cu un profiler pentru a fi sigur că această bucată de cod este o piedică în calea cererii tale.

0
adăugat
Am scris o notă de mai sus. Nu este constructorul vectorului care inițializează la 0. Este redimensionat() care face.
adăugat autor Jim Hunziker, sursa
Cred că ești pe drumul cel bun. Nu am definit niciun constructor în struct, deci constructorul său implicit (cred) zero-inițializează. Voi verifica dacă adăugarea unui constructor implicit care nu face nimic rezolvă problema.
adăugat autor Jim Hunziker, sursa
Greg Rogers are dreptate. Cred că memoria era "zero" din cauza inițializării unor procese independente de codul pe care l-am scris. În C ++, nu plătiți pentru ceva ce nu îl utilizați. Deci, dacă scrieți un cod asemănător C, nu ar trebui să aveți aeriene. Și vectorii sunt destul de buni la asta.
adăugat autor paercebal, sursa
@nobar: Depinde de constructorul MyStruct. Dacă este gol și în linie și membrii MyStruct au constructori cu cost zero, atunci compilatorul C ++ îl va optimiza la nimic. Atunci nu vom plăti pentru asta. Doar pentru redimensionare.
adăugat autor paercebal, sursa
Se pare că vectorul ne-a lăsat în acest caz. Noi plătim pentru inițiere, chiar dacă nu avem nevoie sau ne dorim. Acest lucru este garantat de semantica insertului() care se numește prin redimensionare (). Valoarea folosită pentru inițializare se bazează pe ceea ce se întâmplă în MyStruct pentru a redimensiona (). Deoarece nu ați specificat nimic când ați sunat la redimensionare (), a fost utilizat constructorul implicit. Deoarece constructorul implicit nu face nimic în acest caz, este posibil să obțineți zerouri sau să obțineți altceva. În orice caz, veți plăti pentru inițializarea efectuată
adăugat autor nobar, sursa
Dacă nu ați definit niciun constructor și toate elementele sunt tipuri POD, atunci constructorul nu face nimic. Dacă elementele nu sunt POD, atunci ei ar suna pur și simplu pe constructorii impliciți.
adăugat autor Greg Rogers, sursa
În acest caz, MyStruct are un constructor trivial, astfel încât nimic nu este inițializat. Acest lucru poate fi diferit de situația PO.
adăugat autor Greg Rogers, sursa

Structurile trebuie să fie în memorie contiguoasă sau puteți obține un vector de struct *?

Vectorii fac o copie a ceea ce le adaugi, astfel incat folosind vectori de pointeri mai degraba decat obiecte este o modalitate de a imbunatati performanta.

0
adăugat
Trebuie să fie învecinate. Sunt într-un tampon care urmează să fie trimis prin rețea ca o bucată mare.
adăugat autor Jim Hunziker, sursa

Nu cred că STL este răspunsul tău. Veți avea nevoie să vă rostogoliți propria soluție folosind realloc (). Va trebui să stocați un indicator și dimensiunea sau numărul de elemente și să îl utilizați pentru a găsi de unde să începeți să adăugați elemente după o realloc ().

int *memberArray;
int arrayCount;
void GetsCalledALot(int* data1, int* data2, int count) {
    memberArray = realloc(memberArray, sizeof(int) * (arrayCount + count);
    for (int i = 0; i < count; ++i) {
        memberArray[arrayCount + i].d1 = data1[i];
        memberArray[arrayCount + i].d2 = data2[i];
    }
    arrayCount += count;
}
0
adăugat

Din comentariile dvs. către alte postere, se pare că ați rămas cu malloc() și cu prietenii. Vectorul nu vă va lăsa să aveți elemente neconstruite.

0
adăugat

Din codul dvs., se pare că aveți un vector de structuri, fiecare dintre ele cuprinzând 2 inci. Puteți folosi în schimb 2 vectori de inți? Apoi

copy(data1, data1 + count, back_inserter(v1));
copy(data2, data2 + count, back_inserter(v2));

Acum nu plătiți pentru copierea unui struct de fiecare dată.

0
adăugat
Interesant. Acest lucru ar putea funcționa doar - se pare că ar evita construirea unui obiect intermediar.
adăugat autor nobar, sursa

Aș face ceva de genul:

void GetsCalledALot(int* data1, int* data2, int count)
{
  const size_t mvSize = memberVector.size();
  memberVector.reserve(mvSize + count);

  for (int i = 0; i < count; ++i) {
    memberVector.push_back(MyType(data1[i], data2[i]));
  }
}

Trebuie să definiți un ctor pentru tipul care este stocat în elementVector, dar acesta este un cost mic, deoarece vă va oferi cele mai bune din ambele lumi; nu se face inițializare inutilă și nu se va face nicio realocare în timpul buclă.

0
adăugat
Aceasta nu pare să rezolve problema, deoarece folosește un MyType temporar() și o copiază în vector. Există încă o dublă inițializare.
adăugat autor nobar, sursa

std::vector must initialize the values in the array somehow, which means some constructor (or copy-constructor) must be called. The behavior of vector (or any container class) is undefined if you were to access the uninitialized section of the array as if it were initialized.

Cea mai bună modalitate este să utilizați reserve() și push_back() , astfel încât constructorul de copiere să fie utilizat, evitând construcția implicită.

Folosind exemplul dvs. de cod:

struct YourData {
    int d1;
    int d2;
    YourData(int v1, int v2) : d1(v1), d2(v2) {}
};

std::vector memberVector;

void GetsCalledALot(int* data1, int* data2, int count) {
    int mvSize = memberVector.size();

   //Does not initialize the extra elements
    memberVector.reserve(mvSize + count);

   //Note: consider using std::generate_n or std::copy instead of this loop.
    for (int i = 0; i < count; ++i) {
       //Copy construct using a temporary.
        memberVector.push_back(YourData(data1[i], data2[i]));
    }
}

Singura problemă cu apelarea reserve() (sau resize() ) este că puteți ajunge la constructorul de copiat mai des decât trebuie. Dacă puteți face o bună estimare cu privire la dimensiunea finală a matricei, este mai bine să reserve() spațiul o dată la început. Dacă nu cunoașteți dimensiunea finală, cel puțin numărul de copii va fi minim în medie.

In the current version of C++, the inner loop is a bit inefficient as a temporary value is constructed on the stack, copy-constructed to the vectors memory, and finally the temporary is destroyed. However the next version of C++ has a feature called R-Value references (T&&) which will help.

Interfața furnizată de std :: vector nu permite o altă opțiune, care este de a folosi o anumită clasă din fabrică pentru a construi alte valori decât cea implicită. Iată un exemplu brut despre ceea ce ar arăta acest model ca implementat în C ++:

template 
class my_vector_replacement {

   //...

    template 
    my_vector::push_back_using_factory(F factory) {
       //... check size of array, and resize if needed.

       //Copy construct using placement new,
        new(arrayData+end) T(factory())
        end += sizeof(T);
    }

    char* arrayData;
    size_t end;//Of initialized data in arrayData
};

// One of many possible implementations
struct MyFactory {
    MyFactory(int* p1, int* p2) : d1(p1), d2(p2) {}
    YourData operator()() const {
        return YourData(*d1,*d2);
    }
    int* d1;
    int* d2;
};

void GetsCalledALot(int* data1, int* data2, int count) {
   //... Still will need the same call to a reserve() type function.

   //Note: consider using std::generate_n or std::copy instead of this loop.
    for (int i = 0; i < count; ++i) {
       //Copy construct using a factory
        memberVector.push_back_using_factory(MyFactory(data1+i, data2+i));
    }
}

Făcând asta înseamnă că trebuie să vă creați propria clasă de vectori. În acest caz, complică și ceea ce ar fi trebuit să fie un exemplu simplu. Dar pot exista momente în care utilizarea unei funcții a fabricii ca aceasta este mai bună, de exemplu dacă inserția este condiționată de o altă valoare și ar trebui să construiți în mod necondiționat, în mod necondiționat, unele temporare scumpe, chiar dacă aceasta nu era efectiv necesară.

0
adăugat

Dacă într-adevăr insistați asupra faptului că elementele au fost neinitializate și sacrificați anumite metode precum front (), back (), push_back (), utilizați vectorul de amplificare de la numeric. Vă permite chiar să nu păstrați elementele existente atunci când apelați redimensionarea() ...

0
adăugat

Puteți utiliza un tip de înfășurare în jurul tipului dvs. de element, cu un constructor implicit care nu face nimic. De exemplu.:

template 
struct no_init
{
    T value;

    no_init() { static_assert(std::is_standard_layout>::value && sizeof(T) == sizeof(no_init), "T does not have standard layout"); }

    no_init(T& v) { value = v; }
    T& operator=(T& v) { value = v; return value; }

    no_init(no_init& n) { value = n.value; }
    no_init(no_init&& n) { value = std::move(n.value); }
    T& operator=(no_init& n) { value = n.value; return this; }
    T& operator=(no_init&& n) { value = std::move(n.value); return this; }

    T* operator&() { return &value; }//So you can use &(vec[0]) etc.
};

A folosi:

std::vector> vec;
vec.resize(2ul * 1024ul * 1024ul * 1024ul);
0
adăugat