Warum ist mein Simd Vector Plus und setzt langsamer als die Verwendung von std :: transform und std :: plus <t> - mache
Posted: 02 Mar 2025, 23:49
Neu in Simd Bitte gehen Sie mir leicht, wenn ich Fehler gemacht habe. Mein Prozessor ist der 11. Gen Intel (R) Core (TM) I7-11370H @ 3.30GHz < /p>
Bevor Sie AXV verwenden
Der Skalar -Lauf scheint immer die SIMD -Funktion auszuführen:
. Scalar? < /p>
mache ich etwas falsches? Liegt es daran, dass mein restlicher Handling nicht effizient ist?
Bearbeiten 2
Ich hätte dies wahrscheinlich zuerst tun sollen, was die Kommentare vorgeschlagen haben, die ich mir den ASM angesehen habe, der von meinem Compiler generiert wurde. Für den einfachen STD :: Transformationstest wird es Vektorisierungen /SIMD -Simd vom Compiler mit /O2 -Flag erzeugt. Obwohl ich weiß, dass mein PC AVX unterstützen sollte, nicht sicher, warum es die kleineren SSE -Register verwendet?
Bearbeiten 3
Die Kommentare von @Petercordes haben sehr geholfen, und wie er vorgeschlagen hat, wird im MSVC generierten Code eine Schleife abrollt! Ich werde versuchen, dies für meine AVX -Implementierung zu implementieren und zu sehen, ob ich den Unterschied sehen kann!
Bevor Sie AXV verwenden
Code: Select all
bool supportsAVX()
{
int cpuInfo[4] = { 0 };
__cpuid(cpuInfo, 1);
return (cpuInfo[2] & (1
Bevor die SIMD -Add -Funktion für Vektor implementiert wurde, habe ich sichergestellt, dass der Vektorpuffer 32 Byte mithilfe der Implemenation hier ausgerichtet ist (Implementierung eines Allocators): Moderner Ansatz zur Erstellung von STD :: Vektor -Zuordnungen ausgerichtetem Speicher. Der Demo -Code aus dieser Antwort ist hier: https://godbolt.org/z/pg5ph7936
Also habe ich einen 32 -Byte -Vektor für AVX wie unten definiert.
template
using Aligned32Vector = std::vector;
< /code>
Dies ist meine SIMD -Funktion hinzufügen: < /p>
#include "aligned_vector.hpp"
#include // For SSE/AVX intrinsics
void simd_add_sd_float_avx(const simd_util::Aligned32Vector& a, const simd_util::Aligned32Vector& b, simd_util::Aligned32Vector& c)
{
size_t const total_size = a.size();
constexpr size_t working_width = 32 / sizeof(double);
size_t i = 0;
// AVX SIMD loop
for (; i < total_size - working_width; i += working_width)
{ // Process 4 double at a time
// Load
__m256d va = _mm256_load_pd(&a[i]); // Load 4 double
__m256d vb = _mm256_load_pd(&b[i]); // Load 4 double
// Perform SIMD addition
__m256d vsum = _mm256_add_pd(va, vb); // Add 4 double in parallel
// Store the result back into the 'result' array
_mm256_store_pd(&c[i], vsum); // Store 4 double
}
// Handle leftovers
if (i < total_size)
{
size_t remaining = total_size - i;
alignas(32) double mask_data[4] = { 0.0 };
__m256d mask = _mm256_set_pd(
remaining > 3 ? -1.0 : 0.0,
remaining > 2 ? -1.0 : 0.0,
remaining > 1 ? -1.0 : 0.0,
remaining > 0 ? -1.0 : 0.0
);
// over reading and adding, but who cares we good as long as we dont use them
__m256d va = _mm256_loadu_pd(&a[i]);
__m256d vb = _mm256_loadu_pd(&b[i]);
__m256d vr = _mm256_add_pd(va, vb);
__m256d existing = _mm256_setzero_pd(); // Set to zeros
__m256d blended = _mm256_blendv_pd(existing, vr, mask);
// Scalar write-back
alignas(32) double temp[4];
_mm256_storeu_pd(temp, blended);
for (int j = 0; j < remaining; ++j)
{
c[i + j] = temp[j];
}
}
}
< /code>
Dies ist meine einfache Funktion mit addieren mit std :: vector: < /p>
#include
#include
void add_vector_float_normal(const std::vector& a, const std::vector& b, std::vector& c)
{
std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::plus());
}
< /code>
Mein Testfall ist einfach, mach 2 Vektor der Größe 1000000, Wert 1, addiere sie dann einen dritten Vektor derselben Größe und vergleichen ihre Wandzeit mit Chrono, mit einer Iteration von 10000.std::vector a(1000000,1);
std::vector b(1000000,1);
std::vector c(1000000,0);
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i)
{
add_vector_float_normal(a,b,c);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast(end - start);
double average_time_us = static_cast(duration.count()) / iterations;
< /code>
SIMD: < /p>
simd_util::Aligned32Vector a(1000000, 1);
simd_util::Aligned32Vector b(1000000, 1);
simd_util::Aligned32Vector c(1000000, 0);
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i)
{
simd_add_sd_float_avx(a, b, c);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast(end - start);
double average_time_us = static_cast(duration.count()) / iterations;
. Scalar? < /p>
mache ich etwas falsches? Liegt es daran, dass mein restlicher Handling nicht effizient ist?
Code: Select all
/permissive- /ifcOutput "x64\Release\" /GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"x64\Release\vc143.pdb" /Zc:inline /fp:precise /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /std:c17 /Gd /Oi /MD /std:c++20 /FC /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"x64\Release\simd_matrix.pch" /diagnostics:column
< /code>
In meiner vcxproj -Datei ist es: < /p>
Level3
true
true
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
stdcpp20
stdc17
Console
true
true
true
Ich hätte dies wahrscheinlich zuerst tun sollen, was die Kommentare vorgeschlagen haben, die ich mir den ASM angesehen habe, der von meinem Compiler generiert wurde. Für den einfachen STD :: Transformationstest wird es Vektorisierungen /SIMD -Simd vom Compiler mit /O2 -Flag erzeugt. Obwohl ich weiß, dass mein PC AVX unterstützen sollte, nicht sicher, warum es die kleineren SSE -Register verwendet?
Code: Select all
$LL33@add_vector:
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
movups xmm0, XMMWORD PTR [rcx]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
; Line 3763
add rsi, 8
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
movups xmm1, XMMWORD PTR [rax]
addpd xmm1, xmm0
movups xmm0, XMMWORD PTR [rcx+16]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
; Line 3764
movups XMMWORD PTR [rdx], xmm1
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
movups xmm1, XMMWORD PTR [rax+16]
addpd xmm1, xmm0
movups xmm0, XMMWORD PTR [rcx+32]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
; Line 3764
movups XMMWORD PTR [rdx+16], xmm1
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
movups xmm1, XMMWORD PTR [rax+32]
addpd xmm1, xmm0
movups xmm0, XMMWORD PTR [rcx+48]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
Die Kommentare von @Petercordes haben sehr geholfen, und wie er vorgeschlagen hat, wird im MSVC generierten Code eine Schleife abrollt! Ich werde versuchen, dies für meine AVX -Implementierung zu implementieren und zu sehen, ob ich den Unterschied sehen kann!