# 2017q1 Homework3 (software-pipelining)
###### tags: `embedded`
contributed by <`Cayonliow`>
tags:
## 作業
* 題目: [B05: software-pipelining](https://hackmd.io/s/rks62p1sl#)
* github: [prefetcher](https://github.com/Cayonliow/prefetcher)
* 論文: [When Prefetching Works, When It Doesn’t, and Why](http://www.cc.gatech.edu/~hyesoon/lee_taco12.pdf)
* 相關資料: [SIMD Programming Introduction](https://docs.google.com/presentation/d/1LeVe7EAmZvqD3KN7p4Wynbd36xPOk9biBCFrdmsqzKs/edit#slide=id.p3), [在計算機裡頭實踐演算法](https://hackmd.io/s/HyKtIPN0#)
* 參考資料:
* [Intel Intrinsics Guide(SSE2指令集的部分)](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=SSE2)
* [HOTBALL'S HIVE SSE簡介](https://www.csie.ntu.edu.tw/~r89004/hive/sse/page_1.html)
* [Programming trivia: 4x4 integer matrix transpose in SSE2](https://www.randombit.net/bitbashing/2009/10/08/integer_matrix_transpose_in_sse2.html)
## 論文閱讀
在讀論文之前, 去找了找 Prefetch 的定義
* 來自[百度](http://baike.baidu.com/item/Prefetch)
* =预读取文件夹,用来存放系统已访问过的文件的预读信息,扩展名为PF。之所以自动创建Prefetch文件夹,是为了加快系统启动的进程
* 來自[wikipedia](https://en.wikipedia.org/wiki/Prefetching)(比較完整)
* Prefetching in computer science is a technique for speeding up fetch operations by beginning a fetch operation whose result is expected to be needed soon. Usually this is before it is known to be needed, so there is a risk of wasting time by prefetching data that will not be used. The technique can be applied in several circumstances
* 來自[0xff07的共筆](https://hackmd.io/s/H1d_W-gsg#)所提供的一份[論文](http://www.eecg.toronto.edu/~steffan/carg/readings/ghb.pdf)
* Table-Based Prefetching
* Stride Prefetching
![](https://i.imgur.com/bzjsVKp.png)
* 在 stride prefetching 裏 有一個表是用來儲存 stride-related local history information
* 儲存 the most recent stride, last address, state infoemation
* 當 prefetch 被觸發, a+s, a+2s,...a+ds 的 address 會被預讀
* a = 最根本的地址
* s = 偵測到的範圍
* d = degree of prefetch (不太懂。。
* Markov Prefetching
![](https://i.imgur.com/3SSoapx.png)
* miss address stream: 發生 cache miss 的資料流
* Correlation table : 記錄這個 address 的下一個發生cache miss 的 address
* Distance Prefetching
![](https://i.imgur.com/XVce03a.png)
* 圖會跟 Markov Prefetching 長得很像,因爲是他是 Markov Prefetching 的概括(推論
* 原本是設計給 TLB ,可是後來發現比較適合用來預讀 cache lines
* address delta : 距離上一個發生 cache miss 的 address 的長度
* Markov 是直接記錄地址, 可是 Distance 是記錄與上一個距離, 所以會出現很多重復的資料
### 接下來是論文的部分
提到的是 Software Prefetching, Hardware Prefetching, 跟兩者之間一起使用的利弊
![](https://i.imgur.com/P6MLydF.png)
圖上表示的是 Sw, Hw,Sw&Hw 用各種 benchmark 的效能的測試結果
---
![](https://i.imgur.com/eSNRybF.png)
這有提到各種形態造成的 index 是 direct 或是 Indirect 的, 這會影響到 Hw 的 Prefetch 效能
* Direct 指的是連續或是有某種規律的, Indirect 則是相反(沒有規律的)
* Direct 可以很容易的被 HW 預讀,因爲有規律, 可是如果是 Indirect, 在Hw,需要一些特別的 prefetching mechanism, 可是 Sw 卻能更輕易的 prefetch
* ![](https://i.imgur.com/EFq1yCx.png)
* 如上圖: 在使用 Sw 進行 Indirect 的 Prefetching, 會增加 Intruction Count 和更多的 memory access
---
![](https://i.imgur.com/UjPgCtl.png)
在使用 Sw Prefetching 需要手動輸入一些程式碼
* 在 Intel 中的 SSE SIMD 指令
```
#include <mmintrinsics.h>
void _mm_prefetch(char * p , int i );
```
p = 要做 Prefetch 的資料的地址
i = 上圖的 Hint 的部分
* _MM_HINT_T0 : 放到 L1 Cache
* _MM_HINT_T1 : 放到 L2 Cache
* _MM_HINT_T2 : 放到 L3 Cache
* _MM_HINT_NTA : 這塊資料暫時不會用到
---
![](https://i.imgur.com/RbBVIJt.png)
這裏說的是對 Prefetch 的分類 只有Timely 是好的
![](https://i.imgur.com/vzL05sl.png)
* Late: 在這筆資料需要被取用的時候還沒送到, 可是卻做了 Prefetch 的動作,浪費效能
* Early: 提早太多將資料送到, 可是 cache 會一直刷新,在資料被取用之前就被洗到了,做了 Prefetch 的動作可是卻沒有用到
* Redundant_dc: 送到多餘的資料去 data cache
* dedundant_mshr: 送到多餘的資料去 MSHR^5^
* Incorrect: 送到錯的資料
---
![](https://i.imgur.com/KzhMZ5E.png)
Software Prefetch Distance
* l = prefetch latency, 延遲
* s = the length of the shortest path through the loop body, (不懂
---
#### Software Prefetching & Hardware Prefetching
##### SW 的好處 = HW 的壞處
* Large number of Stream
* SW 因爲可以手動輸入程式所以不會像 HW 一樣,因爲有多條資料流而混亂,
* Prefetch request 可以獨立被輸入進 lbm
* 下面的程式碼就有多條資料流
```
b[i][j][k] = (a[i][j][k] + a[i][j][k+1] + a[i][j][k-1] + a[i][j-1][k] + a[i][j+1][k]+...)/27
```
* Short Streams
* HW 因爲時間去 "學習" 這串資料流的規律,至少兩個 cache misses 才能判斷資料流的方向, 所以當資料流太短,可能在 HW 還沒有學會就已經結束了
* Irregular Memory Access
* 只要手動輸入對應的內聯函數(intrinsics) 就可以任意 prefetch 各種沒有規律的資料,可是 HW 的話則需要非常復雜的架構
* Cache locality Hint
* 可以選擇要放入的 cache
* HW 的準確性一般都很低,所以會很容易 L1 cache pollution
* L1 cache 的空間最小,速度最快,所以如果有L1 cache pollution 的話,會出現很多 cache miss ,對效能造成很大的影響
* Loop Bounds
* 因爲對 HW 來說,他是靠學習來 prefetch 所以沒有人告訴他範圍,他會不停依據規律取資料,可是這些資料可能有些已經越界,而最後取到無用的資料
---
##### SW 的壞處 = HW 的好處
* Instruction count
* SW 因爲需要手動輸入指令,所以指令的數量會增加,而且如果是 irregular / Indirect 的話,所需要的指令會更多, HW 因爲是已經寫死在硬體裏,所以沒有額外的指令
* ![](https://i.imgur.com/EFq1yCx.png)
* 在這裏可以看的出來 Indirect 所需要的指令會比較多
* ![](https://i.imgur.com/lej5kG4.png)
* 在不同的 benchmark 測試下, 有一些的 Prefetch 指令是多於原本的指令超過 100%
* Static Insertion
* prefetching distance 跟記憶體搬資料到 Cache 的時間,以及 Cache 大小有關。而這些可能隨著不同硬體而有所變化,因而最佳的 prefetching distance 也有所不同。SW 不能在 Prefetch 進行的期間修改 Prefetching Distance
* Code Structure change
* 如果在 loop 裏的指令很少, 導致 SW 來不及 Prefetch, 可能就要做 Loop Splitting 的動作來改變資料結構
---
##### SW & HW 的混用
* Handling Multiple Streams
* 可以 prefetch 更多的資料流
* 如果有很多的資料流,可以將 Direct 跟 Regular 的分配給 HW, Indirect 跟 Irregulaer 的分配給 SW
* Training
* 好處: SW 會幫助加快的 HW 的 Training
* 有 refer 參考的概念
* 壞處: 可是 SW 也會幫助減慢的 HW 的 Training
* SW 所 prefetch 好的 prefetched blocks 有時候會被隱藏起來, 這會導致 HW 的“學習” 錯誤, 需要更長的 Training time
* Harmful Software Prefetching
* HW 的準確性一般而言都會比 SW 差 , 當 SW 發生錯誤時, 這會使 HW 有更大的壓力,使得 HW 的效能降低
### 開發記錄
先執行看看結果
```
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
sse prefetch: 58170 us
sse: 122664 us
naive: 267798 us
```
做的是矩陣轉置, 並看到這三種方式的執行時間
然後個別檢查各種方式的 cache miss
* SSE Prefetch
```
Performance counter stats for './main' (100 runs):
428,9651 cache-misses # 78.672 % of all cache refs ( +- 0.05% )
545,2589 cache-references ( +- 0.01% )
848,9913 L1-dcache-load-misses ( +- 0.02% )
<not supported> L1-dcache-store-misses
<not supported> L1-dcache-prefetch-misses
2,9095 L1-icache-load-misses ( +- 0.35% )
0.186183896 seconds time elapsed ( +- 0.09% )
```
* SSE
```
Performance counter stats for './main' (100 runs):
448,4773 cache-misses # 81.924 % of all cache refs ( +- 0.61% )
547,4291 cache-references ( +- 0.06% )
849,2977 L1-dcache-load-misses ( +- 0.03% )
<not supported> L1-dcache-store-misses
<not supported> L1-dcache-prefetch-misses
4,1314 L1-icache-load-misses ( +- 3.31% )
0.253898320 seconds time elapsed ( +- 0.57% )
```
* Naive
```
Performance counter stats for './main' (100 runs):
1678,6696 cache-misses # 93.020 % of all cache refs ( +- 0.09% )
1804,6256 cache-references ( +- 0.01% )
2105,5287 L1-dcache-load-misses ( +- 0.01% )
<not supported> L1-dcache-store-misses
<not supported> L1-dcache-prefetch-misses
3,6499 L1-icache-load-misses ( +- 2.63% )
0.387200088 seconds time elapsed ( +- 0.53% )
很明顯 naive 所造成的 cache-miss 是比較高的
```
>> perf stat 的結果有兩個 not supported 分別是 L1-dcache-store-misses 跟 L1-dcache-prefetch-misses 原因還在查詢中
---
#### AVX
參考 [illusion030 的共筆](https://hackmd.io/s/HkHDV-moe#)與[ierosodin 的共筆](https://hackmd.io/s/rkX95E-il#) 中的 使用 SSE 的指令集
* 依樣畫葫蘆地跟着寫了一個版本
* 去 [Intel Intrinsics Guide(AVX指令集的部分)](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=AVX) 找每一個指令的意思
* 一開始忘記了 header file `<immintrin.h>`
* 然後還一直編譯不過, 因爲沒有在 Makefile 裏 加 這個
```
CFLAGS = -msse2 --std gnu99 -O0 -Wall -Wextra -mavx2
```
| |Description|
|-------------------|--|
| _mm256_loadu_si256|從記憶體中讀入 256-bits 的 整數(integer data) 放入 dst. mem_addr (不需要有特定邊界? |
| _mm256_unpacklo_epi32|將兩個參數的 lower bits 以 32 bits 為單位輪流排序並 return|
| _mm256_unpackhi_epi32|將兩個參數的 higher bits 以 32 bits 為單位輪流排序並 return|
| _mm256_unpacklo_epi64|將兩個參數的 lower bits 以 64 bits 為單位輪流排序並 return|
| _mm256_unpackhi_epi64|將兩個參數的 higher bits 以 64 bits 為單位輪流排序並 return|
|_mm256_permute2x128_si256|將兩個參數以 128 bits 爲單位進行隨機洗牌,然後存入 dst|
|_mm256_storeu_si256|將整數數據以 256 bits 爲單位存入記憶體|
執行100次, 其中的5次執行時間:
```
avx: 58198 us
avx: 57160 us
avx: 59003 us
avx: 56443 us
avx: 56071 us
```
cache miss:
```
Performance counter stats for './cache-miss-avx' (100 runs):
331,0269 cache-misses # 74.376 % of all cache refs ( +- 0.31% )
445,0747 cache-references ( +- 0.32% )
803,6992 L1-dcache-load-misses ( +- 0.25% )
<not supported> L1-dcache-store-misses
<not supported> L1-dcache-prefetch-misses
3,4753 L1-icache-load-misses ( +- 2.62% )
0.200664836 seconds time elapsed ( +- 1.17% )
```
不低, 可是已經比錢三個版本來的低