# 2018q3 Homework2 (lab0)
contributed by < happyincent >
## Environment
#### VM
```
$ cat <(echo) <(echo "CPU: " `lscpu | grep "Model name" | cut -d' ' -f12-17`) <(echo "OS: " `lsb_release -d | cut -f2`) <(echo "Kernel: " `uname -a | cut -d' ' -f1-3,14`) <(echo "gcc: " `gcc --version | head -n1`) <(echo "ldd: " `ldd --version | head -n1`) <(echo "Make: " `make --version | head -n1`)
CPU: Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz
OS: Ubuntu 18.04.1 LTS
Kernel: Linux xubuntu 4.15.0-36-generic x86_64
gcc: gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
ldd: ldd (Ubuntu GLIBC 2.27-3ubuntu1) 2.27
Make: GNU Make 4.1
```
#### RPi
```
$ cat <(echo) <(echo "CPU: " `lscpu | grep "Model name" | cut -d' ' -f14-18`) <(echo "OS: " `lsb_release -d | cut -f2`) <(echo "Kernel: " `uname -a | cut -d' ' -f1-3,12`) <(echo "gcc: " `gcc --version | head -n1`) <(echo "ldd: " `ldd --version | head -n1`) <(echo "Make: " `make --version | head -n1`)
CPU: ARMv7 Processor rev 4 (v7l)
OS: Raspbian GNU/Linux 9.4 (stretch)
Kernel: Linux raspberrypi 4.14.52-v7+ armv7l
gcc: gcc (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516
ldd: ldd (Debian GLIBC 2.24-11+deb9u3) 2.24
Make: GNU Make 4.1
```
## Makefile
* 加上 `-std=gnu11`、嘗試 Implicit Rules、加上 dependency
```diff
.PHONY: dep test clean
CC = gcc
CFLAGS = -O0 -g -Wall -Werror -std=gnu11
GIT_HOOKS := .git/hooks/applied
all: $(GIT_HOOKS) qtest
$(GIT_HOOKS):
@scripts/install-git-hooks
@echo
# queue.o: queue.c queue.h harness.h
# $(CC) $(CFLAGS) -c queue.c
# qtest: qtest.c report.c console.c harness.c queue.o
# $(CC) $(CFLAGS) -o qtest qtest.c report.c console.c harness.c queue.o
queue.o: queue.c queue.h harness.h
qtest: qtest.c report.c console.c harness.c queue.o
dep:
@$(CC) -MM *.c
@$(CC) -M *.c > depend
test: qtest scripts/driver.py
scripts/driver.py
clean:
rm -f *.o *~ qtest depend
rm -rf *.dSYM
(cd traces; rm -f *~)
```
* `.PHONY`
* A phony target is one that is not really the name of a file. [[source](https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html#Phony-Targets)]
* `CFLAGS` [[source](https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html)]
* `-O0`: 關閉優化,default optimization level
* `-g`: Options for Debugging
* `-Wall`: 啟用容易被避免的 warnings
* `-Werror`: Make all warnings into errors.
* `-std=gnu11`
* gcc version 6.3.1 - 7.3.1: default is '-std=gnu11' [[source](https://stackoverflow.com/a/44057210)]
* GNU C Library 有 ISO C 也有 POSIX [[source](https://www.gnu.org/software/libc/manual/html_node/Introduction.html#Introduction)]
* 若用 `-std=c11` 會無法編譯
* `console.h`: unknown type name ‘fd_set’
* `fd_set` 定義在 <sys/select.h> 屬於 [C POSIX library](https://en.wikipedia.org/wiki/C_POSIX_library) 而非 ISO C
* `harness.c`: implicit declaration of function ‘random’ (‘sigsetjmp’)
* 同上述原因, random: <stdlib.h> ([BSD Random](https://www.gnu.org/software/libc/manual/html_node/Pseudo_002dRandom-Numbers.html)), sigsetjmp: <setjmp.h>
* `$(GIT_HOOKS)`
* target 設為 .git/hooks/applied
* 執行 `scripts/install-git-hooks` 時會 touch 檔案: `.git/hooks/applied`
* 第二次後 Make 時因為 `$(GIT_HOOKS)` 檔案已存在且這個 target 沒有 prerequisites 需要確認,因此 Make 會認為已經 up to date 便不會再執行
```
$ rm .git/hooks/applied
$ make clean all > /dev/null && ls -l .git/hooks/applied
-rw-rw-r-- 1 ddl ddl 0 Oct 3 02:40 .git/hooks/applied
$ make clean all > /dev/null && ls -l .git/hooks/applied
-rw-rw-r-- 1 ddl ddl 0 Oct 3 02:40 .git/hooks/applied
$ make .git/hooks/applied
make: '.git/hooks/applied' is up to date.
```
* install-git-hooks 在 [.git/hooks](https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks) 建立兩個在 commit 前會執行的 scripts
* pre-commit: clang-format (檢查格式)、cppcheck (檢查 bugs)
* commit-msg: 檢查 commit message ([git-good-commit](https://github.com/tommarshall/git-good-commit))
* a line starts with`@`: 不 echo 該行指令 ([Recipe Echoing](https://www.gnu.org/software/make/manual/html_node/Echoing.html#Echoing))
* `:=` : Simply expanded variables, `=` : recursively expanded variables,
`?=` : conditional variable assignment [[source](https://www.gnu.org/software/make/manual/html_node/Flavors.html)]
```make
A = $(B)
B = $(C)
C ?= World!
C ?= World!!!
X := Hello $(A)
Y = Hello $(A)
C = DDL!
all:
@echo $(X) ## Hello World!
@echo $(Y) ## Hello DDL!
```
* `all`
* default target ([Standard Targets](https://www.gnu.org/software/make/manual/html_node/Standard-Targets.html))
* `queue.o, qtest`
* `-c`: compile or assemble the source files, but do not link
* `-o file`: output in file *file*
* 如果 rule 中沒有 recipe,Make 會依據 target、prerequisites (the source files) 來決定 [Implicit Rules](https://www.gnu.org/software/make/manual/html_node/Using-Implicit.html#Using-Implicit) ([recursively](https://www.gnu.org/software/make/manual/html_node/Implicit-Rule-Search.html#Implicit-Rule-Search))
* [Built-In Rules](https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html#Catalogue-of-Rules): Compiling C programs、Linking a single object file
* `dep` [[source](https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html#Preprocessor-Options)]
* `-M`: output a rule suitable for make describing the dependencies of the main source file
* `-MM`: 找出非 include 系統且非間接 include 的 source file dependencies
```
$ make dep
console.o: console.c console.h report.h
harness.o: harness.c report.h harness.h
qtest.o: qtest.c harness.h queue.h console.h report.h
queue.o: queue.c harness.h queue.h
report.o: report.c report.h
```
* `test`
* prerequisites: qtest、scripts/driver.py
* 不帶參數執行 scripts/driver.py,依據 ./traces/*.cmd 執行 qtest 並算出分數
* `clean`
* 刪掉 make 出來的 .o、執行檔等
* `.dSYM`: [debug symbols](https://en.wikipedia.org/wiki/Debug_symbol)
* `*~`: 編輯器備份檔
## queue
* Queue Structure
```c
typedef struct {
list_ele_t *head; /* Linked list of elements */
list_ele_t *tail;
size_t q_size; /* Linked list's size */
} queue_t;
```
* 加入 `size_t q_size` 來達成在 O(1) 時間內得到 queue elements 的數目
* size_t: unsigned integer type
* `sizeof(long) = sizeof(size_t)` [[stackoverflow: size_t ](https://stackoverflow.com/questions/46508831/is-the-max-value-of-size-t-size-max-defined-relative-to-the-other-integer-type)]
* Limit of size_t type [[stdint.h](https://code.woboq.org/userspace/glibc/sysdeps/generic/stdint.h.html)]
* SIZE_MAX: 18446744073709551615 (64 bit), 4294967295 (32 bit)
```c
#include <stdio.h>
#include <stdint.h>
int main() {
printf("SIZE_MAX: %lu\n", SIZE_MAX);
size_t a = 18446744073709551615U;
printf("(%lu + 1) > 1: %s\n", a, (a + 1 > 1) ? "true" : "false");
}
```
```
$ gcc -Wall a.c && ./a.out
SIZE_MAX: 18446744073709551615
(18446744073709551615 + 1) > 1: false
```
* 加入 `list_ele_t *tail` 紀錄 queue 中 tail element 的位址
* q_insert_head
* [本來](https://github.com/happyincent/lab0-c/commit/291b8d95e8605b95dfd851a66d137e77ce8824c6)將 insert 的存放字串另外寫成 `static inline list_ele_t *ele_new(char *s)`,但發現會造成更多的 jmp (BL, BNE, ...) 次數
* gcc `-O0` (optimization level 0) 時可能不會展開 inline [[source](https://www.cnblogs.com/cnmaizi/archive/2011/01/19/1939686.html)] [[gcc-inline](https://gcc.gnu.org/onlinedocs/gcc/Inline.html)]
* 除非在 function 加上 **`__attribute__((always_inline))`** 強制做 inline
* q_reverse
```c
/* iterate the queue to reverse each next pointer */
list_ele_t *left = q->head;
list_ele_t *right = NULL;
list_ele_t *tmp = NULL;
q->tail = left;
while (left != NULL) {
tmp = left->next;
left->next = right;
right = left;
left = tmp;
}
q->head = right;
```
![](https://i.imgur.com/v7XVsYd.jpg)
## 比較不同環境的 Grade 分數
#### 結論: 我沒有能力把 C 寫到 -O0 和 -O1 一樣好
* 分別以 optimization level: `-O0` (下圖左)、`-O1` (下圖右) compile [queue.c](https://github.com/happyincent/lab0-c/blob/82d82fefa826be23ae11528d627a996043383b75/queue.c),並比較 IDA Pro disassemble 後 `q_insert_head` 的 graph 圖
* 在 **VM** 環境下 (gcc Ubuntu 7.3.0)
* `O0` 最多需 8 次 jmp
* `O1` 最多需 6 次 jmp
![](https://i.imgur.com/PgzDwJN.png)
* 在 **RPi** 環境下 (gcc Raspbian 6.3.0)
* `O0` 最多需 6 次 jmp
* `O1` 最多需 3 次 jmp
![](https://i.imgur.com/8wv0Iwq.png)
#### 執行結果
* 在 **RPi** 環境 `-O0`
* 從 trace 13 開始就會因為 ih 或 it 的次數較大發生 **`ERROR: Time limit exceeded`**
```
ddl@raspberrypi:~/lab0-c $ make clean all test
...
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient
ERROR: Insertion of dolphin failed (1 failures total)
--- trace-13-perf 0/7
...
```
* 使用 [grade.sh](https://github.com/happyincent/lab0-c/blob/master/grade.sh) 分別以 `-O0, -O1, -O2, -O3` 測試 10 次並計算平均分數 (timeout 10 秒 total = 0)
* **VM** 環境
```
ddl@xubuntu:~/Desktop/sysprog/lab0-c$ ./grade.sh
-O0 grade: 100
-O1 grade: 100
-O2 grade: 100
-O3 grade: 100
```
* **RPi** 環境
```
ddl@raspberrypi:~/lab0-c $ ./grade.sh
-O0 grade: 23
-O1 grade: 98
-O2 grade: 98
-O3 grade: 99
```