Wie teilt Vectoren sauber
-
Hallo,
in meinen numerischen Codes habe ich mit mehreren großen Feldern zu tun. Die ich am Anfang des Programm als einen großen Vector allokiere.
... double *a = new double [100000]; double *b = &a[10000]; double *c = &a[20000]; ...Wenn ich wie es hier propagiert wird die rohen Zeiger durch vector ersetzen möchte, wie weise ich dann b und c zu. Ist die Lösung dann alle einzeln zu allokieren und möglicherweise erst spät im Programm zu merken, ich bekomme nicht genug Speicher zugeteilt?
-
andreasgeorg schrieb:
Wenn ich wie es hier propagiert wird die rohen Zeiger durch vector ersetzen möchte, wie weise ich dann b und c zu. Ist die Lösung dann alle einzeln zu allokieren und möglicherweise erst spät im Programm zu merken, ich bekomme nicht genug Speicher zugeteilt?
Ja, du allozierst den Speicher einzeln, indem du schreibst:
std::vector<double> a(size_a); std::vector<dobule> b(size_b); ...Und wenn du nicht genug Speicher bekommst, wirst du das schon merken.
new schmeißt im Fehlerfall eine Exception und wenn du die nicht fängst, bricht dein Programm ab.
-
Nein, bei festen Größen nutzt man
std::array. Außer die Größen sind so groß, das der Stack zu klein wird...std::array<double, size_a> a; std::array<dobule, size_b> b;Zehntausend Skalare sind (eine Größe von jeweils 8 Byte vorausgesetzt) 80 Kilobytes. Ein Stack ist i.d.R irgendwo im einstelligen Megabyte-Bereich. Sollte wohl gehen, wenn das nicht dutzende Arrays werden. Sonst müsstest du tatsächlich besser vom Heap allozieren (=>
vector).
-
Danke Euch beiden,
std::array geht nicht, da die "100000" (können aber auch gerne Mio's sein) aus einem Configfile gelesen wird.
Schade das das nicht mehr so schön wie früher geht, es war bisweilen schon angenehm das Hauptfeld an die Funktionen zu übergeben und sich dort die nötigen Teilfelder zu extrahieren. Na dann muß ich mal sehen wie konform ich sein will
.
-
andreasgeorg schrieb:
Schade das das nicht mehr so schön wie früher geht, es war bisweilen schon angenehm das Hauptfeld an die Funktionen zu übergeben und sich dort die nötigen Teilfelder zu extrahieren.
size_t size = readFromConfig(); vector<double> v(size); fülleDaten(v); foo(v); void foo(const vector<double> &data){ for (int i = relevanteDatenStart; i < relevanteDatenEnde; i++) tuwas(data[i]); }Wenn das nicht das Richtige ist erklär mal genauer was "die nötigen Teilfelder extrahieren" bedeutet.
-
... std::vector<double> a(100000); double *b = &a[10000]; double *c = &a[20000]; ...Wo ist das Problem?
-
manni66 schrieb:
... std::vector<double> a(100000); double *b = &a[10000]; double *c = &a[20000]; ...Wo ist das Problem?
Das ist so ziemlich das selbe wie bisher, damit kann ich gut leben. Mir war nicht bewußt, daß das so erlaubt ist. Da die Änderung aber nur ein new und ein free ersetzt, lohnt es sich überhaubt die Kontainer nutzen zu wollen?
Danke
@nwp3
Der Konstrukt ist so das die Felder z.B. U,V, W und noch ein paar mehr direkt hintereinander liegen. Und es wird etwa folgendes gemacht.int main(int argc, char* argv[]) { double *vec = GetVecFromCFG(); int size = GetSizeFromCFG(); if(vec) { ... foo(vec,size); ... free vec; } } void foo(double* vec, int n) { double *u = vec; double *v = &vec[n]; double *w = &vec[2*n]; ... }Die Idee war nun die neuen Kontainer zu nutzen wenn es den Code besser macht. Besser in Form von Lesbarkeit, Sicherheit und natürlich keine Performanceverluste.
An einigen anderen Stellen habe ich bereits C++11 genutzt, insbesondere bei der Ein-/Ausgabe aber an den Vektoren habe ich mich bisher nicht vergriffen.
-
lohnt es sich überhaubt die Kontainer nutzen zu wollen?
Wenn ein Auto dich ja nur zehn mal schneller ans Ziel bringt, lohnt es sich überhaupt, es zu benutzen?
-
andreasgeorg schrieb:
Das ist so ziemlich das selbe wie bisher, damit kann ich gut leben. Mir war nicht bewußt, daß das so erlaubt ist. Da die Änderung aber nur ein new und ein free ersetzt, lohnt es sich überhaubt die Kontainer nutzen zu wollen?
Ich hoffe du meinst delete.
Bitte, bitte, mach, dass er sich vertan hat.Die Idee war nun die neuen Kontainer zu nutzen wenn es den Code besser macht. Besser in Form von Lesbarkeit, Sicherheit und natürlich keine Performanceverluste.
An einigen anderen Stellen habe ich bereits C++11 genutzt, insbesondere bei der Ein-/Ausgabe aber an den Vektoren habe ich mich bisher nicht vergriffen.std::vector ist wesentlich besser als new und delete:
- er kann wachsen
- er hat die Größe integriert
- ist mit STL Algorithmen kompatibel
- er ist exception safe
- er gibt den Speicher auf jeden Fall frei
- er hat Unterstützung für verschiedene Allokatoren direkt integriert
- ...Alles keine Dinge, die Pointer + new haben.
-
andreasgeorg schrieb:
Da die Änderung aber nur ein new und ein free ersetzt [...]
Wenn du was mit new anlegst, dann musst du es mit delete freigeben und nicht mit free()...
andreasgeorg schrieb:
[...] lohnt es sich überhaubt die Kontainer nutzen zu wollen?
Wie kümmerst du dich im Moment um die Speicherverwaltung? Exception Safety? Wie stellst du im Moment sicher, dass kein möglicher Ausführungspfad leaked?
-
Alles keine Dinge, die Pointer + new haben.
Wobei das letzte Argument mit dieser Begründung keinen Sinn macht - schließlich kannst du alle anderen Allokatoren auch so benutzen.
-
Asche auf mein Haupt ich weiß natürlich das new und delete zusammen gehören. Die Zeit wo ich malloc/free brauchte liegt weit in der Vergangenheit. Mea culpa
.
-
Sone schrieb:
Alles keine Dinge, die Pointer + new haben.
Wobei das letzte Argument mit dieser Begründung keinen Sinn macht - schließlich kannst du alle anderen Allokatoren auch so benutzen.
Ja, aber mit new muss man erst allocate() und dann construct() aufrufen, bei vector lediglich ein Templateargument ändern.
-
dot schrieb:
andreasgeorg schrieb:
[...] lohnt es sich überhaubt die Kontainer nutzen zu wollen?
Wie kümmerst du dich im Moment um die Speicherverwaltung? Exception Safety? Wie stellst du im Moment sicher, dass kein möglicher Ausführungspfad leaked?
Nun die Speicherverwaltung ist recht simpel. Nach dem Einlesen der Inputdatei Speicher anfordern, wenn ich ihn nicht bekomme war es das -> Fehlermeldung. Sonst rechnen, Daten schreiben, dann Speicher freigeben mit delete.
Speicherleaks sind dadurch kaum möglich das nur einmal Speicher angefordert wird bevor die Rechenthreads gestartet werden.
Exception Safety - Kopf einziehen.
-
andreasgeorg schrieb:
Da die Änderung aber nur ein new und ein free ersetzt, lohnt es sich überhaubt die Kontainer nutzen zu wollen?
So, wie ich dich verstanden habe, handelt es sich um eine globale Variable, die zum Programmstart initialisiert wird und eigentlich nie freigeben werden muss. Der einzige Gewinn liegt dann darin, dass das Feld komplett mit 0 initialisiert wird.
Bei globalen Variablen solte man sich immer fragen, ob es sinnvoll und notwendig ist. Im Regelfall lautet die Antwort "Nein".
-
andreasgeorg schrieb:
Sonst rechnen, Daten schreiben, dann Speicher freigeben mit delete.
Und beim Rechnen und Daten Schreiben kann nirgendwo was schiefgehen?
-
Mal ein Beispiel was man machen könnte. Die Rangechecks gehen nicht kostenlos, deswegen kann man die auskommentieren.
#define DEBUG //kommentier mich aus damits schneller geht //unter VS lieber _DEBUG benutzen, das wird automatisch definiert //beim gcc NDEBUG definieren, damit die asserts weg gehen struct SubVector{ #ifdef DEBUG static SubVector create(vector<double> &v, size_t start, size_t size){ return SubVector(v, start, size); } static const SubVector create(const vector<double> &v, size_t start, size_t size){ return SubVector(const_cast<vector<double> &>(v), start, size); } #else static SubVector create(vector<double> &v, size_t start){ return SubVector(v, start); } static const SubVector create(const vector<double> &v, size_t start){ return SubVector(const_cast<vector<double> &>(v), start); } #endif double &operator [](size_t index){ assert(index < size); return data[size]; } private: #ifdef DEBUG SubVector(vector<double> &v, size_t start, size_t size_) : size(size_){ assert(v.size() > start + size); //sonst greifen wir daneben data = &v[start]; } #else SubVector(vector<double> &v, size_t start){ data = &v[start]; } #endif double *data; #ifdef DEBUG size_t size; #endif };Die vielen #ifdefs nerven und dass C++ sowas nicht unterstützt auch:
SubVector(const vector<double> &v, size_t start) : data(&v[start]) const{ }Im Endeffekt kannst du deine double * durch SubVector ersetzen und hast im Debugmode Range-Checks drin. Im Release tut der SubVector dann dasselbe wie der double *.
Ob das jetzt sauberer ist... naja entscheide selbst.
-
@npw3:
Es ist völlig redundant, die Funktionsdefinition zweimal stehen zu haben, wenn nur ein kleiner Teil davon je nach Makro unterschiedlich ist.
Also die PP-Direktiven in den Funktionsrumpf auslagern.SubVector(const vector<double> &v, size_t start) : data(&v[start]) const{ }Dieser Code macht keinen Sinn. Vor der Konstruktion eines Objektes ist seine Konstanz unwichtig, sonst kämen wir ja zu dem Dilemma das wir es nicht initialisieren könnten (weil es schon im Konstruktor nicht veränderbar ist).
Außerdem hast du den (mMn. nötigen) Konvertierungsoperator vergessen, sonst ist das schnöde
SubVectorsemantisch nicht so dasselbe wie ein Zeiger...
-
Nein, bei festen Größen nutzt man std::array. Außer die Größen sind so groß, das der Stack zu klein wird
Immer deine Pauschalantworten. Was, wenn das Array moveable sein soll? Nein, dafuer werde ich std::Array nicht in einen Smartpointer packen sondern gleich std::vector nehmen.
#ifdef DEBUG ... code #else ... code #endifWie ich das hasse. Ausserdem scheinst jemand nicht verstanden zu haben, was assert in Abhaengigkeit vom Debug und Release-Modus macht.
-
Danke an Alle,
ich habe einige Denkanstöße bekommen.