---
title:
image:
description:
tags: NCKU Class , 進階電腦系統理論與實作 , 2020q3
---
# 2020q3 Homework2 (quiz2)
contributed by < `TsenEN` >
> [題目](https://hackmd.io/@sysprog/2020-quiz2)
> [作業要求](https://reurl.cc/r85ko4)
## 測驗 1
作業介紹了 7 位碼 `ASCII` 是以 7 位元二進位數字進行編碼,可表示 128 個字元,第 128~255 號為擴展字元 (`extended ASCII`)。
既然採用 `7 bits`,則必會是以下表現方式 (`1 bytes = 8bits` , 這邊以 `8 bits` 說明)
| Value |
| ----- |
|00000000|
|00000001|
|........|
|01111111|
```graphviz
digraph {
rankdir=LR;
node [shape=none];
edge [arrowhead=vee,headclip=false, tailclip=false]
MSB[shape=plaintext,fontcolor=red]
LSB[shape=plaintext,fontcolor=blue]
A [label=<
<TABLE BORDER="0" CELLBORDER="1" cellspacing="0">
<TR>
<TD PORT="M">0</TD>
<TD PORT="">1</TD>
<TD PORT="">1</TD>
<TD PORT="">0</TD>
<TD PORT="">0</TD>
<TD PORT="">0</TD>
<TD PORT="">0</TD>
<TD PORT="L">1</TD>
</TR>
</TABLE>>];
{
node [shape=record];
edge [arrowhead=vee,headclip=true, tailclip=true]
rank=R;
MSB -> A:M;
}
{
node [shape=record];
edge [arrowhead=vee,headclip=true, tailclip=true]
rank=same;
LSB -> A:L;
}
}
```
我們可以很簡單觀察到一個特性是 `MSB` 都是 `0`,這是一個很重要的資訊!因為如果今天我們想判斷這個 1 個 `Bytes` 是不是 `ASCII 碼`,我只需要 `0X80` 來判斷!
```c=1
if (str[i] & 0x80) /* i.e. (unsigned) str[i] >= 128 , (0x80) = 0b10000000*/
```
今天 `str[i]` 去 `& 0x80` 只要 `MSB` 是 `0` ,`if` 就不會成立因為它就是 `ASCII 碼`。
所以來回答當我們要一次比對 64 位元的資料 (即 word size), 這個 MMM 要是什麼 ?
很簡單就是 `0x8080808080808080` ,因為 `1 bytes = 8bits` 所以 `64 bits` 是 `8 Bytes`, `1 Bytes` 需要一個 `80` ,八個那就 `8080808080808080`。
### `MMM`
- [ ] `(a) 0x0080008000800080`
- [ ] `(b) 0x8000800080008000`
- [ ] `(c) 0x0808080808080808`
- [x] `(d) 0x8080808080808080`
- [ ] `(e) 0x8888888888888888`
- [ ] `(f) 0xFFFFFFFFFFFFFFFF`
### 延伸問題
待整理
* [可不可以用 casting 的方法來取得 payload 呢?](https://hackmd.io/@RinHizakura/SJ5NuIANP)
* [C 標準庫源碼解析:內存拷貝與字符串拷貝](https://blog.xiocs.com/archives/181/)
* [ARM immediate value encoding](https://alisdair.mcdiarmid.org/arm-immediate-value-encoding/)
* [Barrel Shifter](https://reurl.cc/av4aeD)
## 測驗 2
開發解析器 (parser) 時,常會將十六進位表示的字串 (hexadecimal string) 轉為數值
```cpp=
uint8_t hexchar2val(uint8_t in)
{
const uint8_t letter = in & MASK;
const uint8_t shift = (letter >> AAA) | (letter >> BBB);
return (in + shift) & 0xf;
}
```
> 已知 `in` 一定隸屬於 '0', '1', '2', `…`, '9' 及 'a', 'b', 'c', …, 'f' (小寫) 及 'A', 'B', 'C', …, 'F' (大寫) 這些字元。預期 `hexchar2val('F')` 和 `hexchar2val('f')` 回傳 15,且 `hexchar2val('0')` 回傳 0,其他輸入都有對應的數值。
`'0'`, `'1'`, `'2'`, `…`, `'9'` 對應到 `0x30`, `0x31`, `0x32`, `…` `0x39`
`'a'`, `'b'`, `'c'`, `…`, `'f'` `(小寫)` 對應到 `0x61`, `0x62`, `0x63`, `…`, `0x66`
`'A'`, `'B'`, `'C'`, `…`, `'F'` 對應到 `0x41`, `0x42`, `0x43`, `…`, `0x46`
先列出 `in` Domain 裡的數值來找特性
| 字元 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
| ----- | --- | ----- | --- | --- | --- | --- |:--- |:--- |
| `'0'` | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| `'9'` | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
| `'a'` | 0 | ==1== | 1 | 0 | 0 | 0 | 0 | 1 |
| `'f'` | 0 | ==1== | 1 | 0 | 0 | 1 | 1 | 0 |
| `'A'` | 0 | ==1== | 0 | 0 | 0 | 0 | 0 | 1 |
| `'F'` | 0 | ==1== | 0 | 0 | 0 | 1 | 1 | 0 |
`0~9` 就直接是要轉換的數值,再去觀察 `return (in + shift) & 0xf;` ,`& 0xf` 代表 `b3~b0` 會 `return` 回去,這意味著 `shift` 要是 `0b00000000` 。
`a~f` 、 `A~F` 只有 `b6` 會是一樣並且 `0~9` 的 `b6 = 0`,所以可以看到 `letter` 是用來區分英文與數字的,那這樣就很明確 `b6` 會是關鍵,因為它可以區分兩者,故選 `(c)`。
### `MASK`
- [ ] `(a) 0x10`
- [ ] `(b) 0x20`
- [x] `(c) 0x40`
- [ ] `(d) 0x60`
- [ ] `(e) 0x80`
再來我們觀察 `a~f` 、 `A~F` 的 `b3~b0` 數值區間是 `1~6` 而不管大小寫 `a~f` 是 `10~15` ,這代表我們要加 `9` ,所以我們可以利用 `letter` 配出來,因為 `letter` 是 `2^6^` 所以我們 right shift `3 bits`(等於除以 `2^3^`)得到`8 = 0b00001000` 再 right shift `6 bits`(等於除以 `2^6^`)得到`1 = 0b00000001` 再 `or` 就可以得到 `9 = 0b00001001`。
### `AAA`
- [x] `(a) 3`
- [ ] `(b) 2`
- [ ] `(c) 1`
- [ ] `(d) 0`
### `BBB`
- [ ] `(a) 7`
- [x] `(b) 6`
- [ ] `(c) 5`
- [ ] `(d) 4`
## 測驗 3
建議可以先看延伸問題的 [src/div.c](https://github.com/jemalloc/jemalloc/blob/dev/src/div.c) 的註解
### Magic number M
Suppose $n = q·d$ for some integer $q$, we have
\begin{align}
\newcommand{\floor}[1]{\lfloor{#1}\rfloor}
\newcommand{\ceil}[1]{\lceil{#1}\rceil}
\newcommand{\naturals}[]{\mathbb{N}}
&\floor{\ceil{\frac{2^k}{d}} \cdot \frac{n}{2^k}} \\
=& \floor{\frac{2^k+r}{d} \cdot \frac{n}{2^k}} \\
=& \floor{\ ( \frac{2^k}{d}\ + \frac{r}{d}\ ) \cdot \frac{n}{2^k}} \\
=& \floor{\frac{n}{d}\ + \frac{r}{d} \cdot \frac{n}{2^k} } \\
=& \frac{n}{d}\ +\ \floor{\frac{r}{d} \cdot \frac{n}{2^k} } \\
\end{align}
where $2^k+r = q'·d$ for some integer $r,q'$ and $r<d$. Because $r = -(2^k) mod$ $d$ ,so $\frac{r}{d} <1$, if we select $k$ such that $\frac{n}{2^k} < 1$, we'll have $\frac{r}{d} \cdot \frac{n}{2^k} < 1$ and
\begin{align}
&\frac{n}{d}\ +\ \floor{\frac{r'}{d} \cdot \frac{n}{2^k} } = \frac{n}{d}\
\end{align}
上面這段數學推導給出了一個很棒的想法,**只要今天 n 是被 d 整除且我們選擇的 k 夠大**我們就可以用 $\ceil{\frac{2^k}{d}}$ 當作 magic number $M$。**要做除法時就做 $n \cdot M \cdot \frac{1}{2^k}$ 即可**,對應到 c 語言中,即是乘法與位移兩個動作。
### Back the test 3
並且我們可以應用上面證明的方式來回答測驗 3 , 先來看看數學推導。
Suppose $n = qd + r$ for some $q$ , $0\le r<d$ . Then, we have
\begin{align}
&\floor{\ceil{\frac{2^N}{d}} \cdot n \cdot \frac{1}{2^N}} \\
=& \floor{\frac{2^N+r'}{d} \cdot n \cdot \frac{1}{2^N}} \\
=& \floor{\frac{n}{d}\ +\ \frac{r'}{d} \cdot \frac{n}{2^N} } \\
=& \floor{q + \frac{r}{d}\ +\ \frac{r'}{d} \cdot \frac{n}{2^N} } \\
=&\ q\ +\ \floor{\frac{r}{d}+\frac{r'}{d} \cdot \frac{n}{2^N} }.
\end{align}
$r' = -(2^k) mod$ $d$
The first and second equality comes from the same reasoning in the jemalloc case. The third equality comes from the fact that $\frac{n}{d} = q+\frac{r}{d}$. The final equality stands because $q$ is an integer.
The final line equals to $q$ only if **$\frac{r}{d}+\frac{r'}{d} \cdot \frac{n}{2^N} < 1$** 得到這個重要資訊,先停下來。
先來推理程式給我們的資訊。
```cpp=1
const uint32_t D = 3;
#define M ((uint64_t)(UINT64_C(XXX) / (D) + 1))
/* compute (n mod d) given precomputed M */
uint32_t quickmod(uint32_t n)
{ uint64_t quotient = ((__uint128_t) M * n) >> 64;
return n - quotient * D;
}
```
預期 `quickmod(5)` 會得到 `2` , `quickmod(55)` 得到 `1`, `quickmod(555)` 得到 `0` (555 是 3 的倍數)。
看得出來是要取餘數 ! `return n - quotient * D` 透過剛剛提示看得出來是要回傳餘數,我們可以思考一下得出,$n$ $mod$ $d$ $= n − (n/d)×d$,所以`quotient`就是 $(n/d)$ 。
而 $(n/d)$ 這個除法看了很討厭,所以我們就要把他轉成乘法與位移的處理 $\dfrac{n}{d}=n\times\dfrac{\dfrac{2^N}{d}}{2^N}$ ,看到 `>> 64` 不難看出是除以 2^64^ 所以 `N = 64`,但對應的$M卻不等於 \dfrac{2^N}{d}$ 而是 $M=\dfrac{2^N-1}{d} + 1$ (這是因為答案只有 `0xFFFFFFFFFFFFFFFF` $=2^{64}-1$ ) 因此要證明兩者最終結果是相等的。
### 推導 `0xFFFFFFFFFFFFFFFF` 是否符合等式
$\ceil{\dfrac{2^N}{d}} = \floor{\dfrac{2^N-1}{d}} + 1, ( 2^N,d > 0 ) , 令 2^N = q·d + r ,0\le r<d.$
Here we need to consider two case
$r = 0$
\begin{align}
&\ceil{\frac{2^N}{d}} \\
=&\ceil{\frac{q·d}{d}}\\
=& q
\end{align}
---
\begin{align}
&\floor{\frac{2^N-1}{d}}+1 \\
=&\floor{\frac{2^N-1}{d}+1} \\
=&\floor{\frac{q·d-1}{d}+1} \\
=&\floor{\frac{q·d-1+d}{d}} \\
=&\floor{\frac{(q+1)d-1}{d}} \\
=&\floor{\frac{(q+1)d}{d}\ - \frac{1}{d}} \\
=&\floor{q+( 1\ - \frac{1}{d})} \\
=& q
\end{align}
$r > 0$
\begin{align}
&\ceil{\frac{2^N}{d}} \\
=&\ceil{\frac{q·d+r}{d}}\\
=&\ceil{\frac{q·d+r}{d}}\\
=&q+\ceil{\frac{r}{d}}\\
=&q+1
\end{align}
---
\begin{align}
&\floor{\frac{2^N-1}{d}}+1 \\
=&\floor{\frac{2^N-1}{d}+1} \\
=&\floor{\frac{q·d+r-1}{d}+1} \\
=&\floor{\frac{q·d+r-1+d}{d}} \\
=&\floor{\frac{(q+1)d+r-1}{d}} \\
=&\floor{\frac{(q+1)d}{d}\ + \frac{r-1}{d}} \\
=&\floor{q+1\ - \frac{r-1}{d}} \\
\end{align}
因為 $1\le r<d$,所以 $0 \le \frac{r-1}{d}<1$,這樣就得到
\begin{align}
&\floor{q+1\ - \frac{r-1}{d}} \\
=&\floor{q+1} \\
=&q+1 \\
\end{align}
由上述得證對於數學推導有疑問可看這篇 [C++中 整數除法 向上取整的數學證明](https://reurl.cc/A84yge),數學符號 [LATEX 語法筆記](https://reurl.cc/odrZ9j)
好所以很棒我們整合前面的一堆推導
\begin{align}
&\floor{\ceil{\frac{2^N}{d}} \cdot n \cdot \frac{1}{2^N}} \\
=&\floor{ \floor{\dfrac{2^N-1}{d}} + 1 \cdot n \cdot \frac{1}{2^N}} \\
=&\ q\ +\ \floor{\frac{r}{d}+\frac{r'}{d} \cdot \frac{n}{2^N} } \\
\end{align}
我們需要回去討論 **$\frac{r}{d}+\frac{r'}{d} \cdot \frac{n}{2^N} < 1$**,已知 $r,r' < d ,因為都是 mod$ $d$,而因為`uint32_t n` 所以 $n$ 被限制最大表示只能到 $2^{32} -1$ 又 $N = 64$ 可以得到 $\frac{n}{2^N} < 1$,$\frac{n}{d} = q+\frac{r}{d}$ ($\frac{r}{d}$ 會被程式捨掉)所以 $\frac{n}{d} = q$ 恭喜我們可以選答案了 !
### `XXX`
- [ ] `(a) 0x00F000F000F00080`
- [ ] `(b) 0xF000F000F000F000`
- [ ] `(c) 0x0F0F0F0F0F0F0F0F`
- [ ] `(d) 0xF0F0F0F0F0F0F0F0`
- [ ] `(e) 0x8888888888888888`
- [x] `(f) 0xFFFFFFFFFFFFFFFF`
## 測驗 4
延伸測驗 3,我們想判斷某個數值能否被指定的除數所整除,在 D 和 M 都沿用的狀況下,程式碼如下:
```c
bool divisible(uint32_t n)
{
return n * M <= YYY;
}
```
以 D = 3 來說,divisible(7) 要得到 0 (即 7 無法被 3 整除),divisible(87) 要得到 1 (即白痴是三的倍數)