# <center>Parallel Programming Hw1 0783404 黃揚 # Part 1 ## Q1:run ./myexp -s 10000 and sweep the vector width from 2, 4, 8, to 16 用一萬筆資料去測,不同vector width實驗結果如下: * **Vector width** = 2 Vector Utilization:80.5% ![](https://i.imgur.com/zFJBelr.png) * **Vector width** = 4 Vector Utilization:72.6% ![](https://i.imgur.com/sMqc96T.png) * **Vector width** = 8 Vector Utilization:68.6% ![](https://i.imgur.com/QV2sWzT.png) * **Vector width** = 16 Vector Utilization:66.6% ![](https://i.imgur.com/sRcCVNl.png) * **小結:** 可以很明顯觀察到utilization會隨著vector width加大而下降。 1. 不整除的資料會浪費空間 舉例來說,若`width = 8, N = 20`,則在最後一次切割的時候會剩下4筆資料要處理,但是仍舊佔用了`8`的空間。而這樣的現象會隨著vector width變大而更嚴重,例如當`width = 16, N = 20`時,則最後一次切割會剩4筆資料,但是佔用了`16`的空間。 2. while loop裡面的計算過程 * 在找到exp=0者並把output設為1的時候 使用`./myexp -l`觀察使用情形時可以發現,在`_pp_vset_float(result, 1.0f, maskExpIsZero);`這行的active比例極低,因為測資中`exp=0`的筆數相當少。而同第一點所述,vecotr width越大,就越容易包含這樣的情形。 * 在進行指數運算時 ![](https://i.imgur.com/04oJnIR.png) 以上圖為例。因為在進行指數運算時,會去看哪邊的exp>1再運算。而運算到此處時,16筆資料只有6筆資料是exp>1的,所以這6筆資料會去做result*x、exp減一以及檢察是否有超過上界的情形發生。就會造成其他exp不大於1(即不須運算)的所在空間被浪費掉。 * 每次運算完成後,要再找一次哪邊的exp仍大於1 `_pp_vgt_int(maskNotDone, exp, one, maskNotDone);`算到越後面,exp仍大於1的地方就越少,越容易發生浪費。 # Part 2 ## Q2-1 MOVAPS—Move Aligned Packed Single-Precision Floating-Point Values MOVUPS—Move Unaligned Packed Single-Precision Floating-Point Values 由於avx2中,float是32 bit的([參考的文章](https://zhuanlan.zhihu.com/p/94649418)),所以在code中加入這一段 ```cpp a = (float *)__builtin_assume_aligned(a, 32); b = (float *)__builtin_assume_aligned(b, 32); c = (float *)__builtin_assume_aligned(c, 32); ``` 就可以成功對齊,得到預期的結果`vmovaps`如下圖。 ![](https://i.imgur.com/A0RTKY3.png) ## Q2-2 對於這三個case,都各執行五次,取中位數當作結果 **Case 1:** `$ make clean && make && ./test_auto_vectorize -t 1` | 1 | 2 | 3 |4 | 5 | | -------- | -------- | -------- |-------- |-------- | | 8.16878s | 8.16874s | 8.16857s | 8.16888s | 8.16862s | 取中位數,case1的執行時間為8.16874秒 **Case 2** `$ make clean && make VECTORIZE=1 && ./test_auto_vectorize -t 1` | 1 | 2 | 3 |4 | 5 | | -------- | -------- | -------- |-------- |-------- | | 2.60766s | 2.60733s | 2.60773s | 2.60698s | 2.60814s | 取中位數,case2的執行時間為2.60766秒 **Case 3** `$ make clean && make VECTORIZE=1 AVX2=1 && ./test_auto_vectorize -t 1` | 1 | 2 | 3 |4 | 5 | | -------- | -------- | -------- |-------- |-------- | | 1.35233s | 1.35265s | 1.35261s | 1.3525s| 1.35272s | 取中位數,case3的執行時間為1.35261秒 * **小結:** * Case1所需時間是Case2的3倍左右,Case2所需時間又是Case3的2倍。 * Case3相比於Case2,多使用了AVX2,而avx2的width是256 bit,而SSE是128bit。這兩者的width差了一倍,所以在做平行運算時一次能運算的長度差了一倍,連帶影響到時間差大約一倍。([參考來源](https://software.intel.com/content/www/us/en/develop/articles/migrating-from-sse2-vector-operations-to-avx2-vector-operations.html)) * Case1沒有使用vetorize計算,所以速度會是最慢的 ## Q2-3 用diff指令觀察修改前後的assembly code差異如下圖。左邊是修改後,右邊是修改前 可以看到出現了`movaps`跟`maxps` ![](https://i.imgur.com/BkmCoNw.png) * **小結:** test2.cpp中,這兩種版本的code的主要差異在於,修改前會先預設c[j]=a[j],然後根據判斷式決定式否要把c[j]改成b[j]。而修改後的版本則是先判斷大小,再決定c[j]應該要等於誰。藉由assembly code可以發現,修改前的版本是使用32bit的register,而修改後的是128bit的,推測是因為修改前的版本會受到判斷式是否成立的影響,導致沒辦法align,而修改後的改本其每次執行的運算步驟都是規律的,不會受影響。 參考資料: 1. https://ithelp.ithome.com.tw/articles/10227112 2. http://blog.luoyuanhang.com/2015/07/07/%E5%87%A0%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%B1%87%E7%BC%96%E6%8C%87%E4%BB%A4%E8%AF%A6%E8%A7%A3/