---
# System prepended metadata

title: 'NYCU PP-f23 Programming Assignment I: SIMD'
tags: [NYCU Parallel Programming f23]

---

# NYCU PP-f23 Programming Assignment I: SIMD

###### tags: `NYCU Parallel Programming f23`

[TOC]

## Q1-1: Does the vector utilization increase, decrease or stay the same as VECTOR_WIDTH changes? Why?

### 結果圖
![Vector Width 2](https://hackmd.io/_uploads/B1fHt5WGp.png)

![Vector Width 4](https://hackmd.io/_uploads/HJWl99ZM6.png)

![Vector Width 8](https://hackmd.io/_uploads/H1QQq5WMT.png)

![Vector Width 16](https://hackmd.io/_uploads/H1DIqcZza.png)

### 結論

可以看出當 vector width 越大，vector utilization 會越小。

### 原因

當 vector width 越大，越有可能出現閒置的運算單元 (mask 出現 0 機會越高)。比如以這次作業的 `clampedExpVector()` 來說，單某些數字的 exponents 已經乘完了，但可能還有其他數字還沒乘完，於是乘完的會跟沒乘完的一起進入下一個 while loop，導致 vector 利用率降低。

而這種情況很明顯在 vector width 大時會特別嚴重，因為要等待所以數字乘完的 loops 會越多，導致 mask 中出現大量的 0，最後統計下來 vector utilization 就變比較低。

## Q2-1: Fix the code to make sure it uses aligned moves for the best performance.

![](https://hackmd.io/_uploads/rJoq1s-G6.png)

從原來的組語中可以觀察到，AVX2 指令都是每 32bits 的資料 move 到 register，可以推測 AVX2 的 vector width 為 32bits，故將 `__builtin_assume_aligned(*, 16)` 改為 `__builtin_assume_aligned(*, 32)` 後即可將 `vmovups` 變為 `vmovaps`。如下圖:

![](https://hackmd.io/_uploads/By9ReobGp.png)

## Q2-2: What speedup does the vectorized code achieve over the unvectorized code? What additional speedup does using -mavx2 give (AVX2=1 in the Makefile)? You may wish to run this experiment several times and take median elapsed times; you can report answers to the nearest 100% (e.g., 2×, 3×, etc). What can you infer about the bit width of the default vector registers on the PP machines? What about the bit width of the AVX2 vector registers?

* What speedup does the vectorized code achieve over the unvectorized code?
    * `Unvectorized`: 8.32s
    * `Vectorized`: 2.64s (約為 unvectorized 3x 快)
    * `AVX2`: 1.41s (約為 vectorized 2x 快，unvectorized 6x 快)

* What can you infer about the bit width of the default vector registers on the PP machines?

    ![](https://hackmd.io/_uploads/SkobViZGT.png)

    從組語中 move 的資料寬度來看，PP machine 的 bit width 為 16bits。
    
* What about the bit width of the AVX2 vector registers?

    ![](https://hackmd.io/_uploads/By9ReobGp.png)

    從組語中 move 的資料寬度來看，AVX2 的 bit width 為 32bits。

## Q2-3: Provide a theory for why the compiler is generating dramatically different assembly.

* Case1:
    ```cpp
    c[j] = a[j];
    if (b[j] > a[j])
        c[j] = b[j];
    ```
* Case2:
    ```cpp
    if (b[j] > a[j]) 
        c[j] = b[j];
    else 
        c[j] = a[j];
    ```

從 `MAXPS` 在 document 裡的 psudo code 來看:
```
MAX(SRC1, SRC2)
{
    IF ((SRC1 = 0.0) and (SRC2 = 0.0)) THEN DEST := SRC2;
        ELSE IF (SRC1 = NaN) THEN DEST := SRC2; FI;
        ELSE IF (SRC2 = NaN) THEN DEST := SRC2; FI;
        ELSE IF (SRC1 > SRC2) THEN DEST := SRC1;
        ELSE DEST := SRC2;
    FI;
}
```
`MAXPS` 會回傳較大的變數 (DEST) 進 register，因此 CPU 可以直接將回傳向量直接賦值給 c，因此 case2 直接將 a、b 做比較可以很容易用 `MAXPS` 達到上面說的效果。

而 case1 若要用 vectorized 的概念來思考的話，就變成先將 a 賦值給了 c，再來看 c 中那些 elements 需要被更新，而有些 elements 可能不用更新，有些可能要，但所有 elements 又必須一起處理。這就形成了與 Q1-1 類似的情況，可能會有許多無需運算的 elements 被 move，造成 utilization 效率低下。