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
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;
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!
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[code]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
// 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
__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; [/code] 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?[code]/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>
[/code] [b] Bearbeiten 2 [/b] 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]$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 [/code] [b] Bearbeiten 3 [/b] 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!
Für den Moment sind die von std :: datapar :: simd und std :: experimental :: simd bereitgestellten Funktionen etwas begrenzt, insbesondere wenn es um Element-Mischung oder nicht triviale Lastmuster...
Ich verwende Qt 6.5 in einem Projekt und versuche, ein Baummodell zu überarbeiten/verbessern, das ursprünglich in Qt 5.10 geschrieben wurde. Ich habe einige Zeit damit verbracht, mir das...
Ich möchte denselben Template -Algorithmus auf std :: vectors anwenden, die Objekte mit einem Typ t und (unterschiedlich) std :: vector s enthalten, die std :: shared_ptr S -Objekte enthält. Im std...
Mir ist bewusst, dass mit C++11 Alias-Vorlagen eingeführt wurden, die in diesem Thread ausführlich erläutert werden und es einfach machen, die Ausführlichkeit zu reduzieren. Zum Beispiel:
#include...
Hier gibt es einfache (vielleicht naive) Konzepte. Sie sollen Typen abschneiden, die mit einem bestimmten Operator nicht kompatibel sind.
namespace concepts
{
template
concept...