contributed by < KYG-yaya573142
>
XOR 運算特性為
當初填做答表單會全錯就是因為我沒理解 XOR linked list 實作的核心邏輯,在 XOR linked list 中 node 儲存的 addr
內容是 address of previous node (之後簡稱 prev) 及 address of next node (之後簡稱 next) 以 XOR 運算的結果 addr = prev ^ next
,根據上述 XOR 的運算特性,可以用以下方式在 node 間移動
addr ^ prev = next
addr ^ next = prev
而在 list head 和 list tail 的 node,根據 可知其儲存的 addr
剛好都是鄰近的唯一 node 的指標
0 ^ next = addr = next
prev ^ 0 = addr = prev
*l
需要更新儲存的 addr
為 next node 與 prev node 的 XOR
*l
原本位於 list head,(*l)->addr
直接是 next node 的位置tmp
就是 prev nodeNULL
free
掉 nodel
原本位於 list head,l->addr
直接是 next node 的位置free
掉 l
以前須先更新 next node 中儲存的位置,因為原本的 next node 會變成新的 list head
XOR(next->addr, l)
的結果會是 next
的下個 node 的位置while
直到 free
完所有 nodeleft
指向分割出的單一 noderight
指向剩下的 nodestart
會指向已經排序完成的 list headif (!right || (left && left->data < right->data))
是實行排序判斷的位置,採用遞增順序所以數值小的排在前面
left
的 node 接上 merge
的情況 (也就是 left->data
比較小,或是奇數 node 的情況)left
指向這個分離出來的單一 nodenext
指向剩餘的 listmerge
無任何 node
left
會是唯一的 node,所以儲存的指標為 NULL
start
會指向已經排序完成的 list head,所以也是指向 left
left
接在 merge
的前面
merge
是原本的 list head,merge->addr
直接是 next node 的位置merge
的 prev node 就是前面接上的 left
,merge->addr
須更新為 prev node ^ next node
,因此 LL1
應填入 XOR(merge->addr, left)
left
會是新的 list head,所以 left->addr
會直接指向 next node,因此 LL2
應填入 merge
left
的 node 接上 merge
時的邏輯一樣,只是改為將 right
接上,就不再贅述原理RR1
應填入 XOR(merge->addr, right)
RR2
應填入 merge
delete_list
後來實際使用時,發現 delete_list(queue)
後不會將 queue
指標設為 NULL,這會導致接下來 insert_node(&queue)
會發生 segmentation fault
因此我將 delete_list
改為使用 pointer to pointer 來實作
show
原本的實作缺乏簡單展示 list 內容的函式,而且 XOR linked-list 處理起來較複雜,因此實作一個輔助函式來顯示 list 的所有內容
sort
分割 node 的方式原本 sort
中每次只會分割出一個 node,參照 lab-0 中 merge sort 的做法,修改為每次會將 list 分為一半。實作的程式碼如下,明顯有使用過多變數的缺點,但現階段還想不到更漂亮的寫法
以下列程式碼檢查,結果正確
使用以下程式碼進行測試
Wiki 的 Merge sort 中談到 Optimizing merge sort 的策略
For example, the tiled merge sort algorithm stops partitioning subarrays when subarrays of size S are reached, where S is the number of data items fitting into a CPU's cache. Each of these subarrays is sorted with an in-place sorting algorithm such as insertion sort, to discourage memory swaps, and normal merge sort is then completed in the standard recursive fashion.
剛好可以用 CSAPP Cache memories 講義中的範例
我選用 insertion sort 做為 in-place 排序演算法
修改原本 merge sort 部分的程式碼
threshold
就是閾值 S,直接設為 global variable 以方便實驗時進行更動count
當作參數帶進 merge_sort
實驗的程式碼如下
merge_sort
排序 1000 個 nodes 的所需時間原本以為這漂亮的圖有特別的意義,後來才想到一件事情
本來在思考是否有方法可以了解 merge sort 在不同 S 下使用 cache 的情況,發現可以使用 Valgrind - Cachegrind,但同時也想到之前在 CSAPP 的 cache lab 中有提供一個模擬 cache 行為的程式,因此決定先嘗試這個比較簡單的方法
csim-ref
來模擬,使用的參數是對照 CPU-硬體組態 設置的while
迴圈實際上是在排序,將 newt
根據資料大小插入 list 中適當的位置link
是 pointer to pointer,可以理解為一般 pointer 儲存的是某個變數的位置,而 pointer to pointer 儲存的就是某個指標的位置,因此 *link
是指標while
迴圈做的事情一樣是排序,必須先對 link
進行 dereference 一次才會得到指向 list element 的指標,因此 AA
應填入 (*link)->data < newt->data
&
來取用指標的位置,因此 BB
應填入 link = &(*link)->next
使用以下程式碼進行實驗,分別測試兩種不同的 insert
的所需時間
使用 perf
來分析 branch 的差異
insert
insert_ptp
insert_ptp
在 branch-instructions:u 還是 branch-misses:u 有些微的改善,但是不明顯嘗試改用 perf record & perf report 來分析
insert
insert_ptp
insert
造成的 branch-misses 占了 333 筆資料的 12.54%insert_ptp
造成的 branch-misses 占了 365 筆資料的 5.21%linux 2020