CS:APP 學習指引 (1)--隨堂練習
===
contributed by <`BreezeDa`>
###### tags: `sysprog2017`
## 計算機組織與結構
:::success
4.47: 以 C 語言指標改寫原本使用陣列表示法的氣泡排序程式碼 (Page 327) / 延伸題目: 最佳化 / Analysis on Bubble Sort Algorithm Optimization
:::
1. 改成用指標引用陣列
```clike
void bubble_a(long *data, long count){
long *i, *last;
for(last = data + count -1 ; last > data; last--){
for(i = data; i<last; i++){
if( *(i+1) < *i ){
//swap
long t = *(i+1);
*(i+1) = *i;
*i = t;
}
}
}
}
```
2. 最佳化
這邊一樣使用指標引用,與 1. 不同的地方: **多了一個指標 newLast,分別在 line 2, 3, 4, 11**。 newLast 的作用在於減少比較次數。減少的方法如右說明 : bubble sort 在比較的過程中,若倆倆順序不符,便會 swap,反過來說,若符合大小排列,便不會交換,因此只要找到每輪中最後一個被調換的位置,就可以確定其後方的數列已經排序完成。利用這點在每輪紀錄 newLast 並更新到 last 便可降低比較次數。
```clike=
void bubble_a(long *data, long count){
long *i, *last, *newLast;
for(last = data + count -1 ; last > data; last = newLast){
newLast = data;
for(i = data; i<last; i++){
if( *(i+1) < *i ){
//swap
long t = *(i+1);
*(i+1) = *i;
*i = t;
newLast = i;
}
}
}
}
```
:::success
6.37: 計算 N=64 和 N=60 的 cache miss rate (Page 455)
:::
**由題目可知以下幾個條件**
1. int 大小為 4 bytes
2. cache size = 4KB
3. cache 為 direct map
4. block size = 16 bytes
5. 由 2. 4. 可知 ==**cache 可容納的 block 數量 = 4KB/16B = 256**==
6. 由 1. 4. 可知一個 ==**block 可塞 4 個 integer**==
7. 由 3. 5. 可想像 cache 的長相如下 ( 沒畫出 valid bit ):
|index| block content |
|-|--|
|0| mem[0x0800000] |
|1||
|...||
|255||
8. 其陣列從 0x08000000 開始,因為 LSB 為 0,所以 array 從該 block 的第一個 byte 開始,且
**block number = 0x0800000** ( 0x08000000 所在的 block 的編號 )
0x0800000 mod 0x100 = 0,所以**該 block 0x0800000 若讀取到 cache 中會放置在 index 為 0 的 地方** ( 如上示意圖 )
### N = 64
array 中,每一列都**剛好塞滿** 16 個 blocks,而**每 16 列可剛好塞滿 cache 0~255**,示意圖如下
其中
- 側標及上標表示 array 0~63 的索引
- 每一格代表一個 block
- blk [N] : 表示此 block 若被存放到 cache 將會放在 index = N 的那塊空間
|array|0|4|...|63|
|--|-|-|-|-|
|**0**|==blk [0]==| blk [1]|...|blk [15]
|**1**|blk [16]
|...|...
|**15**|blk [240]| blk [241]|... |==blk [255]==
|**16**|==blk [0]==| blk [1]|...| blk [15]
|**17**|blk [16]
|...|
|**31**|blk [240]|blk [241]|...|==blk [255]==
|**32**|
|...|
|**63**|
---
**sumA**
row major 的存取方式,在存取每個 block 的第一個 integer 時,會 miss,其他皆 hit
又因為條件 6,所以 **miss rate = 1/4 = 25 %**
e.g.
:::info
miss: a[0][0]
hit : a[0][1], a[0][2], a[0][3]
:::
**sumB**
col major 的存取方式,由上面的 array 示意圖可看出,在讀取行時,每 16 列,便會出現 index 相同的 block (cache 中),因此在第一行全部 miss 後,讀到第二行時,仍會因為該 block 早就被其他 block 覆蓋而全部 miss。因此 **miss rate = 100%**
e.g.
:::info
在讀取 a[0][0] 時,系統將該 block 從 memory 搬到 cache index = 0 的位置中,同樣的當讀到 a[16][0] 時,系統也將該 block 從 memory 搬到 cache index = 0 的位置中,如此一來便覆蓋掉 a[0][0] 所在的 block,所以當讀取到第二行 a[0][1] 時,因為其所在的 block 已經被覆蓋掉了,所以仍會 miss。
:::
**sumC**
一次讀取 4 個元素,a[i][j]、a[i][j+1]、a[i+1][j]、a[i+1][j+1],讀取流向與 col major 相同,其中前兩個元素屬於同一個 block,後兩個元素同屬於另一個 block。因此第一輪讀取時,也就是讀取第一、二行時,第一行的部分全部 miss,第二行的元素全部 hit,而在第二輪讀取時,也就是讀取第三、四行時,會因為如同 sumB 覆蓋的原因,使得情況與第一輪相同。因此,每一輪皆有一半 miss 一半 hit,所以 **miss rate = 50%**
### N=60
array 中,每一列都**剛好塞滿** 15 個 blocks,示意圖如下
|array|0|4|...|59|
|--|-|-|-|-|
|**0**|==blk [0]==| blk [1]|...|blk [14]
|**1**|blk [15]
|...|...
|**16**|blk [240]| blk [241]|... |blk [254]
|**17**|==blk [255]==| ==blk [0]==|...| blk [13]
|**18**|blk [14] | blk [15]
|...|
|**35**|blk [254]|==blk [255]==|..
|...|
|**59**|
**sumA**
row major order,同 N = 64 的 sumA 說明
=> **miss rate = 25%**
**sumB**
由上面 array 與 block 的混搭示意圖可看出,
第二個 blk [0] 的位置 = 第一個 blk [0] + 17 row + 1 col
第二個 blk [15] 的位置 = 第一個 blk [15] + 17 row + 1 col
...
所以可以推知,**index 相同的 block 不會出現在同一行中**,這表示讀完第一行後,a[0][0] 所在的 block 仍在 cache 中,因此接著讀第二、三、四行皆會 hit,而第一行全部 miss。接著每 4行一組皆如此,因此可知 **miss rate = 25%**
**sumC**
在第一輪讀取第一、二行的時候 :
miss : a[i][j]、a[i+1][j]
hit : a[i][j+1]、a[i+1][j+1]
在第二輪讀取第三、四行的時候 : all hit,原因同 sumB
一樣每4行一組都是這樣,所以 **miss rate = 25%**