können Sie 2x _mm256_permute2f128_ps
verwenden, um die niedrigen und hohen Fahrspuren für eine vertikale vaddps
aufreihen. Dies ist anstelle von 2x extractf128
/insertf128
. Dies macht auch zwei 128b vaddps xmm
Anweisungen in eine einzige 256b vaddps ymm
.
vperm2f128
ist so schnell wie eine einzige vextractf128
oder vinsertf128
auf Intel-CPUs. Bei AMD ist es allerdings langsam (8 m-Ops mit 4c Latenz bei der Bulldozer-Familie). Trotzdem, nicht so schlimm, dass Sie es vermeiden müssen, selbst wenn Sie sich für AMD interessieren. (Und eine der Permutationen kann tatsächlich eine vinsertf128
sein).
__m256 hsum8(__m256 a, __m256 b, __m256 c, __m256 d,
__m256 e, __m256 f, __m256 g, __m256 h)
{
// a = [ A7 A6 A5 A4 | A3 A2 A1 A0 ]
__m256 sumab = _mm256_hadd_ps(a, b);
__m256 sumcd = _mm256_hadd_ps(c, d);
__m256 sumef = _mm256_hadd_ps(e, f);
__m256 sumgh = _mm256_hadd_ps(g, h);
__m256 sumabcd = _mm256_hadd_ps(sumab, sumcd); // [ D7:4 ... A7:4 | D3:0 ... A3:0 ]
__m256 sumefgh = _mm256_hadd_ps(sumef, sumgh); // [ H7:4 ... E7:4 | H3:0 ... E3:0 ]
__m256 sum_hi = _mm256_permute2f128_ps(sumabcd, sumefgh, 0x31); // [ H7:4 ... E7:4 | D7:4 ... A7:4 ]
__m256 sum_lo = _mm256_permute2f128_ps(sumabcd, sumefgh, 0x20); // [ H3:0 ... E3:0 | D3:0 ... A3:0 ]
__m256 result = _mm256_add_ps(sum_hi, sum_lo);
return result;
}
Diese compiles as you'd expect. Der zweite permute2f128
kompiliert tatsächlich zu einem vinsertf128
, da es nur die niedrige Spur jedes Eingangs in derselben Weise verwendet, die vinsertf128
tut. gcc 4.7 und später tun diese Optimierung, aber nur viel neuere clang-Versionen (v3.7). Wenn Sie sich um alten Klang kümmern, tun Sie dies auf der Quellenebene.
Die Einsparungen in den Quellzeilen sind größer als die Einsparungen in den Anweisungen, denn _mm256_extractf128_ps(sumabcd, 0);
kompiliert zu Null-Anweisungen: es ist nur eine Besetzung. Kein Compiler sollte jemals vextractf128
mit einem imm8 ausgeben, außer 1
. (vmovdqa xmm/m128, xmm
ist immer besser für die untere Spur). Guter Job, Intel verschwendet ein Instruktions-Byte auf Zukunftssicherheit, das Sie nicht verwenden konnten, weil einfache VEX-Präfixe keinen Platz haben, um längere Vektoren zu kodieren.
Die beiden vaddps xmm
Anweisungen können parallel ausgeführt werden, so dass die Verwendung einer einzelnen vaddps ymm
meist nur eine Durchsatz (und Code-Größe) Verstärkung, nicht Latenz ist.
Wir rasieren 3 Zyklen von vollständig eliminiert die endgültige vinsertf128
, obwohl.
vhaddps
3 UOPs, 5c Latenz und eine pro 2c Durchsatz. (6c Latenz auf Skylake). Zwei dieser drei Ups laufen auf dem Shuffle-Port. Ich denke, es macht im Grunde 2x shufps
, Operanden für addps
zu generieren.
Wenn wir haddps
(oder zumindest einen horizontalen Betrieb, den wir verwenden können) mit einem einzigen shufps
/addps
oder so etwas emulieren können, würden wir voraus. Leider sehe ich nicht wie. Ein einzelner Shuffle kann nur ein Ergebnis mit Daten von zwei Vektoren erzeugen, aber wir benötigen beide Eingaben in die vertikale addps
, um Daten von beiden Vektoren zu haben.
Ich glaube nicht, die horizontale Summe auf andere Weise zu tun, sieht vielversprechend aus. Normally, hadd is not a good choice, weil der gemeinsame Anwendungsfall der horizontalen Summe nur ein Element seiner Ausgabe interessiert.Das ist hier nicht der Fall: Jedes Element jedes hadd
Ergebniss wird tatsächlich verwendet.
Ich denke nicht, dass Sie sich dabei stark verbessern können, aber wenn es wirklich performance-kritisch ist, beachten Sie, dass '_mm256_hadd_ps' typischerweise eine Latenz von 5 hat, während' _mm256_add_ps' eine Latenz von 3 hat Sie haben eine Wahl, auch wenn es ein paar Anweisungen hinzufügt. Intels IACA-Tool kann nützlich sein, um die relative Effizienz von kleinen Code-Fragmenten wie dieser zu vergleichen. –
Sie brauchen also einen Vektor von horizontalen Summen von 8 Quellvektoren? Sie * könnten * zuerst transponieren und dann vertikale Summen, aber das ist wahrscheinlich viel langsamer. Schauen Sie sich [meine horizontale Summenantwort] (http://stackoverflow.com/questions/6996764/fastest-way-to-do-horizontal-float-vector-sum-on-x86/35270026#35270026) für Ideen an . Ein Teil der Arbeit mit anderen Insassen als Haddps ist wahrscheinlich gut. OTOH, du nimmst bei jedem Schritt den vollen Vorteil der Merging-Fähigkeit von 'hadd' und nimmst es niemals mit beiden Operanden gleich. 'extract (x, 0)' ist kostenlos, da es sich nur um eine Besetzung handelt. –
Vielleicht können Sie Ihren Algorithmus so überdenken, dass Sie nur vertikalen Operator benötigen –