上一篇文章是測試MinGW64 gcc編譯的效果.
這篇則是要來試試看Vistual C++ 2013的效果.
據Stack Overflow上面文章是說,
VC++ 2010即使是加/Ox, 也是無法產生conditional move(CMOV)指令.
但是我在Stack Overflow上有看到一篇文章,
是說VS2012 C++產生的conditional move有問題,
雖然只是一場誤會. 但還是有產生CMOV的指令.
那我就好奇了,那Vistual C++ 2013會不會變聰明呢?
一樣是用上一篇的C版本來編譯執行,
一開始還是只是用/O2,
32bits執行檔的執行結果是:
=== test1 ===
10.500000 sum = 312275400000
After Sorted...
=== test1 ===
2.893000 sum = 312275400000
64bits執行檔的執行結果是:
=== test1 ===
13.217000 sum = 312275400000
After Sorted...
=== test1 ===
3.542000 sum = 312275400000
看起來兩者差距不大,
而且還是32bits快了一點.
那直接開/Ox來編譯呢?
結果如下:
=== test1 ===
13.218000 sum = 312275400000
After Sorted...
=== test1 ===
3.540000 sum = 312275400000
不管是64bits或是32bits真的是沒差~~~
可是VS2013 C++應該是有支援CMOV指令,
可能是因為寫法的關係,
編譯器不認為需要用CMOV指令,
那我來改個寫法試試看.
=======================
#include <stdio.h> #include <stdlib.h> #include <time.h> void test1(int *data, int arraySize, unsigned int num) { clock_t start; long long sum = 0; int v = 0; double elapsedTime; unsigned int i; int c; start = clock(); for (i = 0; i < num; ++i) { // Primary loop for (c = 0; c < arraySize; ++c) { if (data[c] >= 128) { sum += data[c]; } } } elapsedTime = (double) (clock() - start) / CLOCKS_PER_SEC; printf("=== %s ===\n", __FUNCTION__); printf("%lf\t", elapsedTime); printf("sum = %lld\n", sum); } void test2(int *data, int arraySize, unsigned int num) { clock_t start; long long sum = 0; int v = 0; double elapsedTime; unsigned int i; int c; start = clock(); for (i = 0; i < num; ++i) { // Primary loop for (c = 0; c < arraySize; ++c) { v = 0; if (data[c] >= 128) { v = data[c]; } sum += v; } } elapsedTime = (double) (clock() - start) / CLOCKS_PER_SEC; printf("=== %s ===\n", __FUNCTION__); printf("%lf\t", elapsedTime); printf("sum = %lld\n", sum); } #define arraySize 32768 int main() { int data[arraySize]; int i; for (i = 0; i < arraySize; ++i) data[i] = rand() % 256; test1(data, arraySize, 100000); test2(data, arraySize, 100000); test1(data, arraySize, 100000); test2(data, arraySize, 100000); return 0; }
=======================
function test1 就是原本的code,
if (data[c] >= 128) { sum += data[c]; }
而 function test2 則是改了寫法.
v = 0; if (data[c] >= 128) { v = data[c]; } sum += v;
因為知道CMOV是用來執行某個條件才搬移資料,
所以故意寫成這樣,
乍看之下, 應該會覺得test2() 會變慢,
因為會比test1()多做搬移的動作,也會多了一些加法運算.
不過在VS2013用/O2編譯,
執行結果如下:
=== test1 ===
13.212000 sum = 312275400000
=== test2 ===
3.134000 sum = 312275400000
=== test1 ===
13.215000 sum = 312275400000
=== test2 ===
3.132000 sum = 312275400000
用test2的方法比test1快多了.
差不多就是用gcc -O3的速度.
開disassembly視窗來看,
000000013FAE1120 33 D2 xor edx,edx
000000013FAE1122 41 81 38 80 00 00 00 cmp dword ptr [r8],80h
000000013FAE1129 41 0F 4D 10 cmovge edx,dword ptr [r8]
的確用了CMOV指令.
所以VS2013 C++是會用CMOV指令,
只是前提是你的程式碼要寫的讓編譯器認得,
它才會使用CMOV指令.
以後看到這種有點笨笨的寫法,
別以為別人程式寫的爛,
說不定他是為了讓編譯器認得~~~
後來又開了 /Ox 和 /arch:SSE2 也都沒什麼差別.
因為我的CPU是AMD,也沒辦法試其他的指令集.
那麼回到MinGW64 gcc呢?
還是用-O3去編譯, 跑出來的結果如下:
$ ./a.exe
=== test1 ===
3.997000 sum = 312275400000
=== test2 ===
1.298000 sum = 312275400000
=== test1 ===
3.993000 sum = 312275400000
=== test2 ===
1.299000 sum = 312275400000
什麼? 那用 -O2呢?
=== test1 ===
13.218000 sum = 312275400000
=== test2 ===
4.112000 sum = 312275400000
=== test1 ===
13.218000 sum = 312275400000
=== test2 ===
4.116000 sum = 312275400000
看樣子gcc也是需要認到某種寫法,才能最佳化.
即使CPU不夠強,
有個好的Compiler和好的寫法,
是有辦法跑出好的效果的. ^__^