サーバーの速度 / 指し手生成速度
sakurapyonの居候しているサーバーは /proc/cpuinfo によると Intel(R) Xeon(R) E5645, 2.4GHz, cache 4MB というものらしいんだけど、これって他のマシンに比べてどれだけ速い(遅い)んだろうか?
世の中にはスーパーπランキングというのがあって、CPUごとのランキングが掲載されている。Super Πは整数演算なので、コンピュータ将棋の速度の参考にはなるだろう。将棋の方は置換表とかのメモリアクセスも絡むけど、まあいいや。
Kanada Lab Homepage japanese versionからSuperΠのバイナリをダウンロード。コンパイルオプションがCPUに合ってないかもしれないけど、だぶん条件は皆一緒だろう。419万桁実行するとこんな感じだ。
$ ../super_pi/pi 22 Version 2.0 of the super_pi for Linux OS Fortran source program was translated into C program with version 19981204 of f2c, then generated C source program was optimized manually. pgcc 3.2-3 with compile option of "-fast -tp px -Mbuiltin -Minline=size:1000 -Mnoframe -Mnobounds -Mcache_align -Mdalign -Mnoreentrant" was used for the compilation. ------ Started super_pi run : 2012年 4月 15日 日曜日 10:37:48 JST Start of PI calculation up to 4194304 decimal digits End of initialization. Time= 1.603 Sec. I= 1 L= 0 Time= 4.300 Sec. I= 2 L= 0 Time= 4.865 Sec. I= 3 L= 1 Time= 4.156 Sec. I= 4 L= 2 Time= 3.825 Sec. I= 5 L= 5 Time= 4.615 Sec. I= 6 L= 10 Time= 5.058 Sec. I= 7 L= 21 Time= 4.351 Sec. I= 8 L= 43 Time= 4.109 Sec. I= 9 L= 87 Time= 3.882 Sec. I=10 L= 174 Time= 3.910 Sec. I=11 L= 349 Time= 4.306 Sec. I=12 L= 698 Time= 4.490 Sec. I=13 L= 1396 Time= 4.319 Sec. I=14 L= 2794 Time= 3.761 Sec. I=15 L= 5588 Time= 4.046 Sec. I=16 L= 11176 Time= 4.377 Sec. I=17 L= 22353 Time= 3.903 Sec. I=18 L= 44707 Time= 4.029 Sec. I=19 L= 89415 Time= 3.940 Sec. I=20 L= 178831 Time= 3.619 Sec. I=21 L= 357662 Time= 3.339 Sec. End of main loop End of calculation. Time= 91.380 Sec. End of data output. Time= 0.340 Sec. Total calculation(I/O) time= 91.720( 8.500) Sec. ------ Ended super_pi run : 2012年 4月 15日 日曜日 10:39:20 JST
90秒ちょいか。8bit機の頃を考えると桁違いに速いな。測るたびに1割ぐらい速度が変わるのはvpsのせいだろうなー。
スーパーπランキングの上位はover clockされているものばかり。Core i7 2600k@3.40GHzが44秒なのでクロック比からすると遅めな気がする。
ついでに指し手生成速度: gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)
もうちょっと速くならんかなー。AddMovesがまずいんだと思うけど。simkみたいに事前に手を用意して配列アクセスの方が早いだろうか? (悩みどころ)
↓この表間違ってます。追記あり。
PGOなし | PGOあり | ||||||
初期局面 | 614,860.19回/秒 | 1,689,226.28回/秒 | 30手 | ||||
指し手生成祭り局面 | 465,374.94回/秒 | 1,038,586.61回/秒 | 198手 |
NHK杯見なきゃ…
- 一人指し手生成祭り - 将棋プログラム「技巧」開発日記
- 一人指し手生成祭り - 2011-05-08 - aki.の月記
- かずの心の贅肉 指し手生成祭2
- 指し手生成ルーチンの性能 - 2009-11-07 - aki.の月記
追記:元の方は-pg残ってたんで測りなおし。ついでに always inline 入れて高速化してみた。
-pg付き | PGOなし | PGOあり | |||||||
初期局面 | 627197.94回/秒 | 1638551.85回/秒 | 1766550.37回/秒 | 30手 | |||||
指し手生成祭り局面 | 611332.52回/秒 | 1379780.42回/秒 | 1471510.09回/秒 | 198手 |
PGOの効果はご指摘の通り1割ぐらいでした。すんません。
always inlineの方が効果ありました(^_^;
指し手の速度だけなら
指し手の速度だけならそこそこ出てる気がするなあ。
なんで深く読めないんだろ?
駒打ちルーチン
たぶん、指し手がそこそこ速いのは姑息な展開が利いているのだと思います。こんな感じ。
桂香歩打ちはあまりいじってないです。
class Te { public: uint64 T; inline bool IsNull() { return (T&0x0ffff)==0; } inline Te() {}; inline Te(unsigned char f,unsigned char t,KomaInf k,KomaInf c,int p=0,int K=0 ,short v=0) : T ((uint64)t | (f<<8) | (k<<16) | (c<<24) | ((p==0)?0:(1ULL<<31)) | ((uint64)v)<<32) { (void)K; } (中略) // 駒打ち特化ルーチン // template <int hands,int player>inline void hGenDropHands(int &teNum, Te *teBuf, int n, unsigned char *empty) { for(int pos=0;pos<n;pos++) { if(hands&(1<<3)) teBuf[teNum++]=Te(0,empty[pos],player|GI,0,0); if(hands&(1<<4)) teBuf[teNum++]=Te(0,empty[pos],player|KI,0,0); if(hands&(1<<5)) teBuf[teNum++]=Te(0,empty[pos],player|KA,0,0); if(hands&(1<<6)) teBuf[teNum++]=Te(0,empty[pos],player|HI,0,0); } } // // 打つ手を生成する // template <int player>inline void hGenDrop(Kyokumen &k,int &teNum, Te *teBuf, int n, unsigned char *empty) { const int hands=(player==SELF)? ((k.Hand[SGI]>0?(1<<3):0) |(k.Hand[SKI]>0?(1<<4):0) |(k.Hand[SKA]>0?(1<<5):0) |(k.Hand[SHI]>0?(1<<6):0)): ((k.Hand[EGI]>0?(1<<3):0) |(k.Hand[EKI]>0?(1<<4):0) |(k.Hand[EKA]>0?(1<<5):0) |(k.Hand[EHI]>0?(1<<6):0)); switch (hands){ case 0x00: break; case 0x08: return hGenDropHands<0x08,player>(teNum,teBuf,n,empty); case 0x10: return hGenDropHands<0x10,player>(teNum,teBuf,n,empty); case 0x18: return hGenDropHands<0x18,player>(teNum,teBuf,n,empty); case 0x20: return hGenDropHands<0x20,player>(teNum,teBuf,n,empty); case 0x28: return hGenDropHands<0x28,player>(teNum,teBuf,n,empty); case 0x30: return hGenDropHands<0x30,player>(teNum,teBuf,n,empty); case 0x38: return hGenDropHands<0x38,player>(teNum,teBuf,n,empty); case 0x40: return hGenDropHands<0x40,player>(teNum,teBuf,n,empty); case 0x48: return hGenDropHands<0x48,player>(teNum,teBuf,n,empty); case 0x50: return hGenDropHands<0x50,player>(teNum,teBuf,n,empty); case 0x58: return hGenDropHands<0x58,player>(teNum,teBuf,n,empty); case 0x60: return hGenDropHands<0x60,player>(teNum,teBuf,n,empty); case 0x68: return hGenDropHands<0x68,player>(teNum,teBuf,n,empty); case 0x70: return hGenDropHands<0x70,player>(teNum,teBuf,n,empty); case 0x78: return hGenDropHands<0x78,player>(teNum,teBuf,n,empty); } // 有効そうな手を生成する // pinには必ず有効なpin情報がある前提 template <int SorE>int Kyokumen::hMakeEffectiveMoves(Te *teBuf,char *pin) { int teNum=0; int suji,dan; int nEmpty=0; unsigned char Empty[83]; Empty[nEmpty++]=0; // 盤上の駒を動かす for(dan=1;dan<=9;dan++) { for(suji=0x10;suji<=0x90;suji+=0x10) { if (ban[suji+dan]==EMPTY) { Empty[nEmpty++]=suji+dan; }else if (ban[suji+dan]&SorE) { AddMovesPromote(SorE,teNum,teBuf,ban[suji+dan],suji+dan,pin[suji+dan]); } } } Empty[nEmpty]=0x9a; if(SorE==SELF){ hGenDrop<SELF>(*this,teNum,teBuf,nEmpty-1,&Empty[1]); }else{ hGenDrop<ENEMY>(*this,teNum,teBuf,nEmpty-1,&Empty[1]); } (以下略) }
今気がついた。voidな関数の戻り値をreturnで返してやがる(>_<)。直そう…
持ち駒をbitで持ってればもうちょっと速くなるんだけど、改造箇所が増えまくるからなあ。