# 2024q1 Homework5 (assessment) contributed by < `vestata` > ## 測驗題改進 選擇三題進行改進。 ### 1. [第四周測驗題 Q2](https://hackmd.io/@sysprog/linux2024-quiz4#%E6%B8%AC%E9%A9%97-2) 預計: - [ ] 補完之前缺失的 `lab0-c` 之 `ttt` - [x] 完成 coroutine 並行 - [ ] 鍵盤事件 - [ ] 第四周測驗題 Q3 ### 2. [第三周測驗題 Q3](https://hackmd.io/@sysprog/linux2024-quiz3#%E6%B8%AC%E9%A9%97-3) 詳見 [log2](https://hackmd.io/@tony268596/linux2024-homework4#%E6%B8%AC%E9%A9%97%E4%B8%89) 以不同方式實作 `log2()` 比較效能差距。 預計: - [ ] 分析噪點原因 - 改為使用 perf 測量,發現不能明顯體現實驗結果 - [x] 更多 linux 應用案例 ### 3. [第一周測驗題 Q2](https://hackmd.io/@sysprog/linux2024-quiz1#%E6%B8%AC%E9%A9%97-2) 預計: - [ ] 實作 timsort 中的 insertion sort, galloping mode 部份 - [ ] 觀察 list_sort 在 linux 中的使用場景 - [ ] 是否 timsort 在這些場景中表現受否會好 ## 閱讀 [因為自動飲料機而延畢的那一年](https://blog.opasschang.com/the-story-of-auto-beverage-machine-1/) >「一項產業進步的速度,很大程度取決於做實驗修正問題的速度。」 對於這段話,在我進行 Linux 核心實作的課程中有了深刻的體會。我發現 Jserv 老師在課堂上有意培養我們這種習慣。課程初期,我對這個觀念還不太熟悉,經常忽略即時修正的重要性。特別是在 `lab0`,我常因為一些細節問題而進度受阻。現在回顧,我確實認識到細節的重要,但一開始我過於害怕失敗,導致進度落後。作為資訊領域的一分子,我應該善用能夠快速修正的優勢! > 「你不能現在就放棄,要是現在就放棄的話,你這輩子日後遇到這種等級的困難,就只會想逃避而已。」 > 「你最大的問題在太害怕失敗了,既然都已經決定要延畢做飲料機了,那就要好好做,才不會辜負當初自己的期望 … 你該學習的不是看到事情要完蛋了就去避免失敗,而是應該學習如何處理與承受失敗,你才能變得比以前更強大。」 沒想到做飲料機的故事也會遇到 jserv 老師的身影,真的是引響力很大。回顧前五周學習 linux 核心實作這堂課,前面的實作對我來說真的是一大考驗,因為可以參考其他人的進度,可以看到許多修習過這堂課或是本身實力過硬的同學,明明自己花了很多時間也認真學習,但是總是落後他人一節。有時候還是會被上述的情緒所影響。不知道是 jserv 有意無意在此刻安排這些作業內容,讓我修正心態。好好補足自己實作較慢與數學理論的基礎再努力前進! 屆時剛好做完 NVIDIA GPU System Software Engineer Intern 的 hackerrank 測試。因為最近一直在寫實作的作業自認為對程式的掌握有提高,也很久沒有刷 leetcode 了,前面的基礎題還好,後面的程式題(leetcode 類型)真得是表現欠佳。今年找的暑期實習也都沒什麼好消息,心情多少還是被影響到。但是看到文章中 jserv 老師提到: >「你最大的問題在太害怕失敗了,既然都已經決定要延畢做飲料機了,那就要好好做,才不會辜負當初自己的期望。你可以計算要花多少錢,然後評估自己可以接受多少損失,畢業後慢慢還都好,要錢我也可以借你。但青春很貴,你也知道實習會發生什麼事,公司不會指派重要的工作給你,他們只會指派低風險的工作,你學習到的東西並不會比你現在多。你該學習的不是看到事情要完蛋了就去避免失敗,而是應該學習如何處理與承受失敗,你才能變得比以前更強大。」 有時很難揣測 Jserv 老師在想什麼,也不清楚老師的原意是什麼,但是還蠻有趣的,也讓我的心態好轉一些。畢竟還是要繼續前進!(開始每天寫一 leetcode) > 這一連串的問題都是可能的變因,第一眼看到時會覺得頭腦超級混亂,但現實世界就是這麼一回事,解決問題的唯一方法就是冷靜下來分析,做實驗把變因排除掉。 因為自動飲料機而延畢的那一年裡面提到很多自己的心路歷程,雖然所在的領域不一樣,但是心境卻是相通得。常常在螢幕的這一端感受到他的困境。我也回顧我自己當身處在混亂之中,時常腦袋也亂成一團的壞習慣。看到別人提出相同問題,我也深感其中,要修正這壞習慣。 > 儘管世界如此殘酷,但人卻不一樣,當你真心想做到一件事,付出足夠的犧牲,這個世界會聽見並做出回應,周遭的人漸漸願意相信你、花時間幫助你,你的付出並不見得會有結果,但是加上許多人的幫助,可能一切就不一樣了。 對你而言真正重要的事物,會比你想得到的事物更早出現在路邊。 目前的人生之路,我確實是隨心所欲地探索,但途中感到相當開心。我認為我與許多優秀的同學背景大相逕庭,所以也很感謝這堂課提供的平台,讓我能夠遇見不同的人,並對問題提出不同的解決方案。雖然我們背景迥異且素未謀面,能夠相互交流,實在是一個非常好的體驗! ## 簡述想投入的專案 ### llm.c 看到老師在社群上分享的 [karpathy/llm.c](https://github.com/karpathy/llm.c) 關於 Andrej Karpathy 使用 C 語言開發的大語言模型訓練程式,比較目前 C 的實作方法對比 Pytorch 的性能差異。如同老師在第一周上課提到的,我們很常用 Pytorch,但是對於他的限制跟底層實作一概不知。 根據目前的 karpathy 提到的工作重點我想我能貢獻的有: 1. 直接在 CUDA 上實作。 - 目前實作只有前向傳播,希望有貢獻反向傳播的機會。 2. 用 SIMD 指令加速 CPU 版本。 - 目前針對 SIMD 沒有任何實作知識,希望可以學習實作。 - 增加對 CPU 的控制 3. 開發更現代的架構,如 Llama2、Gemma 等。 - - - 紀錄問題 與 jserv 進行一對一討論,紀錄以下問題: > [time=Mon, May 6, 2024 14:00 PM] 與 jserv 討論專題,預計在 [第九週測驗題 第二題](https://hackmd.io/@sysprog/linux2024-quiz9#%E6%B8%AC%E9%A9%97-2) 並行矩陣計算進行延伸,探討是否有可能在 llama.cpp 等專案是否有提升效能的可能性。需要有實質上的提升,並有實驗佐證。 matmul 筆記在 [這裡](https://hackmd.io/@tony268596/Hy4EWvXM0) #### 1. 為何不同大小的方陣不能相乘?參閱 [2024-04-16 問答簡記](https://hackmd.io/@sysprog/ByhWN5jeR?fbclid=IwZXh0bgNhZW0CMTAAAR2Vhu3uEN_0vijWCZMoM92281SVzcpTgQ1JsOy8qGWchylL958hmIEFgRs_aem_AZDrSvHardf4em77UrgH_vWceu5H-C_XJanSG8le3mW9iOeGPz_429xwqePrVC_ypWg3HJGeGfR4jZGZyvlcxJ7n#jouae) >矩陣是線性轉換的有序基底間的映射關係,而矩陣乘向量是將一空間的座標向量映射至另一個空間的座標向量 jouae 從什麼是矩陣相乘講述,它從映射關係講起,令 $(UT)(v)$ ,則可以解釋為原本的 $v$ 從原本的空間映射到 $T$ 向量空間,再從 $T$ 向量空間轉換到 $U$ 向量空間。為了完成線性轉換的兩者的基底必須相同。$A=[T]^{\beta}_{\alpha}$ 和 $B=[U]_{\beta}^{\gamma}$。則 $AB=[UT]_{\alpha}^{\gamma}$ #### 2. llama 的效能瓶頸會是什麼? 目前瓶頸有兩個: 1. Quantization 2. Matrix Multiplication #### 3. 浮點數跟定點數分別需要多少 memory 空間?會有多少精確度? 假設這邊的定點數為 8 位元整數,fp32 需要 32 位元記憶體空間,8 位元整數 需要 8 位元記憶體空間,記憶體成本是浮點數的四分之一。 精度會依據不同 quantization 手段有不同精確度。 [quantization 筆記](https://hackmd.io/@tony268596/HyjsVKBmR) #### 4. bf16, fp16 差別是什麼? ![image](https://hackmd.io/_uploads/B1g93K2MR.png) 以 FP16 來說,有 1 個 bit 作為 sign bit,5 bits 作為 exponent,還有 10 bits 的 fraction (significand)。 sign bit 用來設定正負數,0 設定正數,1 設定負數。 Exponent 用來表示浮點數可以表示的範圍,從 00001 到 11110,,offset 設為 15,故其範圍是 $2^{-14}-2^{15}$。 Fraction 用來設定小數點精度,會有 10 個 bits 紀錄,但其實會包含首位第一個預設的 1,所以會紀錄 11 位。 我們得公式如以下: $(-1)^{sign} * 2^{exponent - 15}*1.fractoin(binary)$ 這邊看到另外一種計算方法,認為很有趣,因為 fraction 10 個 bits 最大為 $2^{10}=1024$ 那所以 fraction 的表示都會在這個之中,可以用比例去計算出 fraction 在 十進制的大小。 $(-1)^{sign} * 2^{exponent - 15}*(1+\frac{fraction(decimal)}{1024})$ 故我們算 fp16 的表示範圍: 最大正數:$0111101111111111=(-1)^0*2^{30-15}*(1+\frac{1023}{1024})=65504$ 最小正數:$0 00001 0000000000=(-1)^0*2^{1-15}*(1+\frac{0}{1024})=0.00006103515625$ (正數負數表示範圍是一樣的,先不考慮 subnormal 的狀態,特殊表示可以在以下 wiki 找到) 範圍我參考 [fp16 wiki](https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Half_precision_examples): ![截圖 2024-05-11 下午3.07.31](https://hackmd.io/_uploads/HkmvF52MC.png) 這邊要注意的是 exponent 全為 0 或是全為 1 的情況: 全為 1 會被用來表示 `INF` ![image](https://hackmd.io/_uploads/HJyN_9nG0.png) BF16 全名為 brain floating point,為 google brain 所開發。有 1 個 bit 作為 sign bit,8 bits 作為 exponent,還有 7 bits 的 fraction (significand)。offest 設為 127。 其公式為 $(-1)^{sign} * 2^{exponent - 127}*1.fractoin(binary)$ 最大正數為: $0 11111110 1111111=(-1)^0*2^{254-127}*(1+\frac{fraction}{128}) = 3.38953139 × 10^{38}$ 最小正數為: $0 00000001 0000000=(-1)^0*2^{1-127}*(1+\frac{0}{128})=1.175494351 × 10^{−38}$ ![image](https://hackmd.io/_uploads/rJBG1snfA.png) | 表示範圍 | FP16 | BF16 | | -------- | ---------------- | ------------------------ | | MAX | 65504 | $3.38953139 × 10^{38}$ | | MIN | 0.00006103515625 | $1.175494351 × 10^{−38}$ | #### 5. [文章]((https://justine.lol/matmul/))中 `fortran` 的矩陣相乘其中它數字代表什麼? 作者提到了 numpy 模組的使用,其中涉及調用 Fortran 來完成 SGEMM。整體的流程為:Python -> Numpy (C) -> BLAS (Basic Linear Algebra Subprograms) -> SGEMM (Fortran)。 ```fortran SUBROUTINE SGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * .. Scalar Arguments .. REAL ALPHA,BETA INTEGER K,LDA,LDB,LDC,M,N CHARACTER TRANSA,TRANSB * .. Array Arguments .. REAL A(LDA,*),B(LDB,*),C(LDC,*) [...] * * Form C := alpha*A*B + beta*C. * DO 90 J = 1,N IF (BETA.EQ.ZERO) THEN DO 50 I = 1,M C(I,J) = ZERO 50 CONTINUE ELSE IF (BETA.NE.ONE) THEN DO 60 I = 1,M C(I,J) = BETA*C(I,J) 60 CONTINUE END IF DO 80 L = 1,K IF (B(L,J).NE.ZERO) THEN TEMP = ALPHA*B(L,J) DO 70 I = 1,M C(I,J) = C(I,J) + TEMP*A(I,L) 70 CONTINUE END IF 80 CONTINUE 90 CONTINUE [...] RETURN END ``` 在這個部分,數字可以自訂,代表 for 迴圈內的所有語句。以上述程序為例,`DO 90 J = 1, N` 至 `90 CONTINUE` 構成了此 for 迴圈的語句範圍;在 C 語言中,這對應於使用大括號來界定。 #### 6. multiply-add matrix 是什麼? $a \leftarrow a+(b*c)$ 將乘法的乘積結果和累加器 A 的值相加,再存入累加器。若沒有使用 MAC 指令,上述的程式可能需要二個指令,但 MAC 指令可以使用一個指令完成。他的輸入格式可以為 8 bits, 16 bits 但是經過最後累加器會變成 32 bit。 若一條 MAC 指令在處理浮點數時只有一次的數值修約,則這種指令稱為 FMA。 TODO: 讀懂 [LLaMA Now Goes Faster on CPUs](https://justine.lol/matmul/) 文章 專題問題: 在 [筆記](https://hackmd.io/@tony268596/Hy4EWvXM0) 中我對 [matmul](https://github.com/attractivechaos/matmul) 使用的資料型態是 float 與 [Matrix_Multiply_using_Arm_Neon_and_Avx](https://github.com/ruthreshx/Matrix_Multiply_using_Arm_Neon_and_Avx) 進行了測試。我發現,並行程式只在大型矩陣相乘時顯示出其優勢,而在小型矩陣相乘時,其並行成本反而會降低效能。 <!-- 在不同 quantization 會產生不同的精度,要怎麼測試它對模型準確率的影響? --> 在測試過程中,我發現 SIMD 對效能提升具有顯著的影響。[matmul](https://github.com/attractivechaos/matmul) 使用了 SSE 技術,而 [Matrix_Multiply_using_Arm_Neon_and_Avx](https://github.com/ruthreshx/Matrix_Multiply_using_Arm_Neon_and_Avx) 則採用了 neon 技術,與文章中提及的 AVX512 相比較。因此,我認為下一步應該是比較不同 quantization 後,使用 SIMD 在核心模組進行效能的比較,並實驗確定在何種矩陣大小下啟用並行計算最為合適。 在 Linux 核心模組使用 SSE/AVX/NEON 等 SIMD 指令集並降低資料存取的延遲,引入 bf16 計算,並適時開啟並行矩陣計算。 [LLaMA Now Goes Faster on CPUs](https://justine.lol/matmul/) 文真中提到關於不同 quantization 所產生的精度差異,我該如何實驗它對模型的影響(訓練速度、推論速、準確率)? 確認 VDPBF16PS 指令怎麼使用在 AVX_VNNI? <!-- 如果想完成 bf16 的實驗,我的設備 `13th Gen Intel(R) Core(TM) i7-13700` 該怎麼完成 bf16 的運算?從 user space (fp32) 使用核心模組轉為 bf16 進行 V * K * Q 再複製回 user space。 --> <!-- 謂何 [OpenBLAS](https://github.com/OpenMathLib/OpenBLAS) 的 sgemm 可以這麼快? --> 期末專案 方案一 Homework5 上次一對一討論的問題紀錄與回覆。 整理 LLaMA Now Goes Faster on CPUs 所學習到的知識,在 筆記 在測試過程中,我發現 SIMD 對效能提升具有顯著的影響。matmul 使用了 SSE 技術,而 Matrix_Multiply_using_Arm_Neon_and_Avx 則採用了 neon 技術,與文章中提及的 AVX512 相比較。目前還在學習怎麼使用使用 SIMD 指令。我認為下一步應該是比較不同 quantization 後,使用 SIMD 在核心模組進行效能的比較,並實驗確定在何種矩陣大小下啟用並行計算最為合適。 quantization 程式實作未看,llamafile 程式未看。 Q: bfloat16, f16, q (fixedpoint) TODO: 理解 data type 與其精確度和 tok/s 速度的取捨 TODO: 探討 llamafile 相較於 llama.cpp 進行哪些調整,得以加速? (重現實驗並分析) TODO: 一旦選定 data type,就聚焦在 matmul 的實作 (w/ SIMD, OpenMP, oneMKL, OpenBLAS, ...) 的效益 => 我們需要有個 wrapper/tester,得以涵蓋現有實作並整合進 llama.cpp,在 Linux 平台驗證,perf (搭配第六次作業提到的[效能分析方式](https://hackmd.io/@sysprog/linux2024-integration/%2F%40sysprog%2Flinux2024-integration-a),排除系統干擾) TODO: (bonus) 提出更快的 matmul > llamafile is defived llama.cpp