2017q1 Homework3 (software-pipelining) === contributed by < `vtim9907` > # 開發環境 - OS : Ubuntu 16.04.2 LTS - Kernel Version : 4.8.0-36-generic - Memory : 32 GB - CPU : Intel® Core™ i5-6600 Processor - cache : - L1i cache : 4*32 KB - L1d cache : 4*32 KB - L2 cache : 4*256 KB - L3 cache : 6144 KB - L1 cache imformation ( sudo dmidecode -t cache ) : ``` Handle 0x001E, DMI type 7, 19 bytes Cache Information Socket Designation: L1 Cache Configuration: Enabled, Not Socketed, Level 1 Operational Mode: Write Back Location: Internal Installed Size: 256 kB Maximum Size: 256 kB Supported SRAM Types: Synchronous Installed SRAM Type: Synchronous Speed: Unknown Error Correction Type: Parity System Type: Unified Associativity: 8-way Set-associative ``` # [論文](http://www.cc.gatech.edu/~hyesoon/lee_taco12.pdf)閱讀紀錄 - SW prefetching 用在短陣列、不規則的記憶體存取、減少 L1 cache miss 時,結果幾乎都是正向影響、提升效能。 - 當 SW prefetching 對 HW prefetcher 的 training 產生干擾時,會造成劇烈的負面影響,造成效能下降,尤其是當正在使用 SW prefetch 一部分 streams 的指令。 > 這裡的 training of the hardware prefetcher 的意思應是指因為 HW prefetcher 是做在硬體上的機制,所以這個機制必須要依靠一些先前的紀錄來做判斷,所以才用 "training" 來表達。 - Data structure 的複雜程度也會影響效能,比如說 array 或 RDS 就比較好預測,所以就容易用 SW prefetch;但像 hashing 就比較難預測,所以比較難有效的作 prefetch。 - streams & strided - unit-stride cache-line accesses as streams - access stride distances greater than two cache lines as strided - 將 prefetch 的情況分類為六種 : ![](https://i.imgur.com/llyvNac.png) - 文中透過實驗得出的一些結果 - SW prefetch 優點 1. Large Number of Streams 2. Short Streams 3. Irregular Memory Access 4. Cache Locality Hint 5. Loop Bounds - SW prefetch 缺點 1. Increased Instruction Count 2. Static Insertion 3. Code Structure Change - SW + HW prefetch 優點 1. Handling Multiple Streams 2. Positive Training - SW + HW prefetch 缺點 1. Negative Training 2. Harmful Software Prefetching # 分析效能 首先參考 [你所不知道的 C 語言:物件導向程式設計篇](https://hackmd.io/s/HJLyQaQMl) 學習封裝技巧,然後改寫 Makefile 把每個轉置的方法獨立出一個執行檔,之後就比較好做分析。 ### NAIVE - 執行時間 ``` naive: 237184 us ``` - 觀察 cache miss 情形 ``` Performance counter stats for './naive_transpose' (100 runs): 4143,0725 cache-misses # 86.427 % of all cache refs ( +- 0.33% ) 4793,7357 cache-references ( +- 0.30% ) 14,4837,4541 instructions # 1.54 insn per cycle ( +- 0.00% ) 9,3915,6402 cycles ( +- 0.12% ) 0.354528643 seconds time elapsed ( +- 0.21% ) ``` ### SSE - 執行時間 ``` sse: 154895 us ``` - 觀察 cache miss 情形 ``` Performance counter stats for './sse_transpose' (100 runs): 1464,3696 cache-misses # 76.178 % of all cache refs ( +- 0.06% ) 1922,2917 cache-references ( +- 0.06% ) 12,3647,2526 instructions # 1.80 insn per cycle ( +- 0.00% ) 6,8775,1007 cycles ( +- 0.10% ) 0.271588039 seconds time elapsed ( +- 0.17% ) ``` ### SSE prefetch 用 D = 8 跑。 - 執行時間 ``` sse prefetch: 43993 us ``` - 觀察 cache miss 情形 ``` Performance counter stats for './sse_prefetch_transpose' (100 runs): 807,7417 cache-misses # 64.951 % of all cache refs ( +- 0.19% ) 1243,6203 cache-references ( +- 0.13% ) 12,8248,3639 instructions # 2.39 insn per cycle ( +- 0.00% ) 5,3685,5417 cycles ( +- 0.03% ) 0.143776019 seconds time elapsed ( +- 0.13% ) ``` ### AVX 原本 SSE 是一次 for loop 跑 4 * 4 的矩陣,但換成 AVX 後,一次可以塞的東西是原來的兩倍,所以我把 code 改成一個 loop 就跑 8 * 8 大小的矩陣。 - 執行時間 ``` avx: 59237 us ``` - 觀察 cache miss 情形 ``` Performance counter stats for './avx_transpose' (100 runs): 1069,2092 cache-misses # 70.618 % of all cache refs ( +- 0.20% ) 1514,0732 cache-references ( +- 0.07% ) 11,4047,2529 instructions # 2.08 insn per cycle ( +- 0.00% ) 5,4719,4266 cycles ( +- 0.12% ) 0.185405113 seconds time elapsed ( +- 0.19% ) ``` ### AVX + (SSE prefetch) > 原本想用 AVX prefetch 跑跑看,但後來看了 [Intel Intrinsics Guide](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=4077,4080,4081) 發現除了 SSE 有 prefetch 的指另外,AVX 只有 AVX-512 有 prefetch 的指令可用,而我的 cpu 沒支援,所以沒辦法用。 但我還是想用用看 prefetch ,所以就直接用 SSE prefetch 指令下去跑。 D = 8 - 執行時間 ``` avx prefetch: 45097 us ``` - 觀察 cache miss 情形 ``` Performance counter stats for './avx_prefetch_transpose' (100 runs): 1120,9174 cache-misses # 71.177 % of all cache refs ( +- 0.10% ) 1574,8256 cache-references ( +- 0.10% ) 11,6350,5367 instructions # 2.17 insn per cycle ( +- 0.00% ) 5,3675,9191 cycles ( +- 0.09% ) 0.161260544 seconds time elapsed ( +- 0.14% ) ``` ## 比較執行時間 目前主要想到變動兩個變數來測量執行時間的差異 1. Array size 的大小 2. Distance 的大小 首先我變動 array size,從原來的 4096 * 4096 開始,之後矩陣的 row 和 column 每次都增加 96,增加 100 次,去觀察各個不同方式的執行時間變化。 ``` SSE prefetch : D = 8 AVX using SSE prefetch : D = 8 ``` ![](https://i.imgur.com/Xql8aJl.png) 首先比較特殊的地方是只有最一開始 N = 4096 的時候,SSE prefetch 是最快的,可是當矩陣逐漸擴大,使用了 AVX 指令集的矩陣轉置速度卻比 SSE prefetch 還快,而且矩陣大小越大,時間差越多。 > 為何 SSE prefetch 只有最一開始比較快我還沒想清楚,會不會是我 code 寫錯 @@ > 還不確定為何圖中的抖動如此劇烈,我已經盡量把可能會擾亂測試的程式或網頁都關了,可是跑了好幾次,抖動的程度和頻率好像都幾乎一樣,應該不是巧合? 也許跟 cache 有關,還在思考中。 改變 AVX + SSE prefetch 的 Distance 再測測看: ``` SSE prefetch : D = 8 AVX using SSE prefetch : D = 16 ``` ![](https://i.imgur.com/OBt9szS.png) 和 D = 8 時差異不明顯,還是跟只用 AVX 差不多,如同論文內所說,Distance 只要不要挑太小,效能就不會太差。