• 沒有找到結果。

棋子表示法 3.1.3

表 4 表示棋子編號的表示法,0~2 bit 為棋子種類(Type),3~4 表示棋子所 屬方,01 表示紅方、10 表示黑方,00 表示空白。

28

表 5 列出了各個棋種對應的編號,其中 0~7 不使用,輪紅下棋時對應的 值(side)為 8,輪黑為 16。舉例來說,黑方炮的編號二進位表示為 10110,前 面 10 表示黑方,後面二進位 110 是十進位的 6-炮的編號。

其中棋子類型為 0 的部分為代表該方所有棋子聯集(Occupied)的編號,在 走步產生以及棋型偵測上佔有十分重要的地位,在後面章節會詳細介紹。

Type

表 4 棋子編號意義,最右邊為 bit 0

棋子類型 0 1 2 3 4 5 6 7

輪紅=8 All

編號 8 9 10 11 12 13 14 15

輪黑=16 All

編號 16 17 18 19 20 21 22 23

表 5 棋子編號方式

如此編號方式特別便於取出以及判斷棋種,例如要取出我方的炮只要使 用side+6 即可,不必去判斷我方是紅方還是黑方;走步產生時也只需要使side+kind 的組合便可以讓紅黑雙方共用走步產生程式碼,提升程式可 維護性;要取出對方的王時使用(side^0x18)+1,使用 XOR 翻轉代表 side 的 3~4 bit 並加上棋種編號即可快速取得;要檢查一個棋子 pc 是否是我方的 只要使用(side&pc)!=0 即可;檢查某一方 side 的棋子於棋盤位置 sq 是否 過河則使用(sq^(sd<<2))&0x40 快速測試。

29

Intrinsic 函數 3.1.4

如果要在兩個 64 位元的資料間進行操作可以使用 C++的傳統語法來達 成,但需要多個步驟因而導致效能的下降,這也是象棋完全使用 BitBoard 困 難的原因之一。而我們使用了 intrinsic 函數解決這個問題。

intrinsic 函數是編譯器提供的 CPU 低階指令呼叫方式。CPU 包含了多種 指令集,除了常用的 x86、x64 外還有 MMX、SSE、AVX 等。一般在編譯 C++

的程式時,編譯器通常只會使用到極少種的 CPU 指令,實際上 CPU 提供了 大量的原生指令,通常可以做到一般 C++語法很難表達的操作,進而大量提 升效能。使用 intrinsic 函數便能以函數呼叫的方式使用這些指令,但必須 Compiler 要支援。

如果要在兩個 64 bits 的資料間做 shift 的指令,以 C++的語法要實現是十 分複雜的,表 6 表示從 B 往左 shift 40 位元到 A 所需要的指令比較。

一般 C++語法 使用 intrinsic 函數 A = (A << 40) | (B >> 64-40);

B <<= 40;

A = __shiftleft128(B, A, 40);

B <<= 40;

表 6 Intrinsic 指令與傳統 C++指令比較:128 bits Shift 為例

一般 C++語法至少會產生 4 條 CPU 指令,而使用 intrinsic 卻只需要 2 條 CPU 指令,在指令延遲減少、資料相依性降低的情況下可以有效增加整體速

30

度。

另外要從 BitBoard 的「佔有」意義轉換成位置編號的訊息必須使用 MS1B、

LS1B (Most/Last Significant 1 bit-取得最高、最低位元為 1 的 Index),也是 BitBoard 最常使用的操作。以 LS1B 為例,傳統作法必須獨立出最低位元為 1 的資料,然後對該資料做Log2運算以取得該 bit 代表的編號,當然 Log 運算 十分慢,亦有作法使用 Magic Hash 查表以加速【9】。但 LS1B 以及 MS1B 都 有對應的原生指令可以使用:_BitScanReverse64 與 _BitScanForward64,如 圖 13 所示。此例中 MS1B 為 60、LS1B 會得到 1。

圖 13 MS1B、LS1B 示意圖

使用 Magic Hash 的 C++語法 使用 intrinsic 函數 t = X & -X; //取出 LSB

//計算 Magic Hash 的 Index

hIndex = t * Magic Number >> S;

//查表取得結果

result = HashTable[hIndex];

//使用 BSF 指令,一步完成

_BitScanForward64(&result, X);

表 7 Intrinsic 指令與傳統 C++指令比較:LS1B 為例

表 7 表示 LS1B 所需要的指令比較,使用 Magic Hash 的 C++語法至少會 產生 5 條 CPU 指令,且需要查詢記憶體資料,而使用 intrinsic 卻只需要 1 條 CPU 指令,而 BSF (BitScanForward) 指令只需要 3 個 CPU 週期,與一個乘法 運算相當,速度比傳統做法快上不少。再者這是 LS1B 的例子,取出 LSB 有

0001001101000001011000101100110001011100010

reverse forward

相關文件