課堂問答簡記
請填入你的 GitHub 帳號名稱
linux/list.h
的 list_swap()
實做原理 ?entry2
的前後接上,其中 WRITE_ONCE()
可以防止賦值的指令被拆分成多個小指令,或是在多執行緒被其他函式調用時時造成錯誤。
#define WRITE_ONCE(var, val) (*((volatile typeof(val) *)(&(var))) = (val))
Line 15,16: 將 entry2
自 list 之中分離出來,經過 __list_del_entry_valid()
確認資料可用之後, 把當下 entry2
的 prev
以及 next
設為預設的 dead pointer (LIST_POISON1
以及 LIST_POISON1
),當調用 LIST_POISON
時會導致 Page Fault。
Line 20-23: 調整指標至如下形式
Line 31-36: 利用 list_add(new, prev, next)
將新節點插入 prev
及 next
之間的行為,將 entry1
加入至原先 entry2
所在地點。
Line 26,27 用以對應傳入的兩個 node 是 XXX
-> entry1
-> entry2
-> YYY
的情況。
if
與 else
兩個區段分別註解會更加清晰indirect
的目的無法直接望文生義TODO:弄清楚 qtest 如何檢驗效能
bool
本身一個 byte 大小的空間造成錯誤
bool
都改成 int
,但會需要連帶修改其他東西,不好維護xs_trim
如何比對欲移除的字元xs_trim
支援讓呼叫者自訂欲剪除的字元,因此若欲排除的字元太多使用逐一比對的話將造成執行效能低落,因此引入另外的方法。在程式碼中,可以看到一個長度為32的mask
陣列,mask
陣列的初始值為0,並且可透過兩個macro: set_bit
及check_bit
進行建表及查詢。在通常的執行環境中,單一個位元所站的空間為1個byte,而程式碼中mask
中元素的型態為uint8_t
,因此可以將所有不同的字元對應到mask
中的不同個bit,而這個工作由set_bit
完成,具體的內容如下:
可以發現到set_bit
將給定的byte
分成上5位元及下3位元,分別成為存取mask
的索引及其中的第幾個位元。以byte = 'A'
為例: A的ascii碼為0x41
,則存取mask
的索引為8,並且其對應到的是mask[8]
中的第2個字元(1 << 1 是2)。找到對應的位元後則透過or將該位元設成1。
表建好後,透過check_bit
在常數時間內查詢該表,便可得知該字元是否為欲刪除之字元,check_bit
的具體內容為:
由上可知,只要透過與set_bit
相同之方式找到mask
中對應的位元,並且確認該位元是否為1。
xs_new
要分兩種方式儲存短字串及長字串在xs_new
中是將長度小於 16 的字串放在 stack,並為長度大於 16 的字串分配 heap 上的空間。這樣做的原因主要是效能考量,有以下兩點:
https://hackmd.io/@hankluo6/2021q1quiz1
上課時將 merge sort 想成 quick sort,merge sort 的 worst case 不應該為不平衡的切割,如果為不平衡切割的話,複雜度應為:
這樣就與 quick sort 一樣為 ,但 merge sort 的概念為 divide and conquer,worst case 應為 ,演算法需以 list 的中心點切割,使兩個 sub-list 皆盡量等長,再對 sub-list 做 merge sort,故 worst case 的複雜度與 best case 相同,應為:
而會造成 merge sort 產生 worst case 的為 merge 時 compare 的次數,當兩個 sub-list 間的大小排序為互相交叉時會發生:
此時 merge 將需要 m + n - 1 次的 comparison,根據 Wikipedia,此數值最高為 。
list_sort.c
的切割方式實現 botton-up 方式的 merge sort,以每兩個元素為單位切割,當合併後產生兩個 數量的 list 時,便會合併成 ,並保證 數量的 linked list 皆存在,這樣就能維持每次 merge (包含最後一次 merge_final
)皆最高只會有 2:1 的數量比例,所以只要 數量的元素都可以容納在 cache 中,便能減少 cache miss。