如果不是ICC BUG太多,ICC生成的代码是速度最快的
今天无聊做了个实验,想看看各种编译器产生的代码效率,结果不出乎所料,在Windows下,GCC和VC差不多,ICC快得多。
ICC虽然很好很强大,但是BUG太多,许多程序根本无法编译,直接就给出“编译器内部错误”,想编译来加速PCSX2的夙愿一直未能实现,Intel要加油啊!
之前一直怀疑GCC在Windows下应该是比VC慢的,结果比较下来区别不大。
这段代码融合了随机数生成,浮点运算,字符串输出,字符串拷贝,内存分配、释放、拷贝等常见的编程操作。实际上最费时的是内存拷贝,因为要拷贝所有运算结果。
代码如下:
- C/C++ code
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <math.h>#define LOOP 20#define LOOP_COUNT 1000#define FLOAT_COUNT 100#define NUM_STR_BUFFER_SIZE 256/* function table */typedef struct _ftable{ int func_id; char name[10]; double (*func)(double);}function_table;function_table func_tb[] = { {0, " sin", sin}, {1, " cos", cos}, {2, " tan", tan}, {3, " log", log}, {4, "log10", log10}, {5, " sqrt", sqrt}, {6, " exp", exp}};int main(int argc, char **argv){ int i = 0, j = 0, k = 0, rand_num = 0, cal_num = 0; double result = 0.0, cost = 0.0; char *str = NULL, *temp_str = NULL, str_line[NUM_STR_BUFFER_SIZE]; clock_t start, finish; printf("calculate %d loops...\n", LOOP); start = clock(); srand(clock()); str = (char*)malloc(sizeof(char) * NUM_STR_BUFFER_SIZE); *str = '\0'; /* * 这段循环总共LOOP次,每次要做: * 1. 进行LOOP_COUNT次浮点数运算,取两次随机数,一次用于选函数,一次用于计算 * 2. 拷贝一次运算结果,字符串拷贝 strncat * 3. 搜索字符 * 4. 拷贝全部运算结果,内存拷贝 memcpy * 5. 释放内存 */ for (j = 0; j < LOOP; ++j){ for (i = 0; i != LOOP_COUNT; ++i){ /* 取两次随机数 */ rand_num = rand() % 6; cal_num = rand(); /* 随机选出计算函数,并进行一次浮点函数 */ result = func_tb[rand_num].func(cal_num); /* 一次字符串输出 */ sprintf(str_line, "%6d : %s(%6d) = %4.10f\n", i + 1, func_tb[rand_num].name, cal_num, result); /* 分配内存用于拷贝结果 */ temp_str = str; str = (char*)malloc(sizeof(char) * (strlen(str_line) + strlen(temp_str) + 1)); /* 进行内存拷贝,拷贝之前的全部结果 */ memcpy(str, temp_str, strlen(temp_str) + 1); /* 拷贝当前运算结果 */ strncat(str, str_line, strlen(str_line)); /* 释放内存 */ free(temp_str); } printf("loop %2d of %2d is done.\n", j + 1, LOOP); } free(str); finish = clock(); cost = (finish - start) / 1000.0; printf("loop %d cost %.3f second.", LOOP, cost); return 0;}结果如下:
每次运行取第二次运行的结果。
- C/C++ code
编译器 版本 编译参数 运行时间 备注cl 16.0 无 6.703cl 16.0 /O2 /arch:SSE2 12.577 最常用的优化选项,得到了最慢的代码 icl 12.1 无 2.744icl 12.1 /fast 2.934 同样,最“恐怖”的优化参数,稍慢一些GCC 4.5.3 无 6.655GCC 4.5.3 -O2 -march=native 6.688 优化也没有效果
这段代码能优化的部分可能真的很少,各编译器的优化几乎都没有效果,甚至还有反效果。
结果显而易见了,GCC和VC差不多,ICC显然得到了比较“恐怖”的提升,负面效果也是有的,icl的代码尺寸也很大,纯粹以空间换时间。GCC的代码尺寸比VC稍大一些。
做这个比较,并非为了引起编译器之争,只是觉得,vc的编译器不能识别不带BOM的UTF-8中的字符,而GCC刚好相反,在进行Qt开发时,vc的编译器让我很难选择文件编码,一个小小的源文件编码问题,导致了很多移植问题。不过现在好了,既然GCC和VC差不多,我们就不必选择太肥胖的Visual Studio了。
[解决办法]
都不是同等比较
icl默认fastmath(无论什么参数)
至少比较时把fastmath禁用,而且该开关容易导致错误的结果,尤以3-4月份intel编译器事故最为严重
此外,icl默认循环展开,其他都没有
/fast又默认了全局优化(IPO)和内联,其他默认也没有,当然对单一源码没有什么影响
所以GCC应该用
-O2 (-O3) -march=native -finline-functions -funroll-loops -ffast-math -malign-double -fomit-frame-pointer -minline-all-stringops -ftree-parallelize-loops=n(n为你的cpu核心数,双核n=2)
4.5不要使用LTO作用单一源码
4.6.1之后应该用
-O2 (-O3) -march=native -funroll-loops -ffast-math -malign-double -fomit-frame-pointer -minline-all-stringops -ftree-parallelize-loops=n(n为你的cpu核心数,双核n=2) -flto -fuse-linker-plugin