C 无法使用SSE进行优化
我正在测试SSE对Zip解密的一种变体。但是,未优化的代码性能更好 使用参数:-msse4-O3运行编译器会产生以下基准测试:- 正常测试:0.275,SSE测试:0.655 我尝试增加循环计数器,但基准没有太大变化。编译器正在进行哪些优化?我们是否应该使用SSE来匹配它 编辑:按照Jens的建议使用了墙时间,增加了循环迭代次数,并固定了printf格式C 无法使用SSE进行优化,c,sse,C,Sse,我正在测试SSE对Zip解密的一种变体。但是,未优化的代码性能更好 使用参数:-msse4-O3运行编译器会产生以下基准测试:- 正常测试:0.275,SSE测试:0.655 我尝试增加循环计数器,但基准没有太大变化。编译器正在进行哪些优化?我们是否应该使用SSE来匹配它 编辑:按照Jens的建议使用了墙时间,增加了循环迭代次数,并固定了printf格式 #include <stdio.h> #include <stdint.h> #include <smmintr
#include <stdio.h>
#include <stdint.h>
#include <smmintrin.h>
#include <time.h>
// Windows
#ifdef _WIN32
#include <Windows.h>
double get_wall_time()
{
LARGE_INTEGER time,freq;
if (!QueryPerformanceFrequency(&freq))
{
// Handle error
return 0;
}
if (!QueryPerformanceCounter(&time))
{
// Handle error
return 0;
}
return (double)time.QuadPart / freq.QuadPart;
}
double get_cpu_time()
{
FILETIME a,b,c,d;
if (GetProcessTimes(GetCurrentProcess(),&a,&b,&c,&d) != 0)
{
// Returns total user time.
// Can be tweaked to include kernel times as well.
return
(double)(d.dwLowDateTime |
((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001;
}
else
{
// Handle error
return 0;
}
}
// Posix/Linux
#else
#include <sys/time.h>
double get_wall_time()
{
struct timeval time;
if (gettimeofday(&time,NULL))
{
// Handle error
return 0;
}
return (double)time.tv_sec + (double)time.tv_usec * .000001;
}
double get_cpu_time()
{
return (double)clock() / CLOCKS_PER_SEC;
}
#endif
static void test_sse()
{
double start = get_wall_time();
uint64_t sum = 0;
uint32_t nk0 = 0x12345678;
uint32_t nk1 = 0x23456789;
uint32_t nk2 = 0x34567890;
uint32_t nk3 = 0x45678901;
__m128i mask = _mm_set1_epi32(0xff);
uint64_t i;
for(i = 0; i < 100000000; i++)
{
uint32_t newKeys[] = {nk0, nk1, nk2, nk3};
__m128i *nk_sse = (__m128i*)(&newKeys);
__m128i opa = _mm_and_si128(*nk_sse, mask);
__m128i opr8 = _mm_srai_epi32 (*nk_sse, 8);
__m128i opr16 = _mm_srai_epi32 (*nk_sse, 16);
__m128i opr24 = _mm_srai_epi32 (*nk_sse, 24);
__m128i oprsum = _mm_add_epi32(_mm_add_epi32(_mm_add_epi32(opa, _mm_and_si128(opr8, mask)), _mm_and_si128(opr16, mask)), _mm_and_si128(opr24, mask));
uint32_t* oprsum_ptr = (uint32_t*)(&oprsum);
uint32_t sum_sse = oprsum_ptr[0] + oprsum_ptr[1] + oprsum_ptr[2] + oprsum_ptr[3];
sum += sum_sse;
nk0--;
nk1--;
nk2--;
nk3--;
}
double end = get_wall_time();
double ms = end - start;
printf("SSE Test - Sum: %lu, ms: %f\n", sum, ms);
}
static void test()
{
double start = get_wall_time();
uint64_t sum = 0;
uint32_t nk0 = 0x12345678;
uint32_t nk1 = 0x23456789;
uint32_t nk2 = 0x34567890;
uint32_t nk3 = 0x45678901;
uint64_t i;
for(i = 0; i < 100000000; i++)
{
uint8_t res0 = (uint8_t) (nk0 & 0xff);
uint8_t res1 = (uint8_t) (nk0 >> 8);
uint8_t res2 = (uint8_t) (nk0 >> 16);
uint8_t res3 = (uint8_t) (nk0 >> 24);
uint8_t res4 = (uint8_t) (nk1 & 0xff);
uint8_t res5 = (uint8_t) (nk1 >> 8);
uint8_t res6 = (uint8_t) (nk1 >> 16);
uint8_t res7 = (uint8_t) (nk1 >> 24);
uint8_t res8 = (uint8_t) (nk2 & 0xff);
uint8_t res9 = (uint8_t) (nk2 >> 8);
uint8_t res10 = (uint8_t) (nk2 >> 16);
uint8_t res11 = (uint8_t) (nk2 >> 24);
uint8_t res12 = (uint8_t) (nk3 & 0xff);
uint8_t res13 = (uint8_t) (nk3 >> 8);
uint8_t res14 = (uint8_t) (nk3 >> 16);
uint8_t res15 = (uint8_t) (nk3 >> 24);
sum += res0 + res1+ res2 + res3 + res4 + res5 + res6 + res7 + res8 + res9
+ res10 + res11 + res12 + res13 + res14 + res15;
nk0--;
nk1--;
nk2--;
nk3--;
}
double end = get_wall_time();
double ms = end - start;
printf("Normal Test - Sum: %lu, ms: %f\n", sum, ms);
}
int main (int argc, char **argv)
{
test();
test_sse();
return 0;
}
您使用了错误的工具来衡量绩效。时钟,至少在符合标准的平台上,为您提供CPU时间,而不是挂钟时间 另一件您做错的事情是打印带有%d的uint64\t。当int小于64位时,这具有未定义的行为。此时您的int打印可能只是接收垃圾 编辑:现在您已经修复了代码,我已经将其编译为汇编器gcc选项-S。事实上,gcc在向量化和展开测试函数方面做得非常好。使用我的三个编译器,我得到了完全不同的结果,都是用-O3-march=native编译的 国际商会: 通用条款4.6:
Normal Test - Sum: 169248636030, ms: 0.462793
SSE Test - Sum: 169248636030, ms: 0.348453
叮当声:
Normal Test - Sum: 169248636030, ms: 0.625905
SSE Test - Sum: 169248636030, ms: 0.423343
所以icc和clang在未优化的代码上做的差不多,但是gcc做得更好。使用gcc和clang,您的sse代码更好,而icc则更宽松。总的来说,icc是一个商业编译器,你必须为它支付真正的费用,与公共领域的免费编译器相比,它的性能确实令人失望
编辑2:
现在有了新版本的gcc,我甚至可以
gcc 4.7:
Normal Test - Sum: 169248636030, ms: 0.158695
SSE Test - Sum: 169248636030, ms: 0.406921
所以你可以看到,正常测试的效果更进一步,而
SSE测试比以前差了一点。您使用了错误的工具来衡量性能。时钟,至少在符合标准的平台上,为您提供CPU时间,而不是挂钟时间 另一件您做错的事情是打印带有%d的uint64\t。当int小于64位时,这具有未定义的行为。此时您的int打印可能只是接收垃圾 编辑:现在您已经修复了代码,我已经将其编译为汇编器gcc选项-S。事实上,gcc在向量化和展开测试函数方面做得非常好。使用我的三个编译器,我得到了完全不同的结果,都是用-O3-march=native编译的 国际商会: 通用条款4.6:
Normal Test - Sum: 169248636030, ms: 0.462793
SSE Test - Sum: 169248636030, ms: 0.348453
叮当声:
Normal Test - Sum: 169248636030, ms: 0.625905
SSE Test - Sum: 169248636030, ms: 0.423343
所以icc和clang在未优化的代码上做的差不多,但是gcc做得更好。使用gcc和clang,您的sse代码更好,而icc则更宽松。总的来说,icc是一个商业编译器,你必须为它支付真正的费用,与公共领域的免费编译器相比,它的性能确实令人失望
编辑2:
现在有了新版本的gcc,我甚至可以
gcc 4.7:
Normal Test - Sum: 169248636030, ms: 0.158695
SSE Test - Sum: 169248636030, ms: 0.406921
所以你可以看到,正常测试的效果更进一步,而
SSE测试比以前差了一点。为什么要用-Os编译?我的错,我本来打算用-O3编译的。以下是-O3:-正常-2,SSE-6@Partho顺便说一句,你看过非see测试的反汇编吗?当给出-msse4时,编译器本身可能也会使用SSE指令,至少GCC承诺了类似的东西。为什么用-Os编译?我的错,我想用-O3来编译。以下是-O3:-正常-2,SSE-6@Partho顺便说一句,你看过非see测试的反汇编吗?当给出-msse4时,编译器本身可能也会使用SSE指令,至少GCC承诺了类似的内容。感谢您提供的信息。我用的是墙上的时钟时间解决方案。还修复了printf格式。谢谢你提供的信息。我用的是墙上的时钟时间解决方案。还修复了printf格式。