or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Syncing
xxxxxxxxxx
Lab0
lib/list_sort.c
學習筆記lib/list_sort.c
出於好奇,我會將這些
list_sort.c
的問題,對 ChatGPT 進行提問,並將對話附在下方,最後再做比較及總結研讀過程
merge
這段程式可以從函式名稱得知用途是合併兩條鍊結串鍊,並且會使用函式指標
cmp
進行大小的比較,可以特別的看到前面使用了一個特別的巨集__attribute__((nonnull(2,3,4)))
可以從Is attribute((nonnull)) standardized in C 知道它並不是 C 語言標準庫內的規範,而是特定於像是 GCC/Clang 這類的編譯器內的規範。我們可以透過 6.30 Declaring Attributes of Functions 得知他的定義為指定這個函是的參數應該要是非空指標 (non-null pointer
),當中的數字是用來指定第幾個參數要為非空指標,若是沒有參數的話,就是預設所有的參數都是非空指標畢竟這邊是 linux kernel 的部份,不免俗的使用
indirect pointer
,來進行 list 的走訪,詳情請見 你所不知道的 C 語言: linked list 和非連續記憶體上面的
for-loop
進行的是兩條鍊結串列中每個值的大小比較,可以發現假如a
跟b
所指向的值是相同的時候,會選擇將a
排入,這麼做是為了排序是stable sorting
,並且當a
或是b
其中一方到達tail
後,它會直接將另一條接在結束的那條鍊結串列後。merge_final
merge_final
的實作原理與merge
其實很像,只是他是在合併最後一個list
的時候才會使用到的,因此它額外多了一個函式參數struct list_head *head
,此時就是把所有比較後的東西都接在head
的後面。根據上面所述, linux kernel 開發者應該是認為剩下的
b
都是已經排序過的,因此不太需要在花時間比較b
的值了,因此,才會使用一個uint8_t count = 0
的變數來確定是否要進行比較,並且在之後使用運算子前置 ++ 來讓第一次是unlikely
而若是有++count
的話,就會變成!unlikely
,最後是把b
全數加入後,將頭尾相連形成環狀之後完成__built_except()
是 GCC 編譯器所提供的函式,用來將 branch 的相關資訊提供給 Compiler ,告訴 Compler 開發者預期的結果,方便他們對程式碼改變分支預測的順序來進行優化。至於為何使用!!(x)
這樣的語法,是因為透過兩次的 Not operator (!) 可以確定結果必定為 0 或是 1 。至於會需要這樣的巨集與 Cache 有關,可以知道當 cache 未命中時會從 Memory 搬移一個區塊到 cache 中,而這樣會使得鄰近的程式碼也一起被搬移,進而增加 Cache 命中的機會。因此這個巨集就是將比較可能的部份往前移,比較不可能的部份往後面移動,此部份與老師的提示相吻合。
list_sort
首先先從註解理解程式
它首先解釋了合併以及未合併的個數比例要是2a:1a,並且每a條 sublist 的節點個數為 \(2^k\) 個,所以就可以知道總數會是 \(3*2^k\) 個節點。這麼做的原因是他要將這些點都放進 L1 cache ,這麼做可以避免 cache thrashing。
count 為 pending lists 中節點的數量,在 count 變 count + 1 後,可以觀察到第 k 個 bit 會改為 1,0 ~ k - 1 個 bit 會變 0,此時會將 2 個 \(2^k\) 個節點合併,變成 \(2^{k+1}\) 然後留下一個 \(2^k\)
這邊的註解主要在說明上述的部分重複兩次後,就會有兩條 \(2^{k+1}\) 可以合併成 \(2^{k+2}\) ,如此一來就可以保持只有兩條 pending lists
這邊主要也有不合併的情況,當 count + 1 後是 \(2^k\) 的時候(也就是轉成二進制時最高位數是1,其他的位數都是 0 的情況),就不會合併
接著開始探討程式碼的部分
可以看到可以看到他先檢查了鏈結串列的狀況,以及將其變成非環狀的鏈結串列
可以從註解看到他要找到的是 count 中最低位的 clear bit ,並且從 for-loop 的迭代
bits >>= 1
驗證是在找二進制的最低位數這邊使用到了上面所出現過的 likely 巨集,應該也是為了減少 branch penalty 所做的優化,如果 bits 在上一個步驟的結尾變成了 0 的話,就不會進行合併,而如果是 1 的話,就會進到這個 if 敘述句並且合併
先從註解看起,可以知道他是要將一個節點從 input list 移動到 pending list 中,所以他是把 list 接到 pending 的後面,並且 list 迭代一個,再把 count++
這邊是要把所有的 pending lists 合併在一起,最後再使用
merge_final
將頭尾連結在一起。TODO
用 graphviz 可視化上述的過程
ChatGPT 版本
與 chatgpt 的對話會是對話式的,
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn 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
Learn 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
Learn 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
Learn More →參考