# 2022q1 Homework3 (fibdrv)
contributed by <`curlyw819` >
###### tags: `linux2022`
## **大數運算**
:::danger
中英文間用一個半行空白字元或標點符號區隔。
:notes: jserv
:::
由於 Fib(92) 開始的計算會有 overflow 的問題,因此需要使用到大數運算的方法。原則上是直接使用字元運算並以陣列的形式來儲存,藉此來解決 overflow 的問題。
### **資料結構**
使用結構來儲存字元與操作所需的資訊,定義資料結構如下
```c
typedef struct {
char decimal[500];
int length;
} bignum;
```
* `decimal[size - 1]` = `msb`,`number[0]` = `lsb`
* `length`用來儲存數字位數
#### **大數加法**
* 運算的過程使用ASCII來使數字以字元的方式直接做相加,所以兩數相加後要再減去多加的48
* 從個位數開始相加並且考慮進位,也就是說MSB為個位數
```c
if (x->length < y->length)
return bn_add(y, x, dest);
```
* 以上程式碼為了計算方便,使`x->length` >= `y->length`
```c
void bn_add(bignum *x, bignum *y, bignum *dest)
{
if (x->length < y->length)
return bn_add(y, x, dest);
int carry = 0;
for (int i = 0; i < y->length; i++) {
dest->decimal[i] = y->decimal[i] + x->decimal[i] + carry - OFFSET;
if (dest->decimal[i] >= 10 + OFFSET) { // OFFSET = 48
carry = 1;
dest->decimal[i] -= 10;
} else
carry = 0;
}
for (int i = y->length; i < x->length; i++) {
dest->decimal[i] = x->decimal[i] + carry;
if (dest->decimal[i] >= 10 + OFFSET) {
carry = 1;
dest->decimal[i] -= 10;
} else
carry = 0;
}
dest->length = x->length;
dest->decimal[dest->length] = carry + OFFSET;
if (carry)
dest->length++;
dest->decimal[dest->length] = '\0';
}
```
第一個 `for` 是做字元的相加並考慮進位,結果放入 `dest`,第二個for是將較長的數字 `x` 剩餘的部分直接放入 `dest`
#### **新增數字**
```c
#define bn_tmp(x) bn_new(&(bignum) { .length = 0 }, x)
bignum *bn_new(bignum *bn, char decimal[])
{
bn->length = strlen(decimal);
memcpy(bn->decimal, decimal, bn->length);
return bn;
}
```
#### **fib_without_fast_doubling**
這邊的`fib`是沒有使用`fast_doubling`加速過的,且使用不會用到陣列的計算方式
```c
bignum *fib_sequence_org(int k)
{
int i;
bignum *f0 = bn_tmp("0");
bignum *f1 = bn_tmp("1");
bignum *ans = bn_tmp("0");
if(k == 0)
return f0;
if(k == 1)
return f1;
for(i = 2; i <= k; i++){
bn_add(f0, f1, ans);
f0 = f1;
f1 = ans;
}
return ans;
}
```
## **效能量測**
第一階段我主要想測量在沒有`fast_doubling`的原始fib
量測結果尚未排除作業系統排程的干擾
### **量測user space 和 kernel space 間傳輸使用copy_to_user的傳遞時間**
* 使`fibdrv`中的`fib_read`回傳`fib_sequence_org`在`kernel space`運算費式數列的時間
* 量測`client`中`user space`呼叫`read`前後之時間差
* 將`kernel space`與`user space`量到的時間做相減,可得到`copy_to_user`的傳遞時間
#### **kernel space read**
* 在`kernel space`使用`ktime`
```c
static ssize_t fib_read(struct file *file,
char *buf,
size_t size,
loff_t *offset)
{
ktime_t k1, k2;
k1 = ktime_get();
char *rev_fib = fib_sequence_org(*offset)->decimal;
int length = strlen(rev_fib);
char result[MAX_DIGIT];
for (int i = 0; i < length; i++)
result[i] = rev_fib[length - 1 - i];
result[length] = '\0';
copy_to_user(buf, result, length + 1);
k2 = ktime_sub(ktime_get(), k1);
return (long long)ktime_to_ns(k2);
}
```
#### **user space read**
* 在`user space`使用`clock_gettime`
```c
long elapse(struct timespec start, struct timespec end)
{
return ((long) 1.0e+9 * end.tv_sec + end.tv_nsec) -
((long) 1.0e+9 * start.tv_sec + start.tv_nsec);
}
```
由於量測結果很小,所以先將其放大並增加精確度再相減
```c
for (int i = 0; i <= offset; i++) {
lseek(fd, i, SEEK_SET);
clock_gettime(CLOCK_REALTIME, &t1);
sz = read(fd, buf, 500);
clock_gettime(CLOCK_REALTIME, &t2);
printf("%d ",i);
printf("%lld ",elapse(t1,t2) - sz);
printf("%ld ", elapse(t1, t2));
printf("%lld \n",sz);
}
```
#### gnuplot
![](https://i.imgur.com/lACyf5F.png)
將量測出的時間用gnuplot建成圖表
* 隨著`size`增加,傳輸的資料量也會增加,但是耗費的時間卻幾乎相同