or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
2023q1 Homework2 (quiz2)
contributed by <
LiChiiiii
>測驗一
運作原理
考慮
next_pow2
可針對給定無號 64 位元數值 x,找出最接近且大於等於 2 的冪的值,例如:next_pow2(7)
= 8next_pow2(13)
= 16next_pow2(42)
= 64最初想法是將 x 左移 1 後,最高位元的 1 保留下來,其餘設定為 0 ,根據測驗一的題目實作,利用
shift
和or
將最高位元的 1 至最低位元的 bit 皆設定為 1 ,最後回傳 x+1 即為答案。Example:
發現:如果 x 剛好是 2 的冪,那麼應該要回傳 x 本身,但上述程式碼不會回傳 x 。
Example:
因此可加上條件式判斷 x 是否剛好是 2 的冪,避免不必要的操作。
用
__builtin_clzl
改寫__builtin_clzl(x)
回傳 x 中有多少個 leading 0-bits ,當64 減去__builtin_clzl(x)
即可找到最高位元的位置。在 Linux 核心原始程式碼找出類似的使用案例並解釋
在 The Linux Kernel API 中找到
__roundup_pow_of_two
實作。其原始程式碼在 linux/log2.h 。__roundup_pow_of_two
給定一個無號整數 n,回傳該值最接近且大於等於 2 的冪的值,若 n 為 2 的冪則回傳 n。
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →fls_long
用來找從最高位元開始連續的 0 的個數,也就是找到出現第一個 1 的位置。linux/bitops.h如果
l
是一個 32 位元的整數(即sizeof(l) == 4),則調用fls
函數。如果
l
是一個 64 位元的整數(即sizeof(l) > 4),則調用fls64
函數。__roundup_pow_of_two
使用案例在 commit 8758768 可以看到原本的
i40iw_qp_round_up
被替換成roundup_pow_of_two
。當上述
clz
內建函式已運用時,編譯器能否產生對應的 x86 指令?x86 中有一條稱為
bsrq
的指令,可以實現尋找最高位元 1 的位置的功能,其運作方式與clz
函式類似。編譯器可以將clz
函式轉換為bsrq
指令來實現對應的功能。實際執行
cc -O2 -std=c99 -S next_pow2.c
,可在next_pow2.s
第 22 行看到bsrq
指令。next_pow2.s
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →BSR (Bit Scan Reverse)
指令和bsrq
指令差別在哪裡?測驗二
運作原理
給定一整數 n ,回傳將 1 到 n 的二進位表示法依序串接在一起所得到的二進位字串,其所代表的十進位數字 mod \(10^9+7\) 之值。
len
:用來紀錄整數i
之 bit 長度,也就是要左移的位元數。迴圈會從 i = 1 開始執行至 i = n,也就是說, bit 長度會越來越長。當
i
為 2 的冪時,代表進位到下一個 bit , 因此執行len++
增加 bit 長度,而每次循環會將前面迴圈的結果向左移len
個位元數,利用or
與i
合併後,再做mod M
的運算來避免 overflow 。嘗試使用
__builtin_{clz,ctz,ffs}
改寫,並改進 \(mod\) \(10^9+7\) 的運算int __builtin_ffs (int x)
int __builtin_ctz (unsigned int x)
int __builtin_clz (unsigned int x)
__builtin_clz(i) + __builtin_ctz(i) = 31
作為 2 的冪之條件式判斷。以上程式碼改寫並沒有增進效能,於是又想了其他改寫方式。
__builtin_clz
會回傳從最高位元開始連續的 0 之個數,將 32 bits 減去__builtin_clz(i)
即為需左移的位元數。測驗三
運作原理
UTF-8 字元可由 1, 2, 3, 4 個位元組構成。其中單一位元組的 UTF-8 由 ASCII 字元構成,其 MSB 必為 0。
UTF-8 的多位元組字元是由一個首位元組和 1, 2 或 3 個後續位元組所構成。首位元組可以表示其 UTF-8 字元是幾個位元組所組成,舉例來說,2 bytes 的字元其首位元組之最高位元開始存在連續 2 個 1,下表為多位元組字元的規則:
以下程式碼使用了 SWAR(SIMD Within A Register)算法,計算給定的 UTF-8 編碼的字串中字元數量。
程式碼中使用 for 迴圈來一次處理 8 bytes ,所以先透過
len>>3
(也就是len/8
)來計算總共有幾個 8 bytes ,接著在迴圈中將not bit6 and bit7
這個想法實作,並透過__builtin_popcount
計算有多少 1,來紀錄有幾個continuation byte
(也就是10xx.xxxx
),最後將continuation byte
的數量從總字串長度中減去,即可確定字元數量。比較 SWAR 和原本的實作效能落差
swar_count_utf8
中的循環次數比count_utf8
少,因為對於一次處理 8 bytes 的長度範圍,使用位元運算進行計數,減少了循環的次數。而count_utf8
則需要遍歷每個 byte 進行判斷,增加了循環的次數。swar_count_utf8
和count_utf8
, 再利用perf
重複執行 100 次並計算instructions
和cycles
的數量,從以下結果可以明顯看到count_utf8
使用到較多的指令數量,花費較多的運行時間。在 Linux 核心原始程式碼找出 UTF-8 和 Unicode 相關字串處理的程式碼,探討其原理,並指出可能的改進空間
在 /fs/unicode/utf8-norm.c 找到 UTF-8 和 Unicode 相關字串處理的程式碼。
utf8byte()
是一個 UTF-8 字串的解碼器,用於從 UTF-8 字符串中提取規範化形式的單個字節,並處理了 UTF-8 字符串的解碼和解析過程中可能出現的各種特殊情況。這段程式碼存在一些重複的部分,可以將重複部分抽取出來,另外建立函式來改進可讀性。utf8byte(struct utf8cursor *u8c)
測驗四
運作原理
此程式碼是為了檢查從最高位元開始是否只包含一段連續的 1,使用一個 for 迴圈不斷的左移 x ,並檢查最高位元是否為 1 ,直到 x 等於零就結束迴圈回傳 true ,否則繼續左移 x ,若發現最高位元為 0 即回傳 false 。
改寫成精簡的程式碼,先計算 x 的 2 補數 ( ~x + 1 ) ,兩者做 XOR 來保證高位元的值為連續的 1 ,即可在最後比較大小來檢查是否特定樣式。
Example:
在 Linux 核心原始程式碼找出上述 bitmask 及產生器,探討應用範疇
參見 Data Structures in the Linux Kernel 找到
BITMAP_FIRST_WORD_MASK
和BITMAP_LAST_WORD_MASK
實作,其原始程式碼在 linux/bitmap.h 。BITMAP_FIRST_WORD_MASK
此函式用來獲得從最高位元開始包含連續 1 的 mask ,參數 start 代表從最低位元開始算起含有 0 的個數,其餘為 1 。
將
0xFFFFFFFF
左移(start) & (BITS_PER_LONG - 1)
,其中BITS_PER_LONG
是一個常量,表示 unsigned long 型別的位數,通常是 32 位元或 64 位元,取決於機器的架構。Example:
BITMAP_LAST_WORD_MASK
此函式用來獲得從最低位元開始包含連續 1 的 mask ,參數 nbits 代表從最低位元開始算起含有 1 的個數,其餘為 0 。
假設
BITS_PER_LONG
為 32 ,那麼實作就是將0xFFFFFFFF
右移32 - nbits
。Example:
實際應用
這是一個用於清除 bitmap 指定範圍內的位元數的函數