この前、
型による演算速度の違いという日記を書きました。
しかし、同じマシンでも、OSが違えば実行速度が違う、ということはあるのでしょうか?
では、試してみましょう。
検証環境
プロセッサ: Intel(R) Core(TM)2 Duo CPU T8100 @ 2.10GHz 2.10GHz
メモリ(RAM): 4.00GB
エントリーNo.1 : Windows Vista Home Premium Service Pack 2 / gcc4.7.2
エントリーNo.2 : Ubuntu 10.10 / gcc4.4.5
エントリーNo.3 : MIKO GNYO/LINUX 5.0 / gcc4.6.3
検証1 : 前の日記のコードを改良したコード
► スポイラーを表示
CODE:
#include
#ifdef _WIN32
#include
#else
#include
typedef struct {
long long QuadPart;
} LARGE_INTEGER;
int QueryPerformanceFrequency(LARGE_INTEGER* freq) {
freq->QuadPart=1000000;
return 1;
}
int QueryPerformanceCounter(LARGE_INTEGER* counter) {
struct timeval tv;
gettimeofday(&tv,NULL);
counter->QuadPart=(long long)tv.tv_sec*1000000+(long long)tv.tv_usec;
return 1;
}
#endif
#define ITERATION_NUM 1000000000
int main(void) {
LARGE_INTEGER start,end;
LARGE_INTEGER freq;
LARGE_INTEGER result;
int i;
int test_int=0;
short test_short=0;
char test_char=0;
long long test_long_long=0;
double test_double=0;
float test_float=0;
long double test_long_double=0;
if(!QueryPerformanceFrequency(&freq)) {
puts("パフォーマンスカウンタに非対応です。");
return 1;
}
printf("パフォーマンスカウンタ周波数 : %lldHz\n",(long long)freq.QuadPart);
printf(" ループ回数 : %d回\n",ITERATION_NUM);
printf(" 空転 : ");
QueryPerformanceCounter(&start);
for(i=0;i
#include
#include
#define MOD_BY 1000000009ULL
typedef unsigned long long ull;
typedef struct {
int x;
ull y;
} xy_t;
ull matrix[64][75][75];
void matrix_mul(ull in1[75][75],ull in2[75][75],ull out[75][75],int n) {
ull result[75][75];
int i,j,k;
for(i=0;i0)matrix[0][i][i-1]=1;
matrix[0][i][i]=1;
if(iy)>(b->y))return 1;
if((a->y)y))return -1;
if((a->x)>(b->x))return 1;
if((a->x)x))return -1;
return 0;
}
int main(void) {
int case_num;
int w,n;
ull h;
int i;
ull now[75];
ull now_pos;
xy_t noentry[30];
ull now_matrix[75][75];
for(case_num=1;;case_num++) {
scanf("%d%llu%d",&w,&h,&n);
if(w==0 && h==0 && n==0)break;
for(i=0;inow_pos) {
memset(now_matrix,0,sizeof(now_matrix));
matrix_kaizyo(now_matrix,w,noentry[i].y-now_pos);
matvec_mul(now_matrix,now,now,w);
now_pos=noentry[i].y;
}
now[noentry[i].x-1]=0;
}
if(now_pos
#ifdef _WIN32
#include
#else
#include
typedef struct {
long long QuadPart;
} LARGE_INTEGER;
int QueryPerformanceFrequency(LARGE_INTEGER* freq) {
freq->QuadPart=1000000;
return 1;
}
int QueryPerformanceCounter(LARGE_INTEGER* counter) {
struct timeval tv;
gettimeofday(&tv,NULL);
counter->QuadPart=(long long)tv.tv_sec*1000000+(long long)tv.tv_usec;
return 1;
}
#endif
#define ITERATION_NUM 1000000000
int main(void) {
LARGE_INTEGER start,end;
LARGE_INTEGER freq;
LARGE_INTEGER result;
int i;
int test_int=0;
short test_short=0;
char test_char=0;
long long test_long_long=0;
double test_double=0;
float test_float=0;
long double test_long_double=0;
if(!QueryPerformanceFrequency(&freq)) {
puts("パフォーマンスカウンタに非対応です。");
return 1;
}
printf("パフォーマンスカウンタ周波数 : %lldHz\n",(long long)freq.QuadPart);
printf(" ループ回数 : %d回\n",ITERATION_NUM);
printf(" 空転 : ");
QueryPerformanceCounter(&start);
for(i=0;i<ITERATION_NUM;i++);
QueryPerformanceCounter(&end);
result.QuadPart=end.QuadPart-start.QuadPart;
printf("%13.6fms(%12lld)\n",(double)result.QuadPart/freq.QuadPart*1000.0,result.QuadPart);
printf(" long long加算 : ");
test_long_long=0;
QueryPerformanceCounter(&start);
for(i=0;i<ITERATION_NUM;i++) {
test_long_long=8123440183949381ll;
test_long_long=test_long_long+7940581038459233ll;
}
QueryPerformanceCounter(&end);
result.QuadPart=end.QuadPart-start.QuadPart;
printf("%13.6fms(%12lld)\n",(double)result.QuadPart/freq.QuadPart*1000.0,result.QuadPart);
printf(" long long減算 : ");
test_long_long=0;
QueryPerformanceCounter(&start);
for(i=0;i<ITERATION_NUM;i++) {
test_long_long=8123440183949381ll;
test_long_long=test_long_long-7940581038459233ll;
}
QueryPerformanceCounter(&end);
result.QuadPart=end.QuadPart-start.QuadPart;
printf("%13.6fms(%12lld)\n",(double)result.QuadPart/freq.QuadPart*1000.0,result.QuadPart);
printf(" long long乗算 : ");
test_long_long=1;
QueryPerformanceCounter(&start);
for(i=0;i<ITERATION_NUM;i++) {
test_long_long=8123440183949381ll;
test_long_long=test_long_long*7940581038459233ll;
}
QueryPerformanceCounter(&end);
result.QuadPart=end.QuadPart-start.QuadPart;
printf("%13.6fms(%12lld)\n",(double)result.QuadPart/freq.QuadPart*1000.0,result.QuadPart);
printf(" long long除算 : ");
test_long_long=1;
QueryPerformanceCounter(&start);
for(i=0;i<ITERATION_NUM;i++) {
test_long_long=8123440183949381ll;
test_long_long=test_long_long/7940581038459233ll;
}
QueryPerformanceCounter(&end);
result.QuadPart=end.QuadPart-start.QuadPart;
printf("%13.6fms(%12lld)\n",(double)result.QuadPart/freq.QuadPart*1000.0,result.QuadPart);
return 0;
}
]
結果は、
CODE:
空転 : 3079.913000ms
long long加算 : 7006.405000ms
long long減算 : 7002.811000ms
long long乗算 : 9194.985000ms
long long除算 : 13414.261000ms
となりました。やはり、この時long longの乗算は加減算より遅く、Windowsのlong longよりも遅くなりました。最適化がかかっていたようです。
続いて、実数を見てみましょう。
まず、Windowsの実数計算は、他に比べて全て遅いです。特に、Ubuntuと比べてもdoubleの遅さが目立ちます。
WindowsがUbuntuに勝っているのはlong doubleの除算だけです。多分誤差でしょう。横着して1回しか測定していないので。
そして、巫女ぐにょの実数計算は速いです。floatはUbuntuとそれほど変わりませんが、doubleとlong doubleがかなり速くなっています。
この結果が真実で実用的なら、おそらく巫女ぐにょでPrimeGridのLLRをすればWindowsよりかなり捗るでしょう。LLRは倍精度の実数が必要なようですから。
追記:実際に巫女ぐにょでPrimeGridをしてみましたが、実行速度はWindowsと全く変わりませんでした。
それにしても、なぜWindowsのdoubleはこんなに遅いのでしょうか…前の日記ではこんなに差がつかなかったのに、コンパイラの差でしょうか?それとも???
最後に、Three-way Branchを見てみましょう。
最適化なしではUbuntuよりWindowsの方がかなり遅いですが、最適化ありではWindowsの方が速いです。
おそらく、コンパイラがかなり改良されている、ということを意味しています。
そして、やっぱり巫女ぐにょは超速い!!!
この問題、本来は7秒以内に実行が終わらないと時間切れ失格なのですが、今回見た中で巫女ぐにょのみ制限をクリアしています。
最適化しなくても速いですし、最適化するとさらに速くなっています。
型ごとの演算速度で見た「(intの範囲)×(intの範囲)」のlong longの計算を多用しているコードのため、速くなったのでしょう。
ちなみにこのコードをAOJで実行した時のタイムは5.73秒でした。
(AOJの当時のスペックはDual Core Intel(R) Xeon(R) E3113 3GHz / gcc 4.3.0)
しかし、反省点があります。
それはOSによってgccのバージョンがバラバラだったこと。そのおかげで、おそらくOSの勝負ではなくgccの勝負になってしまいました。
Windowsでコンパイルしたアセンブリを用意したのですが、Ubuntuでコンパイルするときにエラーになってしまったので使用しませんでした。
次はコンパイルできるアセンブリを用意して実験したいと思います。