使用 AVX 或更高版本编译我的 DLL 的 32 位版本有什么真正的好处吗?
Are there any real benefits to compiling a 32-bit version of my DLL with AVX or higher?
我有一个遗留 Windows DLL(用 c++ 编写),我需要为它维护 32 位版本和 64 位版本。我正在使用 Agner 的向量 class 库使用 simd 更新繁重的数学代码,与 SSE4.2 相比,使用 AVX 编译时,32 位版本的速度几乎没有或没有提高。我知道使用 32 位代码总是只有 8 个向量寄存器可用,但我不清楚(经过大量搜索)这在使用 AVX、AVX2 或 AVX512 进行编译时究竟意味着什么。是否有编译器选项(Microsoft 或 Clang)可以让我比 SSE4.2(对于简单的浮点运算循环)有一些有价值的速度改进,或者我应该为自己省去一些麻烦并使用 SSE4.2 编译 32 位版本?
我自己在回答这个问题,尽管可以说这个问题应该被删除......也许它会在某个时候帮助某人。
当我完成我的 simd 代码(对齐内存有很大的不同)并摆弄 MSVC 编译器选项时,我的 32 位编译开始完全按预期运行,当没有 simd 与 SSE4.2 进行比较时、AVX 和 AVX512。对下面的示例代码进行基准测试显示,对于 32 位,SSE4.2、AVX、AVX512 的速度提升率分别为 48%、22% 和 10%。
奇怪的是,对于没有 simd 的 64 位编译运行速度要快得多,但对于所有三个 simd 选项(新问题的好主题)来说比 32 位编译运行速度稍慢。
我编译代码时没有使用 /Qpar 开关和 /Qvec-report:2 /Qpar-report:2 以尽可能验证没有自动矢量化或自动并行化。
int Simd_debug(int idebug_branch, int iters, int asize)
{
int j, k, iret = -3;
double u, d;
double* TR = 0;
double* UP = 0;
double* DN = 0;
char* TR_unaligned = 0;
char* UP_unaligned = 0;
char* DN_unaligned = 0;
const int vectorsize = SIMD_SIZE_SPN; //8, 4, 2 = AVX512, AVX, SSE size
#if SIMD_SIZE_SPN == 8
Vec8d vec_up, vec_dn, vec_tree;
#elif SIMD_SIZE_SPN == 4
Vec4d vec_up, vec_dn, vec_tree;
#else
Vec2d vec_up, vec_dn, vec_tree;
#endif
const bool go_align_mem = true;
bool go_simd = (idebug_branch != 2);
bool go_intrinsic = (idebug_branch == 1);
int alignby = sizeof(double) * vectorsize;
int datasize = asize;
int arraysize = (datasize + vectorsize - 1) & (-vectorsize);
int regularpart = arraysize & (-vectorsize);
if (go_simd)
{
if (go_align_mem)
{
TR_unaligned = new char[arraysize * sizeof(double) + alignby];
char* TR_aligned = (char*)(((size_t)TR_unaligned + alignby - 1) & (-alignby));
TR = (double*)TR_aligned;
UP_unaligned = new char[arraysize * sizeof(double) + alignby];
char* UP_aligned = (char*)(((size_t)UP_unaligned + alignby - 1) & (-alignby));
UP = (double*)UP_aligned;
DN_unaligned = new char[arraysize * sizeof(double) + alignby];
char* DN_aligned = (char*)(((size_t)DN_unaligned + alignby - 1) & (-alignby));
DN = (double*)DN_aligned;
//debug check alignment
if ((((uintptr_t)TR & (alignby - 1)) != 0) || (((uintptr_t)UP & (alignby - 1)) != 0) || (((uintptr_t)DN & (alignby - 1)) != 0))
{
iret = -703;
goto bail_out;
}
}
else
{
TR = new double[arraysize];
UP = new double[arraysize];
DN = new double[arraysize];
}//if (go_align_mem)
}
else
{
TR = new double[arraysize];
UP = new double[arraysize];
DN = new double[arraysize];
}//if (go_simd)
u = 1.01;
d = 0.99;
UP[0] = u;
DN[0] = d;
for (k = 1; k < arraysize; k++)
{
UP[k] = u * UP[k - 1];
DN[k] = d * DN[k - 1];
}
for (j = 0; j < iters; j++)
{
if (go_simd)
{
for (k = 0; k < regularpart; k += vectorsize)
{
vec_up.load(UP + k);
vec_dn.load(DN + k);
vec_tree = vec_up * vec_dn;
vec_tree.store(TR + k);
}
}
else
{
#pragma loop(no_vector) //don't need this, according to /Qvec-report:2 ...
for (k = 0; k < arraysize; k++)
{
TR[k] = UP[k] * DN[k];
}
}//if (go_simd)
}
iret = 10000 * idebug_branch + arraysize;
bail_out:
if (go_simd && go_align_mem)
{
delete[] TR_unaligned;
delete[] UP_unaligned;
delete[] DN_unaligned;
}
else
{
delete[] TR;
delete[] UP;
delete[] DN;
}
return iret;
}
我有一个遗留 Windows DLL(用 c++ 编写),我需要为它维护 32 位版本和 64 位版本。我正在使用 Agner 的向量 class 库使用 simd 更新繁重的数学代码,与 SSE4.2 相比,使用 AVX 编译时,32 位版本的速度几乎没有或没有提高。我知道使用 32 位代码总是只有 8 个向量寄存器可用,但我不清楚(经过大量搜索)这在使用 AVX、AVX2 或 AVX512 进行编译时究竟意味着什么。是否有编译器选项(Microsoft 或 Clang)可以让我比 SSE4.2(对于简单的浮点运算循环)有一些有价值的速度改进,或者我应该为自己省去一些麻烦并使用 SSE4.2 编译 32 位版本?
我自己在回答这个问题,尽管可以说这个问题应该被删除......也许它会在某个时候帮助某人。
当我完成我的 simd 代码(对齐内存有很大的不同)并摆弄 MSVC 编译器选项时,我的 32 位编译开始完全按预期运行,当没有 simd 与 SSE4.2 进行比较时、AVX 和 AVX512。对下面的示例代码进行基准测试显示,对于 32 位,SSE4.2、AVX、AVX512 的速度提升率分别为 48%、22% 和 10%。
奇怪的是,对于没有 simd 的 64 位编译运行速度要快得多,但对于所有三个 simd 选项(新问题的好主题)来说比 32 位编译运行速度稍慢。
我编译代码时没有使用 /Qpar 开关和 /Qvec-report:2 /Qpar-report:2 以尽可能验证没有自动矢量化或自动并行化。
int Simd_debug(int idebug_branch, int iters, int asize)
{
int j, k, iret = -3;
double u, d;
double* TR = 0;
double* UP = 0;
double* DN = 0;
char* TR_unaligned = 0;
char* UP_unaligned = 0;
char* DN_unaligned = 0;
const int vectorsize = SIMD_SIZE_SPN; //8, 4, 2 = AVX512, AVX, SSE size
#if SIMD_SIZE_SPN == 8
Vec8d vec_up, vec_dn, vec_tree;
#elif SIMD_SIZE_SPN == 4
Vec4d vec_up, vec_dn, vec_tree;
#else
Vec2d vec_up, vec_dn, vec_tree;
#endif
const bool go_align_mem = true;
bool go_simd = (idebug_branch != 2);
bool go_intrinsic = (idebug_branch == 1);
int alignby = sizeof(double) * vectorsize;
int datasize = asize;
int arraysize = (datasize + vectorsize - 1) & (-vectorsize);
int regularpart = arraysize & (-vectorsize);
if (go_simd)
{
if (go_align_mem)
{
TR_unaligned = new char[arraysize * sizeof(double) + alignby];
char* TR_aligned = (char*)(((size_t)TR_unaligned + alignby - 1) & (-alignby));
TR = (double*)TR_aligned;
UP_unaligned = new char[arraysize * sizeof(double) + alignby];
char* UP_aligned = (char*)(((size_t)UP_unaligned + alignby - 1) & (-alignby));
UP = (double*)UP_aligned;
DN_unaligned = new char[arraysize * sizeof(double) + alignby];
char* DN_aligned = (char*)(((size_t)DN_unaligned + alignby - 1) & (-alignby));
DN = (double*)DN_aligned;
//debug check alignment
if ((((uintptr_t)TR & (alignby - 1)) != 0) || (((uintptr_t)UP & (alignby - 1)) != 0) || (((uintptr_t)DN & (alignby - 1)) != 0))
{
iret = -703;
goto bail_out;
}
}
else
{
TR = new double[arraysize];
UP = new double[arraysize];
DN = new double[arraysize];
}//if (go_align_mem)
}
else
{
TR = new double[arraysize];
UP = new double[arraysize];
DN = new double[arraysize];
}//if (go_simd)
u = 1.01;
d = 0.99;
UP[0] = u;
DN[0] = d;
for (k = 1; k < arraysize; k++)
{
UP[k] = u * UP[k - 1];
DN[k] = d * DN[k - 1];
}
for (j = 0; j < iters; j++)
{
if (go_simd)
{
for (k = 0; k < regularpart; k += vectorsize)
{
vec_up.load(UP + k);
vec_dn.load(DN + k);
vec_tree = vec_up * vec_dn;
vec_tree.store(TR + k);
}
}
else
{
#pragma loop(no_vector) //don't need this, according to /Qvec-report:2 ...
for (k = 0; k < arraysize; k++)
{
TR[k] = UP[k] * DN[k];
}
}//if (go_simd)
}
iret = 10000 * idebug_branch + arraysize;
bail_out:
if (go_simd && go_align_mem)
{
delete[] TR_unaligned;
delete[] UP_unaligned;
delete[] DN_unaligned;
}
else
{
delete[] TR;
delete[] UP;
delete[] DN;
}
return iret;
}