F
sugnitar schrieb:
Stellt sich die Frage, wieso? Geht es besser? Was läuft im gcc (Version 5.3.0) schief im Vergleich zu clang++ (Version 3.7.1)?
Der Code von Finnegan braucht bei mir 17.61s, sehr wahrscheinlich benutze ich ihn falsch.
Hi! ... ich habe das ich nochmal mit GCC 5.2 und einem 3.8er Clang unter Windows gestestet und auch bei mir erzeugt GCC diesen extremen Ausreisser bei Variante C.
Interessant ist, dass GCC 5.3 im Vergleich zu Clang und seiner 4.9er Reihe die Schleife in main() scheinbar nicht vektorisiert: siehe hier (GCC 5.3) und hier (GCC 4.9) (xmm-Register vs Standardregister).
Ob das alleine der Grund für die extremen Laufzeitunterschiede ist, kann ich auf die Schnelle nicht sagen. Immerhin scheint signumC() ordentlich ge-inlined worden zu sein.
Was meine vorgeschlagene signum4() -Funtion angeht, so könnte der Grund für deren schlechte Laufzeit sein, dass sie mit _mm_store_si128() eine explizite Anweisung enthält,
das Ergebnis der Berechnung in den Speicher zu zurückzuschreiben, während der Compiler bei den anderen Varianten die Möglichkeit hat, die gesamte Berechnung sowie das
abschließende XOR in Registern auszuführen. Da die Speicherzugriffe wahrscheinlich ohnehin einen sehr grossen Anteil an der gemessenen Laufzeit haben werden, ist es nicht
verwunderlich, dass signum4() in diesem Fall schlechter abschneidet.
Ich habe den Testcode mal auf die Schnelle modifiziert, so dass auch die anderen Funktionen das Ergebnis in den Speicher schreiben und signum4() mit in den Test aufgenommen: http://ideone.com/UyZyrF.
(man möge mir das plumpe "malloc" nachsehen, leider benötigen SIMD-Operationen ein spezielles Speicher-Alignment, und das einem std::vector beizubringen hätte mehr Arbeit gemacht und den Code nur unnötig aufgebläht).
Mit dieser Variante erziele ich auf meinem Rechner folgende Ergebnisse:
g++ 5.2.0, clang 3.8.0 (based on LLVM 3.8.0svn)
compiler-flags: -O3 -march=native -std=c++11
[ args: 50000 100000 ] [ args: 100000 1000000 ]
g++ clang g++ clang
A: 1.833s 2.903s 38.267s 57.430s
B: 1.715s 1.503s 37.051s 33.353s
C: 22.447s 1.482s - 29.394s
D: 1.742s 1.491s 37.458s 33.368s
E: 23.319s 23.360s - -
signum4: 1.399s 1.391s 28.274s 28.138s
Obwohl die Compiler anscheinend in der Lage sind, die Schleifen zu Vektorisieren, scheinen die expliziten SIMD-Instruktionen von signum4() hier dennoch minimal besser abzuschneiden
(Allerdings finden hier mit der lesen-schreiben-Kombination auch mehr Speicherzugriffe statt, welche die Performance der reinen Rechnenoperation überschatten dürften).
Ob sich das allerdings lohnt ist eine andere Frage, da solche manuellen SIMD-Implementierungen meistens erst dann glänzen, wenn man viele Operationen auf einem zusammenhängenden
Speicherbereich durchführt. Wenn noch ein bisschen mehr als nur das Signum auf den Daten gemacht werden muss, das man gut vektorisieren kann, würde ich es mir allerdings überlegen.
Gruss,
Finnegan