# 2023q1 Homework5 (assessment) contributed by < `ShamrockLee` > ## 作業改進 ## 〈為了自動飲料雞延畢一年〉讀後心得 為了寫這篇心得再次讀這系列文章,和第一次看一樣是嘴巴開著看完。以下不會是一篇配得上原文的心得,只是粗淺的經驗和看法。 這樣一篇熱血的文章,但作者多次強調他們在做各項決定前的規劃,與決策當中的調整。很多決定都有量化的數據支撐。 想到大一下實驗物理的期末專題,我當時計畫用洞洞板縫上導線做出觸控板,作為移位暫存器的應用。開始沒多久就有發現「縫上」的動作太過耗時,但當時只是想說再快一點就能完成,導致沒有足夠時間檢查買來的電子元件,期末才發現位移暫存器壞了。經過慘痛得教訓,之後的實驗物理課一直提醒自己要控制規模。 以文中作者的作法,他們可能會先訂出各個實驗架設的階段能花費的時間上限,並在實作過程中依照量化的進度修正。如果我當時能當機立斷,把觸控版大小縮小為四分之一,我們就有時間進行測試,甚至因為要連接的線不多而能繞過位移暫存器,直接接在開發版上,最後就不至於開天窗。 教授在一對一討論時曾說過我還是在靠感覺寫程式,或許就是指「缺乏規劃」吧! 教授也在課程一開始就引入各種測試工具。像是用 santizer 檢驗越界存取記憶體與 race condition 。比起跑十億次迴圈來猜程式的行為,前者顯然是更堅實、更基於事實的基礎。 除了在程式上,還有在修課的腳步上。我在一開始的時候一頭熱的衝,學期才剛開始就後繼無力,也忽略了本職的研究進度,都是課程跟不上的因素。 除了規劃之外,「引進外部資源」也是作者在卡關時能繼續推進的動力。從找到專長於機械的夥伴、向飲料店借用數據、場地、努力向領域中的專業人士求教,到買進冰塊分裝機、加糖機等等。這些資源提供了很多作者他們靠自己想破頭也無法想到的解方。 教授從一開始作業撰寫、繳交的方式,就一直鼓勵、要求及早公開、互相提問、觀摩他人的作法。期末專題中的 `fls_u32_branchless` ,是因為教授建議我參照另一份專題的成果,才能升級為 `gen_f{f,l}s_branchless` 。我會在 lab0-c 就卡關,也是因為持續閉門造車,只想等「弄好」再繳交。以前有人說「作業沒寫完,怎麼還敢繳交?」,這個學期的經驗很肯定的告訴我:沒做完的東西給別人看,就算給別人笑或給別人罵,搞不好還能找到克服的方法;卡在那會什麼都沒有。 最後,或許「熱情」不是起頭不顧一切的踩油門,而是帶著一個人在困難與遠路上繼續前進的東西。 ## 教材討論 ## 貢獻程式碼到 Linux 核心 ### 前情提要 我在期末專題中「 Linux 核心 $\log_2$ 實作」一節加入實作無分支 `log2` 的成果,主要涉及無分支 `fls` 的實作。經 jserv 教授建議後,對照 Shiritai 同學的期末專題〈回顧 bitops 並改進〉成果,以改進我的實作。詳情已於我的期末專題共筆中敘述,就不在此重複。 〈回顧 bitops 並改進〉的一項成果,是在巨集的定義中,透過 `_Pragma` 運算子精準的指示 GCC 編譯器展開特定迴圈( unroll loop )。具體的程式如下: ```c _Pragma("GCC unroll 6") do { ...; } while (...); ``` 以上實作成功達到目的,但明顯只是用於 GCC ,不利於使用 Clang 編譯 Linux kernel 的開發者。 在 Linux 核心專案中,以 `GCC unroll` 為關鍵字,搜尋現存的程式碼;發現結果僅有三處,可能是因為 GCC 直到第八版才支援這項 directive 的緣故。其中兩處包含在 Intel 的兩個 network drivers 中,是同一個人寫的。內容分別如下: [`drivers/net/ethernet/intel/i40e/i40e_xsk.h`](https://github.com/torvalds/linux/blob/v6.4-rc7/drivers/net/ethernet/intel/i40e/i40e_xsk.h) ```c /* This value should match the pragma in the loop_unrolled_for * macro. Why 4? It is strictly empirical. It seems to be a good * compromise between the advantage of having simultaneous outstanding * reads to the DMA array that can hide each others latency and the * disadvantage of having a larger code path. */ #define PKTS_PER_BATCH 4 #ifdef __clang__ #define loop_unrolled_for _Pragma("clang loop unroll_count(4)") for #elif __GNUC__ >= 8 #define loop_unrolled_for _Pragma("GCC unroll 4") for #else #define loop_unrolled_for for #endif ``` [`drivers/net/ethernet/intel/ice/ice_xsk.h`](https://github.com/torvalds/linux/blob/v6.4-rc7/drivers/net/ethernet/intel/ice/ice_xsk.h) ```c #ifdef __clang__ #define loop_unrolled_for _Pragma("clang loop unroll_count(8)") for #elif __GNUC__ >= 8 #define loop_unrolled_for _Pragma("GCC unroll 8") for #else #define loop_unrolled_for for #endif ``` 這剛好就能同時支援 GCC 與 Clang 展開迴圈的功能。不過這個實作把定義好的 directive 和 `for` 綁在一起,如果要用在 `while` 迴圈或 `do` 迴圈又要重新定義了。這個實作只用在一個 for 迴圈上,不成問題,但把 for 拿掉顯然能更能重用: ```c #ifdef __clang__ #define pragma_unroll_headername _Pragma("clang loop unroll_count(8)") #elif __GNUC__ >= 8 #define pragma_unroll_headername _Pragma("GCC unroll 8") #else #define loop_unrolled_headername #endif ``` 我以這種寫法用來改善我的 `fls` 實作,但仍希望他更通用一點,因為如果換了數字,又要再定義一遍,寫在 Linux 核心裡面就會像現在一樣在不同地方重複。希望能時做出 `pragma_unroll` 原先嘗試使用 C 語言 string literals 並陳時會相接的特性,但編譯器的錯誤表示 `_Pragma("")` 語法中只接受單一的 string literal 。後來嘗試先以 token pasting/concatenation (`##`) 將數字與其他部份相接,但發現`"clang loop unroll_count(N)"` 當中數字與括號( `(` 、 `)` )相接時會報錯,而且無法處理 `"_Pragma GCC unroll N"` 的空格。後來想到能在 數字前方加上 `0` ,的確也在 N = 4 或 N = 6 時順利展開,但在 Linux 核心中編譯測試才發現 `0` 是八進位 integer constant 的前綴,因為接了 8 而報錯。 雖然最後未能成功實作,但藉此機會認識 Linux 專案的貢獻規則,最後也將過程中找到的錯字做成 patches 送出,獲得維護人員採納。 ### 實作過程 前面的嘗試參考了 Intel 兩個有關網路的 drivers 內容,意外以 `scripts/checkpatch.pl` 發現檔案中的 transmission 被拼成 tranmission ,少了第一個 s 。 以 transmit 與 transmission 共通的開頭 `transm` 的拼寫錯誤版本 `tranm` 搜尋專案內容,發現這樣的拼寫錯誤不少見,共有八處,便逐一更正之。 另外發現, `script/checkpatch.pl` 其實是依賴 `scrcipt/spelling.txt` 檢驗拼寫錯誤,而 transmission 剛好在其中,但沒有包含其他變化型,而導致其他錯誤未被發現。因此將 transmit 與 retransmit 文法上的各種變化型都加入該對照表。 ### 撰寫 commit message 透過 ``` gitk path/to/file.h ``` 查詢過往的 commit message ,發現 linux source tree 的 commit message 格式為: ``` name: Do something Description Description ... ``` 其中冒號前的部份,可能是子系統的名稱或對於變更這個檔案所屬的元件的描述,大致上會沿用先前 commit message 裡的的名稱。於是我依次查詢每個檔案的 git logs ,以決定適當的名稱。 在網路搜尋引擎上搜尋 `typo site:lkml.org` 能找到 Linux kernel mailinig list 上關於錯字曾出現的電子郵件紀錄,發現有人使用過 "Spell ___ properly." ,一來是祈使句,符合格式,二來不會讓 commit message 中出現錯字,增加檢查的麻煩,於是我沿用這樣的格式。另外也發現曾有維護者提醒 patch 作者,應該在 commit 標題中包含相應的子系統名稱,這部份我想我算是有做到。 ### 製作 patches ### 檢驗 patches 並尋找 maintainers ### 設定與使用 `git send-email`