C-Array vs std::vector
-
Du hast den Optimizer nicht eingeschaltet.
-
Hallo Shisha,
Ich hielt deine Messergebnisse für höchst unglaubwürdig, daher habe ich den Code selbst nochmals nachgemessen.
Ergebnisse mit MinGW-w64 GCC 7.1.0 (
-O3 -march=native -DNDEBUG
):Run on (12 X 3400 MHz CPU s) 06/21/17 15:55:42 --------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------- ShishaMalloc/1 2 ns 2 ns 373929227 486.367M items/s ShishaMalloc/4 16 ns 16 ns 40792279 949.995M items/s ShishaMalloc/16 230 ns 229 ns 2991434 1063.99M items/s ShishaMalloc/64 3944 ns 3887 ns 172583 1004.99M items/s ShishaMalloc/256 96620 ns 98035 ns 7479 637.527M items/s ShishaMalloc/1024 5457031 ns 5432178 ns 112 184.088M items/s ShishaMalloc/4096 146390461 ns 148200950 ns 4 107.962M items/s ShishaMalloc_BigO 8.71 N^2 8.82 N^2 ShishaMalloc_RMS 6 % 7 % ShishaVector/1 2 ns 2 ns 320510766 425.949M items/s ShishaVector/4 23 ns 23 ns 29914338 664.997M items/s ShishaVector/16 350 ns 352 ns 1950935 693.909M items/s ShishaVector/64 6151 ns 6119 ns 112179 638.398M items/s ShishaVector/256 137313 ns 137666 ns 4986 453.996M items/s ShishaVector/1024 7314861 ns 7453381 ns 90 134.167M items/s ShishaVector/4096 159680211 ns 159901025 ns 4 100.062M items/s ShishaVector_BigO 9.51 N^2 9.52 N^2 ShishaVector_RMS 4 % 4 %
Der Unterschied ist zwar deutlich kleiner als der, den du zu messen gemeint hattest, besteht aber nichtsdestoweniger.
LG
-
Der Vergleich ist ja auch nicht korrekt. Beim C-Beispiel machst du nirgends ein vergleichbares Resize zu Vector? (Bin kein C-Kenner)
Sieht für mich so aus, als ob du nur immer Speicher neu anlegst? Oder wo machst du ein realloc?Bei der C++-Variante machst du:
Vector anlegen (ähnlich einem malloc), und ein resize auf den Vector, was einem realloc und evtl. umkopieren der initialen Vector-Daten in das Resized-Vector enstpricht.Mach doch mal beides gleich. Entweder die C-Variante an die C++-Variante anpassen. Oder mal bei Vector gleich beim Konstruieren die gewünschte initiale Größe angeben.
-
OK:
Rechner : Intel i7-4770
Compiler: g++ 5.4CXXFLAGS: -pthread -llikwid -fopenmp -std=c++11
Wenn man die Optimierung noch anschaltet (-O2)
sieht die Sache tatsächlich schon viel besser aus:Gesamtlaufzeiten (also nicht nur Berechnung von c):
C-Array : 2.031s
vector (Keine Optimierung) : 4.938s
vector (O2) : 2.278s
-
@Artchi: Gemessen wird das zwischen
LIKWID_MARKER_START
undLIKWID_MARKER_STOP
, die Speicherreservierung etc. geschieht jedoch zuvor ininit()
und wird somit nicht mitgemessen. Ferner liegst du mit der Aussage, dass bei seinenresize
s umkopiert wird, falsch, denn erresize
d lediglich leere Vektoren. Ergo wird da gar nichts umkopiert.Die beiden Codes bilden m.E.n. einen fairen Vergleich.
-
Bei den gemessenen Zeiten im ersten Beitrag hadelte es sich nur um die Zeit, die für den Code-Teil zwischen dem LIKWID-Marker benötigt wurde.
Im neuen Beitrag sind Gesamtlaufzeiten zu sehen, da spielen dann solche Dinge schon eine Rolle, aber der Laufzeitunterschied ist nun schon so gering, dass ich das Problem für gelöst erachte.
-
Mich würde mal ein Vergleich mit Rust interessieren.
-
shisha schrieb:
Wenn man die Optimierung noch anschaltet (-O2)
Jeder Benchmark ohne Optimierung ist irrelevant!
Benutze -O3.
-
Wo und wie wird das Messergebnis ausgegeben?
Wie wird dieses likwid benutzt, um damit etwas zu messen? Ich habe keine Lust, 10000 man-Pages zu durchsuchen, nur um rauszufinden, wie dieses Framework einen einzelnen Benchmark starten kann.Wozu überhaupt der Aufwand? <chrono> tut es auch.
Wenn ich raten müsste, würde ich sagen, dass in
for(int i = 0; i < N; i++) { for(int j= 0; j <N; j++) { c[i][j] = a[i][j] + b[j][i]; } }
der Prozessor im Prinzip die ganze Zeit auf den Speicher wartet. Insbesondere weil b mit vertauschten Indizes benutzt wird, muss dort ständig auf das erste Level gewartet werden, bevor die endgültige Adresse bekannt ist.
Und schließlich besteht ein vector-Objekt typischerweise aus 3 Zeiger, der Durchlauf durch b führt damit zu deutlich mehr Druck im L1-Cache - 3*8*10000=240000; das sind ja nicht zu vernachlässigende Größenordnungen.
Das Grundproblem ist nat. dass weder double** noch vector<vector> geeignet sind, wenn man NxM-Felder behandlen möchte.Diese Interpretation passt auch zu Fytchs Messergebnissen. Am oberen und unteren Ende sind beide Varianten ja etwa gleich schnell.
-
camper schrieb:
Das Grundproblem ist nat. dass weder double** noch vector<vector> geeignet sind, wenn man NxM-Felder behandlen möchte.
Nochmal zitiert, weil es die wichtigste Aussage ist.
Ein Zeiger ist kein Array, ein Array ist kein Zeiger. Ein Vector ist kein Array, ein Array ist kein Vector. Demnach ist ein Zeiger auf einen Zeiger ganz bestimmt kein Zeiger auf ein Array; und ein Vector von von Vectoren ist kein Vector von Arrays.