contributed by < ZhuMon
>
linux2020
在 queue.[c/h]
中完成以下實作
q_new
: 建立新的「空」佇列;q_free
: 釋放佇列所佔用的記憶體;q_insert_head
: 在佇列開頭 (head) 加入 (insert) 給定的新元素 (以 LIFO 準則);q_insert_tail
: 在佇列尾端 (tail) 加入 (insert) 給定的新元素 (以 FIFO 準則);q_remove_head
: 在佇列開頭 (head) 移除 (remove) 給定的元素。q_size
: 計算佇列中的元素數量。q_reverse
: 以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列元素,換言之,它只能重排已經存在的元素;q_sort
: 以遞增順序來排序鏈結串列的元素queue.h
中的 queue_t
,使得 q_size()
以及 q_insert_tail()
能以 時間完成queue.h
中的 queue_t
增加成員 (size
)q_size()
藉由直接讀取 queue_t
中的 size
,不必每次重新計算 queue 的大小tail
指標,也讓 queue 所需的記憶體空間增大 sizeof(pointer) + sizeof(int)
q_new
queue_t
前,先確認是否成功分配記憶體,避免操作到空指標q_free
tmp
作為釋放用的節點q->head
的移動,遍歷整個 queue
,並一一清除本來想以
q->head
與q->tail
是否相同作為結束迴圈的條件,後來發現這樣無法完全將queue
清除,於是改為判斷q->head
與q->tail
是否同時存在,又發現只要判斷q->head
是否存在即可
q_insert_head
list_ele_t
) 放入 queue 的前端q
是否為 NULL
,避免分配記憶體後才發現 q
為 NULL
,造成 memory leaknewh
清除string.h
> 的 strncpy
複製 s
,放入新元素的 value
strncpy
不會自動將其他位置設為 \0
,所以使用 memset
將 newh->value
歸零q->tail
能夠正常運作,第一個 element 建立時,將 q->tail
指向 q->head
,且隨著新增元素,位移到最後q_insert_tail
q_insert_head
差不多list_ele_t
) 放入 queue 的尾端queue
為空 (q->tail == NULL
),則將 head
和 tail
同時指向新元素 (newt
)q_remove_head
q->head
是否為 NULL
,確認 queue
是否為空sp
不為 NULL
,必須將成功刪除後的字串複製進去,並且依照傳入的 bufsize
決定複製多少
bufsize
or 要刪除的字串長度 比較短,來決定真正要複製進 sp
的長度為何memset
時,需要保留最後一位來放 \0
,所以 realbufsize
要加一bufsize
),由於呼叫 memset
會將 size += 1,而且複製完不能超過 bufsize
,所以 realbufsize = bufsize - 1
tmp
以供釋放 (free
)tmp
的成員都清空後,再釋放 tmp
q_size
q
不存在,則返回 0
q->size
,達成在 時間內完成 q_size()
q_new
時,便已將 q->size
設為 0
,因此就算 queue 為空,還是會返回 0
q_reverse
prev
, now
, next
) 來反轉 queueq->tail->next
和 q->head->next
,取代新的指標,只使用一個新的指標 tmp
q->size < 2
來確認 queue
是否只有一個或沒有元素tmp
指向下一個,避免待會將 b-next
反向後,無法取得 c
q->head->next->next
,將 b->next
反向,指向 a
q->head->next
前進,而不至於讓 b
無法取得,於是將 tail->next
指向 b
q->head->next
指向 tmp
,準備下次的反向while (tmp != q->tail) { tmp = tmp->next; q->head->next->next = q->tail->next; q->tail->next = q->head->next; q->head->next = tmp; }
while (tmp != q->tail) { tmp = tmp->next; q->head->next->next = q->tail->next; q->tail->next = q->head->next; q->head->next = tmp; }
考慮以下變更:
要點: 維持簡潔的縮排和風格
已更改程式碼為只剩一個新指標
q_sort
merge_sort()
l1
, l2
) 不同速度的前進l1
會指向最後的 element
l2
會指向中間的 element
l1
指向 l2->next
後,便可以得到兩個等長的 linked list
(head
和 l1
)l2
指向 *head
,避免接下來使用 *head
後造成錯誤merge
,將比較字串的部分縮進 while
l1
和 l2
,並比較兩者元素,來建立新 Linked listwhile
不是剩下 l1
就是 l2
,於是縮成一行q_sort()
q->head
TODO:
沒有注意到參考的程式碼與當前的命名風格不同,已改正
改寫程式碼
merge()
以及 mergeSortList()
合併為 merge_sort()
while
迴圈內,但是還想不到辦法實現(更新)
while
strcmp
改為 strnatcmp
發現在執行
traces/trace-15-perf.cmd
時,會因為時間超過,而測試失敗
strcmp
與 strnatcmp
的需要時間。以 time.h
的 clock
函式計算時間
此為未使用任何 gcc 最佳化方法 (-O1 -O2 -O3 …) 的狀況
strnatcmp
需要的時間在十萬次以上的情況下,與 strcmp
差距極大,造成 qtest
Timeout很好!你終於發現這個故意安插的細節。
需要留意到,glibc 裡頭的 strcmp 做了一系列最佳化,類似 Speeding up string compare and sorting 的手段,但額外透過 SSE 和 AVX 指令集加速。相反地,natsort 實作就很 naive (取「原始」和「單純」的意思),意味著有很大的加速空間,你可試著改進,記得對照 CS:APP 第 5 章 來思考。
經由老師提示,我試著去 linux kernel 內尋找有使用到 natural sort 的部分,一開始我認爲應該跟 file system 有關,於是聚焦在 linux/fs,粗略地尋找了一陣子卻沒有找到有關的資訊,但是在搜尋的過程中有發現
sort
這個命令
sort
只能被稱「命令」,請變更用語;i-node
,sort
就仰賴 metadata 進行排序,所以,討論的主體該釐清,不是 Linux 核心去「排序」檔案名稱,而是 coreutils 一類的套件提供 sort
工具來排序;
- 好的,已更改用語
- 也就是說我一開始的找尋方向錯誤,Linux 核心不負責檔案的排序,「人類」看到的檔案系統順序都是由 coreutils 等套件來排序,所以我的確應該去閱讀 coreutils 的
sort
命令
不能說「方向錯誤」,有好奇心是值得稱許的事。需要注意,排序不全然跟 Linux 核心無關,可參見 The kernel and character set encodings,在核心內部的檔案系統實作,我們還是需要透過核心處理字元集 (如 Big5 [廣泛用於台灣], GB18030 [中國國家標準]),這樣後續在使用者層級的排序才有意義。
sort
這個命令是否符合 natural sort
sort
命令
後來發現應該使用
$ sort -V
來做排序
sort
命令的 man page 後,發現, sort
可以針對版本號碼做排序
效果符合 sourcefrog 的預期sort
命令 的運行方式後,發現 sort.c
關於檔案、版本號碼的排序是由 filevercmp 這個檔案來實作-O3
)strcmp 所需時間一直只在 1 µs 附近,或許更低,但目前使用的 library 精準度只到微秒
gnulib
比較字串的方式效率也不盡人意後,決定先熟悉編譯器的最佳化等教材,之後再試著改進運用 Valgrind 排除 qtest 實作的記憶體錯誤,並透過 Massif 視覺化 “simulation” 過程中的記憶體使用量,需要設計對應的實驗
使用 make valgrind
命令後,想要讓使用 massif
來視覺化記憶體使用量,可是卻發生錯誤
由於在 macOS 和 Linux 都遇到相同問題,確認兩者的版本
查詢 Valgrind 官網 發現,最新版本的確是 3.15.0
重新閱讀 lab0-c 的 README.md
後,發現在 .valgrindrc
中有關於 Valgrind 的配置,並且找到該選項,將它刪除後,便可運作
發現 Valgrind 的另外一個配置: --leak-check
在 .valrindrc
裡的寫法為 --memcheck:leak-check=full
,於是將 --show-leak-kinds=all
前也加上 memcheck
,再執行,便通過了
請提交 .valrindrc
相關的 pull request
已提交
研讀論文 Dude, is my code constant time?,解釋本程式的 “simulation” 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 Student’s t-distribution 及程式實作的原理;
dudect
解釋 select 系統呼叫在本程式的使用方式,並分析 console.c 的實作,說明其中運用 CS:APP RIO 套件 的原理和考量點。可對照參考 CS:APP 第 10 章重點提示
為避免「舉燭」,請比照 qtest 實作,撰寫獨立的終端機命令解譯器 (可建立新的 GitHub repository)Image Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
select()
可以監聽多個 file descriptors,發現新通過的 Pull request 含有一些小bug,進行修改
已發 Pull Request,也已通過
Extend auto-indention for generic C/C++ source (#29)
簡介:新增 *.c 以及 *.hpp,使得 C/C++ 的檔案都適用自動排版
由於我的主系統是 macOS,他這個方法需要找到 clang-format.py
的位置,再取代 <path to your clang-format.py>
,避免以後還需要再手動更新,於是寫了一個 script 將路徑寫好,並且 export
成環境變數
.vimrc
clang-format.py
的位置以 $CLANG_FORMAT_PATH
取代uname
命令,找出當前是在哪個作業系統setEnv.sh
的資料夾裡,也有複製了老師的 .clang-fomat
檔案,所以之後不管在哪裡都會使用該設定進行排版~/.zshrc