Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » vector <-> list

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
15.03.2006, 10:54 Uhr
flappinski



Hallo Leute,
ich habe eine allgemeine Frage zu den Containern <Vector> und <List>. Mein Programm besteht aus ziemlich vielen Vectoren (habe ich zum Programmieren als am einfachsten empfunden). Gleichzeitig kommt es bei mir sehr stark auf Performance an (die Berechnungen laufen telweise tagelang). Jetzt bin ich dabei, das Programm zu optimieren. Und nach Tips von Virtual, die ich hier gefunden habe, habe ich bei fast allen Vectoren kurz nach Deklarierung einen reserve Befehl angewendet, da ich meistens vorher die Grösse kenne. Jetzt bin ich auf den Container List gestossen und vor allem auf die Andeutung von Virtual, dass dieser sehr viel schneller arbeitet. Ich glaube, dass viele meiner Vectoren auch als Listen funktionieren würden. Kann mir jemand sagen, was der grosse Unterschied ist und was für Nachteile ich eingehen würde?
Schöne Grüsse,
Stephan
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
15.03.2006, 11:16 Uhr
virtual
Sexiest Bit alive
(Operator)


Na, da ich so oft zitiert werde:

Der wesentliche Unterschied zwischen Vectoren und Listen ist der, daß ein vector die Elemente immer in einem zusammenhängenden Speicher verwaltet, eine Liste dagegen für jedes Element einen getrennten Speicherplatz belegt und intern einfach verweise auf das jeweils nächste/vorhergehende Element verwaltet. Darauf aufbauend kann man nun folgende Beobachtungen machen:

Speicherbedard
Eine Liste benötigt generell mehr Speicher als ein Vector. Wenn Du einen Vector mit integern hast (sagen wir mal 4 Byte Pro integer), wird der Platzbedarf irgendwo bei X+4*N liegen, wobei N die Anzahl der Integer ist und X einen Konstante im bereich <40.
Dagegen liegt der Platzbedarf einer Liste bei (12+X)*N+Y Bytes, wobei X,Y<=10 sind (meistens). Das sind jetzt nur schätzgrößen, aber sie zeigen, daß eine Liste bei kleinen Objekten deutlich mehr SPeicher benötigt.

Einfügegeschwindigkeit
Ohne weitere Verkehrungen wird eine Liste bei Einfügeoperationen einen vector klar abhängen. Der Grund ist recht einfach: weil ein vector die Elemente in einem Zusammenhängenden Speicherbereich halten muß, muß von zeit zu zeit - wenn der Speicher nicht mehr ausreicht - der gesamt Vector kopiert werden. Bei einfügeoperationen mitten im Vector oder am Anfang sind - unabh. davon, ob man genug Speicher hat oder nicht - stets größere Kopierorgien notwendig. Lediglich in dem seht speziellen Fall, daß stets am Ende des Vectors eingefügt wird und der Speicher von vorne herein ausreicht, kann bei Einfügeoperationen ein Vector mit einer Liste mithalten.
Eine Liste belegt einfach Speicher für das neue Element und verkettet es mit den übrigen. Das ist recht billig.

Zugriffsgeschwindigkeit
Will man einfach in der Reihenfolge, in der die Elemente in der Liste/Vector vorliegen auf die Elemente zugreifen, also wenn man zB eine Schleife hat, in der alle Elemente in genau dieser Reihenfolge abgearbeitet werden, dann bedient man sich in der Regel Iteratoren. Im Falle eines Vectors ist der Iterator in der Regel ein einfacher Pointer: Der Zugriff auf das Element entspricht einer einfachen dereferenzierung, das weiterhüpfen einer Pointer addition. Beides extrem billig.
Im Falle einer Liste hängt dies sehr von der konkreten Implementierung ab; aber in der Regel ist die Anzahl der Operationen zum Zugriff oder zum Weiterhüpfen um den Faktor 2-3 Höher als bei einem Vector.
Ausserdem ist die Liste insichtlich der Iteratoren nicht so flexibel: bei einem Vector kann man ziemlich billig auf das 17te Element zugreifen, dagegen muß man bei einer Liste sich extra dorthin hangeln. Dh die Kosten, auf das n-te Element zuzugreifen, liegen bei einer Liste bei O(n), bei einem Vector bei O(1).

Fazit
Häufig ist es günstig, die Elemente zunächst in einer Liste zusammenzuführen. Bei N Elementen macht dies in jedem Fall kosten von O(N). Bei einem Vector im Average Case O(N*N).
Danach kann man - wenn dies aufgrund der Zugriffsweise sinnvoll erscheint - die liste in einen Vector umwandeln. Diese Kosten betragen dann nochmal O(N).
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
15.03.2006, 13:32 Uhr
flappinski



Vielen Dank Virtual,

habe schon damit gerechnet, dass Du dich der Frage annimmst. Sehr ausführlich, wunderbar, jetzt bleibt aber dennoch einige kleine konkrete Probleme offen. Ganz konkret:
Meistens (vor allem bei den Operationen, die für die Performance massgeblich sind) hänge ich die Daten hinten an die Liste/Vektoren und arbeite sie tatsächlich sequenziell ab. Also scheint es günstig für mich zu sein, die Vektoren den Listen zu bevorzugen.

1) Bisher arbeite ich die Vektoren wie ein Array ab, also [1..size()], beipielsweise um die Summe der Quadrate zu errechnen (s.u.): Arbeitet der Prozess mit einem Pointer schneller? Oder ist es das gleiche?


C++:
inline double calc_sumqua(vector <double> &col){
  double sumqua=0.0;
  for (int zz=0; zz < col.size() ; zz++){
    sumqua += sqr(col[zz]);
  }
  return sumqua;
}





2) Ich kenne zumindest die maximale Grösse der Vektoren schon vorher. Was hälst Du davon, doch auf Arrays zurückzustufen. Eigenltich arbeite ich nur mit Vektoren, damit ich die Grösse mitgeliefert bekomme. Da könnte ich aber auch ein Objekt erzeugen, dass die Grösse einfach mitnimmt, oder? Wäre durch Arrays eine spürbare Performanceverbesserung zu erreichen?

Viele Grüsse,
Stephan

Dieser Post wurde am 15.03.2006 um 13:33 Uhr von flappinski editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
15.03.2006, 13:55 Uhr
ao

(Operator)



Zitat von flappinski:
Meistens (vor allem bei den Operationen, die für die Performance massgeblich sind) hänge ich die Daten hinten an die Liste/Vektoren und arbeite sie tatsächlich sequenziell ab. Also scheint es günstig für mich zu sein, die Vektoren den Listen zu bevorzugen.

Ja.


Zitat:
Bisher arbeite ich die Vektoren wie ein Array ab, also [1..size()], beipielsweise um die Summe der Quadrate zu errechnen (s.u.): Arbeitet der Prozess mit einem Pointer schneller? Oder ist es das gleiche?

Vielleicht, ein bisschen. Aber ich habe auch schon Beispiele gesehen, wo es genau umgekehrt war. Weiß leider die Zusammenhänge nicht mehr.

Ich würde umstellen auf einen Iterator; wie der intern arbeitet, braucht dich nicht zu interessieren. Du kannst dann einfach den Code zwischen vector und list umändern; der Iterator bleibt syntaktisch gleich, nur die Implementierung wird ausgetauscht.

Aber bei den STL-Klassen würde ich einfach mal blind annehmen, dass sie ganz gut optimiert sind.

ao
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
15.03.2006, 15:04 Uhr
virtual
Sexiest Bit alive
(Operator)


Hallo,
ich würde auch auf Iterator umstellen: Ein Iterator ist bei einem Vector einfach ein Pointer, ggf. würde ich sogar insgesamt ein wenig mehr in die STL eintauchen:

C++:
inline double calc_sumqua(vector <double> &col){
  double sumqua=0.0;
  for (int zz=0; zz < col.size() ; zz++){
    sumqua += sqr(col[zz]);
  }
  return sumqua;
}


Ist in Iterator schreibweise eigentlich nur noch:

C++:
// Nur zur vereinfachung
typedef std::vector<double> double_vector;
typedef double_vector::const_iterator const_double_iterator;

inline double calc_sumqua(const double_vector& col){

  double sumqua=0.0;
  const_double_iterator end = col.end();
  for(double_iterator i=col.begin(); i!=end; ++i) {
       sumqua +=sqr(*i);
  }
  return sumqua;
}


--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
15.03.2006, 19:06 Uhr
flappinski



Danke schön,
sehr ausführlich, ich bin begeistert. Vor allem von dem Code-Beispiel.
Nur noch zwei Dinge:
1) Was haltet Ihr von meiner 2.Anmerkung aus dem letzten Thread (also Richtung Array umstellen?)
2) Warum eigentlich auf Pointer umstellen? Läuft doch alles. Und der Code wird für mich auch nicht einfacher (allerdigns auch nicht schwerer). Wird es denn jetzt dadurch schneller, oder welchen vorteil bekomme ich dadurch?
Gruss,
Stephan
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
15.03.2006, 20:28 Uhr
virtual
Sexiest Bit alive
(Operator)


ad 1)
Hängt davon ab. Soweit ich verstanden habe, kennst Du zwar die Obergrenze der Möglichen Elemente im Array/Vector, jedoch nicht zwingend die tatsächliche. Wenn dem so ist, dann ist ein STL Container ala Vector die bessere Wahl, weil Du sonst überall die tatsächliche Anzahl der Arrayelemente mit an die funktion übergeben müsstest. Das ist sehr umständlich. Und da ein vector ja intern auch einfach nur ein Array ist, ist durch den Umstief auf ein "echtes" Array kein deutlicher Performancegewinn zu erwarten.


ad 2)
Zwar verhalten sich Iteratoren wie Pointer, aber Iteratoren haben in C++ schon eine gewisse Bedeutung. Man kann damit seine sachen zaubern. Nehmen wir mal Deine Routine, die ich ja schon auf Iteratoren umgestellt habe. Der nächste logische Schritt wäre, Coderedundanzen zu vermeiden und einfach die STL machen lassen. Denn sowas wie Aufsummieren kann die ja schon. Ich schreibe also nochmal um:

C++:

// Nur zur vereinfachung
typedef std::vector<double> double_vector;
typedef double_vector::const_iterator const_double_iterator;


// Deine Ursprüngliche Funktion
inline double calc_sumqua(const double_vector& col){
      // numeric includieren!
     return inner_product(v.begin(), v.end(), v.begin(), 0.0); // Unter der Annahmen, daß sqr(v)==v*v ist
}



Letztlich würde ich das ganze dann also sowieso schreiben als:

C++:
template<typename I>
inline double calc_sumqua(I begin, I end){
     return inner_product(begin, end, begin, 0.0);
}


Und kann das dann immer gleich anwenden, egal, ob ich mich nun für ein Array, einen Vector einer Liste entscheide:

C++:
std::list<double> l;
std::vector<double v;
double a[..];

calc_sumqua(l.begin(), l.end());  // Aufruf liste
calc_sumqua(v.begin(), v.end()); // Aufruf vector
calc_sumqua(a, a+sizeof(a)/sizeof(*a)); // Aufruf Array



In diesem Zusammenhang sei noch darauf hingewiesen, daß es mit valarrays einen weiteren, auf numerische Operationen optimierten Container gibt.
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: