contributed by < OscarShiang
>
linux2020
本題所在的位置是在 xs.c:xs_new
函式中
這個函式的用途是初始化字串結構 xs
的相關設定,觀察 xs
的結構可以發現
可以看到 len
的大小就是 p
指標所指向的字元陣列包含空字元的大小,而在 xs_new:11
的地方就是將字串複製到 data
中
但是因為 data
的總長度只有 16 個 byte ,所以可以判斷 xs_new:5
的用意是判斷該字串 p
是否有超出 data
可以儲存的長度,所以正確答案就是:
AAA = 16
這題是在 xs.c:xs_trim
的位置,因為在這個函式中 mask
的用途是將 trimset
中的字元記錄在 mask
中
觀察 xs_trim
中的 macro
可以發現 set_bit
可以把字元的數值,記錄在 mask
的其中一個位元中,所以考慮所有字元都可以存入的需求,也就是 ACSII 所規範的 255 的字元都可以利用 mask
記錄下來的情況, BBB
應該為 32 ,也就是 個 bit 才能符合要求
BBB = 32
根據 xs_trim
中對於 check_bit
的用途
也就是確認該字元是否是要修剪的字元,所以應該算出來的數值應該是
0
: 該字元是 trimset
中所出現的字元0
: 該字元不是 trimset
中所出現的字元而因為該字元所對應到 mask
的位置就是 1 << (uint8_t) byte % 8)
的位置,所以答案就是:
CCC =
&
根據 macro 的定義, xs_tmp
的用途就是回傳一個已經透過 xs_new
初始化的字串
因為在 main
函式中的呼叫是不是存進 pointer 中,而在 xs_tmp
最後所呼叫的 *xs_new
的回傳型態是卻是 pointer ,所以需要透過 derefernce 取得
思考上述程式碼列表的 16
這個數值,解釋其作用,考慮到 cache,提出調整方案
好的,正在研讀 Cache 原理和實際影響,並思考改進的空間
Oscar[color= orange]
在 xs 的結構中,如果使用 stack 來儲存字串的話就會利用 space_left 來記錄剩餘的 byte 數,所以如果在 xstring 還是使用 stack 來記錄資料就可以利用 16 - space_left
取得字串長度,但是如果 stack 剛好用完,也就是 '\0'
位於第 16 個 byte 上也不會發生問題,因為 '\0'
在 ASCII 中對應到的就是數值上的 0
所以即使因為 '\0'
位在第 16 個 byte 而覆蓋到 space_left
的資料, xs_size
還是能正確地計算出字串的長度
在 xs_concat
實作中如果遇到連接的結果超出 string
的 stack 所能管理的長度時,會透過宣告一個新的字串 tmp
來連接字串,最後將原本的字串 string
釋放掉,換成 tmp
的字串
但是因為我們可以透過 xs_grow
調整字串的容量,所以就不需要另外宣告新的字串來處理連接,所以我透過 xs_grow
調整 string
的長度接著將 data 指標更新,最後再將字串複製到 string
裡面
為了確定修改過後的版本能夠減少執行時間,我利用開機參數 isolcpus
將 CPU0 排除在開機程序外,接著透過 taskset
將程序綁定在 CPU0 上,並重複測量 次,以 95% 的區間進行平均
而實驗結果發現修改過後的執行時間較原先的版本短,如下圖所示
因為省去 malloc
新空間與釋放 string
的程序,所以執行的效率較原先的版本好
利用 valgrind 檢查 memory leak 時發現,在字串使用 heap 來儲存時,會因為在 main
函式中沒有被釋放而導致 definitely lost ,所以必須在 main
函式使用完 xs 後強制進行 xs_free
,如果該 xs
是使用 heap 來儲存的話才能將 malloc 的空間歸還給記憶體
為了能在程式結束時自動執行 xs_free
函式釋放透過 heap 存取的字串,我在 xs 變數宣告的時候加入 gcc attribute __cleanup__
綁定 xs_free
函式,讓其在程式結束時會被呼叫,釋放記憶體空間
因為需要考慮到 Copy-on-Write (CoW) 的議題,所以將字串複製分成兩種情況考慮
char *ptr
指向來源字串顧及 CoW,其他操作字串的函式也需修改
針對因為改寫來源字串而需要複製字串的函式定義新的函式 xs_dup
來簡化流程
因為如果 dst
是指標的話,我們可以先從 xs_data
函式取得 dst
指標對應的字串位置,並利用 xs_size
函式取得字串長度,當取得字串位置與字串長度之後,將 dst
字串重置,並將字串複製到 dst
中,最後回傳 dst
位置,完成複製
接著建構 xs_is_ref
函式讓我們可以知道該字串是否為 reference ,並將會操作到字串內容的函式: xs_concat
與 xs_trim
函式加入以下操作,在操作字串為參考的情況下複製字串以供操作
接著著手建構 xs_tok
函式
根據 Linux Programmer's Manual
所描述的 strtok
:
The strtok() function is used to isolate sequential tokens in a null-terminated string, str.
These tokens are separated in the string by at least one of the characters in sep.
The first time that strtok() is called, str should be specified; subsequent calls, wishing to obtain further tokens from the same string, should pass a null pointer instead.
The separator string, sep, must be supplied each time, and may change
between calls.
因為在 xs_tok
中也要使用在 xs_trim
所使用到的 mask
的技巧,所以將 #undef
的巨集移動到 xs_tok
的結尾處
利用 for 迴圈判斷所在字元是否是 sep
集合中的其中一種,如果是的話,就將該字元改為 \0
,用指標的指標更新 main
函式內的字元指標 last
後,回傳字串開頭的指標完成 xs_tok
參考 strtok_r
來改寫,避免用到 static char *dataptr
已參考
strtok_r
重新實作xs_tok
Oscar[color= orange]
而在 xs_tok
的實作中因為其也會改動到字串的資料,所以在開頭需要加上判斷其為字串參考的操作,避免複寫字串原先的資料