Geschwindigkeit bei Schreiben auf Speicher
-
Ich habe 4000 Integer Werte und jetzt frage ich mich, was schneller ist im Schreib- und Lesezugriff.
int* ptr = new int[4000]; // *data is some pointer so the values I intent to save memcpy(ptr, data, 4000);
oder
std::vector<int> v{}; for (size_t i{0}; i < 4000; ++i) { v.push_back(data[i]); }
Danke für eure Hilfe
-
memcpy ist schneller. Das zweite tut nämlich gar nichts.
-
memcpy
ist schneller. Was zum Teil auch daran liegt, dass du keinen Speicher fürv
reservierst (es gibt also ein paar reallocations), und zum anderen daran, dass4000
die falsche Längenangabe ist.Du kannst dieselbe Performance erreichen, wenn du den
vector
zuvor auf 4000 resized und dannstd::copy
verwendest, oder wahrscheinlich auch wenn du den Iterator-paar Konstruktor einsetzt (da wird intern wahrscheinlich das gleiche, optimierte Funktionstemplate aufgerufen).
-
wieso ist 4000 die falsche Längenangabe und wieso tut de untere Möglichkeit nichts?
meinst du reserve oder resize?
-
Sewing schrieb:
wieso ist 4000 die falsche Längenangabe und wieso tut de untere Möglichkeit nichts?
meinst du reserve oder resize?
std::vector<int> v{}; // <- deklariert eine Funktion for (size_t i{0}; i < 4000; ++i) { v.push_back(data[i]); // Wenn der Compiler das übersetzt, wirf ihn weg }
VG
Edit: Sehe gerade, daß du geschweifte Klammern benutzt hast. Nehme meine Aussage daher wieder zurück.
-
Sewing schrieb:
wieso ist 4000 die falsche Längenangabe und wieso tut de untere Möglichkeit nichts?
memcpy
s dritter Parameter repräsentiert die Byteanzahl. Ich bezweifle stark, dassint
auf irgendeiner Maschine 1 Byte lang ist (auch wenn das prinzipiell möglich wäre, da wir von einem C++-Byte sprechen).meinst du reserve oder resize?
Ich meine
reverse
fürs reservieren. Die Größe kannst du auch im Konstruktor angeben. Wobei ich wie erwähnt die Iterator-Paar Variante ausprobieren würde.Und noch ein Tipp: Ich würde mir um solche Dinge erstmal keine Gedanken machen. Eigne dir idiomatisches C++ an, dann wird dein Code in vielen Fällen auch optimal schnell werden (da idiomatisches C++ i.d.R. auch hinsichtlich Performance optimal ist). Falls deine Anwendung dann zu langsam ist, profilierst du sie. Deine frühreife "Optimierung" mit
mempcy
ist ja irgendwie schon schief gegangen.
-
memcpy rechnet in Byte. Es weiß, wie so ziemlich alle C-Funktionen, nichts von den unterliegenden Typen. Dein Beispiel memcpy kopiert also wesentlich weniger als du denkst.
Nimm, wie schon gesagt wurde, std::copy, wenn du einen memcpy-artigen Effekt in C++ möchtest. Da können solche Fehler nicht passieren und es ist genau so schnell, und es kommt auch mit komplexen Datentypen zurecht.
Oder, wie auch schon gesagt, wenn es darum geht, eine Datenstruktur zu initialisieren, dann mach es direkt, ohne copy.
-
Ok Danke, werde ich mir zu Herzen nehmen.
das ganze soll allerdings in einer Methode stattfinden von einer Klasse, die nen member std::vector<int> hat. Das heißt, ich kann nicht den ctor für eine iterator range verwenden, sondern nur deren assign-variante. Taugt das auch?
Ich weiß leider apriori nicht, wieviele Werte der vector später hat
-
Sewing schrieb:
das ganze soll allerdings in einer Methode stattfinden von einer Klasse, die nen member std::vector<int> hat. Das heißt, ich kann nicht den ctor für eine iterator range verwenden, sondern nur deren assign-variante. Taugt das auch?
Ja.
-
Also drei Möglichkeiten,
std::vector<uint16_t> _AplitudeImg{}; void setAmplitudeImg(uint16_t* data) { _AmplitudeImg.assign(data, data + 4000); }
std::vector<uint16_t> _AplitudeImg{}; void setAmplitudeImg(uint16_t* data) { std::copy(data, data+4000, _AmplitudeImg.begin()); }
std::vector<uint16_t> _AplitudeImg{}; void setAmplitudeImg(uint16_t* data) { memcpy(_AmplitudeImg.data(), data, sizeof(uint16_t)); }
korrekt?
macht es Sinn, vorher den vector auf irgendeinen Wert zu reserven im constructor?
Gibt es eine Möglichkeit einen leeren container mit einer bestimmten capazität anzulegen? Denn wenn ich schreibe
std::vector<int> v(4000)
habe ich ja direkt 4000 value-initialized integer, die ich später sowieso wieder reassigne
oder gibt es es wirklich nur
std::vector<int> v{}; v.reserve(4000);
-
1. Du musst den
vector
vor demcopy
entsprechend vergrößern, à lav.resize(4000)
.
2.reserve
macht Sinn, wenn dervector
unmittelbar danach wiederholt (bspw. durch vielepush_back
s) auf einen bekannten oder gut abschätzbaren Wert vergrößert wird.
-
warum resize und nicht reserve? Ich kopiere doch sowieso andere Werte unmittelbar danach in den Container
-
Lies dir doch endlich mal die Konstruktoren von vector durch! Das wurde schon mehrmals in diesem Thread gesagt.
std::vector<uint16_t> _AplitudeImg(data, data + 4000);
Da! Eine Zeile, so schnell wie es geht, keine Möglichkeit, irgendetwas falsch zu machen.
-
wenn du meinen Beitrag gelesen hättest, dann wüsstest du, dass ich keine Möglichkeit habe, den container direkt zu initialisieren sondern das über ne member function tun muss
-
@SeppJ: Sein vorletzter Beitrag sagt doch unmissverständlich, dass eine Memberfunktion den Container neu befüllen soll. Lesen musst auch Du.
-
SeppJ schrieb:
Lies dir doch endlich mal die Konstruktoren von vector durch! Das wurde schon mehrmals in diesem Thread gesagt.
std::vector<uint16_t> _AplitudeImg(data, data + 4000);
Da! Eine Zeile, so schnell wie es geht, keine Möglichkeit, irgendetwas falsch zu machen.
Arcoth schrieb:
@SeppJ: Sein vorletzter Beitrag sagt doch unmissverständlich, dass eine Memberfunktion den Container neu befüllen soll. Lesen musst auch Du.
Kann man ja ganz einfach anpassen
_AplitudeImg = decltype(_AplitudeImg)(data, data + 4000);
-
Arcoth schrieb:
Du kannst dieselbe Performance erreichen, wenn du den
vector
zuvor auf 4000 resized und dannstd::copy
verwendest,Nicht ganz, da muss ja erstmal alles mit 0 vollgeschrieben werden und danach kommt erst das Kopieren. memcpy auf un-initialisierten Speicher ist schneller. Und dass der Compiler erkennt dass das unnötig ist und es wegoptimieren kann, davon würde ich nicht ausgehen.
Arcoth schrieb:
oder wahrscheinlich auch wenn du den Iterator-paar Konstruktor einsetzt (da wird intern wahrscheinlich das gleiche, optimierte Funktionstemplate aufgerufen).
Genau. Oder halt
.assign(begin, end)
wenn man den vector schon hat.Ich weiss nicht wie gut es die aktuellen Libraries/Compiler wirklich optimieren können, aber das ist mMn. die Variante die am besten optimiert werden sollte. Die Library könnte hier sehr einfach ne Spezialisierung für memcpy-kompatible Typen haben und dann wirklich nur mehr sinngemäss malloc + memcpy machen, ohne unnötige Zwischenschritte.
-
@hustbaer: muss ich den vector mit reserve vorher allozieren umd danach mit assign bzw nem constructor mit der iterator range zu kopieren oder geht das auch mit nem defaukt-initialized vector?
und auf der ersten Seite: Sind die drei von mir aufgeführten Möglichkeiten so korrekt? und wenn ja, sind die gleich performant?
-
Sewing schrieb:
warum resize und nicht reserve? Ich kopiere doch sowieso andere Werte unmittelbar danach in den Container
reserve
+push_back
ist nicht optimal, weil der Compiler vermutlich nicht schlau genug ist dabei den "ist nocht genug Platz" Check inpush_back
wegzuoptimieren. Dazu müsste er sehen können dass auf Grund desreserve
davor der Test nie negativ ausgehen kann, und das ist was was ich als extrem schwer für den Compiler einschätzen würde. So schwer dass ich schätzen würde dass kein aktueller Compiler es schafft. Verglichen mit 1xmemcpy
für alle 4000 Elemente wird das dadurch ordentlich viel langsamer werden. Wenn man komplexere Elemente einfügt ist dann allerdings schnell der Punkt erreicht wo der prozentuelle Unterschied sehr klein wird.Was du machen kannst ist
v.reserve(elementCount)
+v.insert(v.begin(), elementsBegin, elementsEnd)
. Bzw. ebenv.reserve(elementCount)
+v.assign(elementsBegin, elementsEnd)
wenn der vector davor sowieso leer ist. Dasreserve
davor sollte nicht nötig sein, aber ich vermute dass es laut Standard nicht vorgeschrieben ist dass vector das selbst macht, und wenn du sicher gehen willst kannst du es explizit hinschreiben.
-
hustbaer schrieb:
Arcoth schrieb:
Du kannst dieselbe Performance erreichen, wenn du den
vector
zuvor auf 4000 resized und dannstd::copy
verwendest,Nicht ganz, da muss ja erstmal alles mit 0 vollgeschrieben werden und danach kommt erst das Kopieren. memcpy auf un-initialisierten Speicher ist schneller. Und dass der Compiler erkennt dass das unnötig ist und es wegoptimieren kann, davon würde ich nicht ausgehen.
Man kann sich nicht darauf verlassen, aber mindestens eine Implementierung ist ausgeklügelt genug:
auto f(int* arr) { std::vector<int> v(1000); std::copy_n(arr, 1000, v.data()); return v; }
GCC Godbolt: https://godbolt.org/g/vgDA9d
Clang 4.0 mit -O2:f(int*): # @f(int*) push r14 push rbx push rax mov r14, rsi mov rbx, rdi xorps xmm0, xmm0 movups xmmword ptr [rbx], xmm0 mov qword ptr [rbx + 16], 0 mov edi, 4000 call operator new(unsigned long) mov qword ptr [rbx], rax lea rcx, [rax + 4000] mov qword ptr [rbx + 16], rcx mov qword ptr [rbx + 8], rcx mov edx, 4000 mov rdi, rax mov rsi, r14 call memcpy mov rax, rbx add rsp, 8 pop rbx pop r14 ret
GCC rafft es leider nicht (siehe das verlinkte Fenster, -O3 zeigt immer noch
rep stosq
zum Ausnullen). Selbes gilt für ICC. Bei VC++ erkenne ich nichts mehr, der Assembler ist irgendwie ungekürzt.