# 2016q 3Homework4 (clz) contributed by <`heathcliffYang`>, <`janetwei`> >>請記得更新分組面中的 github 連結 >>[color=red][name=課程助教] ## 目標 * 閱讀[重新理解數值](https://hackmd.io/s/SkKZBXZT) * 設計實驗,測試各個 clz 程式處理 32-bit 數值的效能差異,並分析原因 * 進行效能優化 ## [重新理解數值](https://hackmd.io/s/SkKZBXZT) ### bite-wise operator * `x & (x - 1) == 0` 在unsigned的情況下,可以判別X是否為2的N次方,若為true則是 * `x | (x + 1)` 確定最低位元的值 ### Count leading zero * ffs():回傳給定數值的first bit set的位置 例如 * 128 在 32-bit 表示為 `0x10000000`,ffs(128)會回傳 8 * 129 在 32bit 表示為 `0x10000001,`ffs(129) 會回傳 1 但是這裡發現錯誤,應該是寫成 * 128 在 32-bit 表示為`10000000`,ffs(128)會回傳 8 * 129 在 32bit 表示為 `10000001`,ffs(129) 會回傳 1 ## 觀察與實驗設計 ### 1. clz.c & clz.h : 將各個clz函式蒐集 * 現有材料為五種clz的函式: iteration, binarySearch, byteShift, recursive, harley - [ ] recursive 修正 32可以由16,8,4,2,1 組成 (有或沒有總共32種組合,0~31) ```c uint32_t count = 16; // Global variable 讓 recursive 每一層知道shift到哪 int recursive(uint32_t x) { int result; // shift upper half down, rest is filled up with 0s uint16_t upper = (x >> count); // 另存shift之後的值以供檢查是否足夠shift if(x==0) return 32; // stopping condition if(count == 1) { return !(x >> 1); /* 01 & 10 為例,shift 1之後分別為0 & 1, 前者是多一個0,但shift 1之後沒有值代表x還有一個0,所以要回傳1(也就是要+1)*/ } // shifting count and go into recursive count >>= 1; result = upper ? recursive(upper) : (count << 1) + recursive(x); /* shift成功代表leading zero的數量不足count,shift之後繼續用更小的count去檢查 shift不成功代表leading zero的數量足夠count,用shift前的值繼續檢查,而count<<1則把 該次檢查計算的leading zero數量加上*/ count <<= 1; // 以防多次執行時count的值發生錯誤 return result; } ``` :::info 參考[黃鏡清](https://github.com/workfunction/clz-tests)同學的版本,並做一些修改,e.g. 拿掉`mask`,因為在mask後x跟lower的值會不同的情況下,必是做upper的 ::: >> for example 的縮寫是 [e.g.](https://en.wiktionary.org/wiki/e.g.),不是 "ex" [name=jserv] - [ ] harley (仍在理解中) ```C int harley(uint32_t x) { static int table[64] = {32,31, u,16, u,30, 3, u,15, u, u, u,29,10, 2, u, u, u,12,14,21, u,19, u, u,28, u,25, u, 9, 1, u, 17, u, 4, u, u, u,11, u,13,22,20, u,26, u, u,18, 5, u, u,23, u,27, u, 6, u,24, 7, u, 8, u, 0, u}; /* Propagate leftmost 1-bit to the right */ x = x | (x >> 1); x = x | (x >> 2); x = x | (x >> 4); x = x | (x >> 8); x = x | (x >> 16); /*把最高位後的位元全部補為1*/ /* x = x * 0x6EB14F9 */ x = (x << 3) - x; /* Multiply by 7. */ x = (x << 8) - x; /* Multiply by 255. */ x = (x << 8) - x; /* Again. */ x = (x << 8) - x; /* Again. */ return table[x >> 26]; /*用前面6個位元表示leading zero有幾個*/ } ``` * 第一部分 : **把最高不為零位之後的位元全部補為1**可以算是一種第一次的分類,歸類相同clz數值的數。 * 第二部分 : 之後用**一些運算**(尚在理解中)讓各個數產生一對一的對應,之後做表格 -> hash function 的概念應用。 * shift 26 bits,也就是用剩下的 6 bits 來表示0~32的 clz 可能值,但總數是64。根據程式碼的觀察結果,我們認為他是用 output 的結果去做 table >> 延伸閱讀: [Fast, Deterministic, and Portable Counting Leading Zeros](http://embeddedgurus.com/state-space/2014/09/fast-deterministic-and-portable-counting-leading-zeros/) [name=jserv] * [error](http://stackoverflow.com/questions/6131455/compile-error-c2099-initializer-is-not-a-constant) : initializer element is not constant ---> `const int` 在 C 裡面不算是 constant,要用`#define` ### 2. 測試效能 & 作圖 #### 測執行時間 * 用0 ~ 4000先小測試一次 ![](https://i.imgur.com/XZhql5R.png) >> 製圖不要用 "function1", "function2" 一類不容易看不出來的名稱 [name=jserv] ```perf stat分析iteration Performance counter stats for './benchmark_time_1': 12,2282,3714 cache-misses 10,2295,7826,9347 cpu-cycles 17,5241,2939,1061 instructions # 1.71 insns per cycle 18,9201,5112 branch-misses 8635.382627756 seconds time elapsed ``` >> 除了 cache miss,也用 [prefetch 案例](/s/ryTASBCT) 裡頭 perf 參數和分析方式去探討 [name=jserv] #### 資料處理: * 一開始嘗試用Makefile的`for`去做跑數值的輸入,但是常常跑到一段時間後電腦就掛了,錯誤訊息是: too long list。 * 把 list 移到 benchmark 裡面,也因為 csv 的檔案太大開不太起來,連圖也不太能畫 * ==目前正在改寫 Makefile,預計分別輸入到不同檔案,正在解決檔案名稱與變數相關的問題......== ![](https://i.imgur.com/8Ne9Ptl.png) 我們試著用 iteration 函式執行一次0到2^32-1的時間,做圖結果如上 因為資料龐大,每執行一次就會耗費很多時間,因此我們還在思考簡化執行的方法 ![](https://i.imgur.com/45fqi8F.png) * 因為跑全部的資料檔案太大,所以我們只取 clz 的不同值跑了一次 * 用 perf top 觀察到函式熱點依序為harley-> iteration-> recursive-> binary-> byteSearch #### 效能分析: * iteration version: 當數字小的時候跑的很快,但是當數字很大時,迴圈裡面執行的行數更多,所以和其他函式比較起來效能不是很好 * binary version & byte Shift: 因為在判斷後做的事情一樣,可是判斷的方法略有不同,一個是直接去比值,一個是shift之後值是否為零,我們認為, byteShift 做的事情比較多,但是他的效能卻比較好;對於數值的大小來說,因為做的判斷次數一樣,所以執行時間和其他函式比相對穩定 * recursive version: 在數值較小時,叫用函式次數較多,所以執行時間較長;和其他函式相比,他的函式執行次數多了幾倍,因此效能最差 * harley's algorithm: 不管輸入的數值大小為何,函式所執行的動作都相同,因此他是執行時間最穩定的函式 ### 3. 應用(not yet) 1. **null suppression** 2. >>中英文關鍵字之間要使用空白區隔喔![color=red][name=課程助教]