--- tags: linux2023 --- # 2023q1 Homework3 (quiz3) contributed by < `visitorckw` > >[2023q1 第 3 週測驗題](https://hackmd.io/@sysprog/linux2023-quiz3) ## 測驗 `1` ## 測驗 `2` ## 測驗 `3` Reference: https://zh.wikipedia.org/wiki/%E7%BA%BF%E6%80%A7%E5%8F%8D%E9%A6%88%E7%A7%BB%E4%BD%8D%E5%AF%84%E5%AD%98%E5%99%A8 LSFR 是一種轉換操作,通常用來生成看起來是隨機的且循環周期非常長的序列。 程式碼中的 log2_64 函數用來計算 $log_2$ 的值。運用二分搜的概念,逐步尋找最高位所在的位置。而陣列 log_table_256 用來存放該 index 取 $log_2$ 的值,透過巨集 _ 來展開。 ```c static const char log_table_256[256] = { #define _(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, _(4), _(5), _(5), _(6), _(6), _(6), _(6), _(7), _(7), _(7), _(7), _(7), _(7), _(7), _(7), #undef _ }; #undef _ /* ASSUME x >= 1 * returns smallest integer b such that 2^b = 1 << b is >= x */ uint64_t log2_64(uint64_t v) { unsigned r; uint64_t t, tt, ttt; ttt = v >> 32; if (ttt) { tt = ttt >> 16; if (tt) { t = tt >> 8; if (t) { r = 56 + log_table_256[t]; // AAAA } else { r = 48 + log_table_256[tt]; // BBBB } } else { t = ttt >> 8; if (t) { r = 40 + log_table_256[t]; // CCCC } else { r = 32 + log_table_256[ttt]; // DDDD } } } else { tt = v >> 16; if (tt) { t = tt >> 8; if (t) { r = 24 + log_table_256[t]; // EEEE } else { r = 16 + log_table_256[tt]; // FFFF } } else { t = v >> 8; if (t) { r = 8 + log_table_256[t]; // GGGG } else { r = 0 + log_table_256[v]; // HHHH } } } return r; } ``` 此函數可以利用 __builtin_clz64 來加速,並且程式碼撰寫上更為簡潔 ```c uint64_t log2_64(uint64_t v) { return 63 - __builtin_clz64(v); } ``` lsfr 函數用來透過當前狀態轉移得到下一個狀態,new_bit 用來暫存將要放入最左邊的位元,並將其他位元向右位移 1 位。 ```c /* Implementation of LFSR (linear feedback shift register) * on uint64_t using irreducible polynomial x^64 + x^61 + x^34 + x^9 + 1 * (On 32 bit we could use x^32 + x^22 + x^2 + x^1 + 1) */ static void lfsr(uint64_t *up) { uint64_t new_bit = ((*up) ^ ((*up) >> 3) ^ ((*up) >> 30) ^ ((*up) >> 55)) & 1u; /* don't have to map '+1' in polynomial */ *up = ((*up) >> 1) | (new_bit << 63); /* shift *up by 1 to RIGHT and insert new_bit at "empty" position */ } ``` bucket_number 函數則是會將傳入的參數 x 找到一個對應的 bucket。 mask111 為有效位全為 1 的 bitmask,而 mask011 則為 mask111 的最高位改設為 0 的 bitmask 。 程式會判斷 x & mask111 是否小於 bucket 的數量。 * 若 x & mask111 小於 bucket 的數量 * 直接返回 x & mask111 作為結果 * 若 x & mask111 不小於 bucket 的數量 * 取 x 的較高位數再跟 mask011 進行 and 運算 綜合以上兩種情況可以得到以下程式碼 ```c return (leq * (x & mask111)) + // IIII ((1 - leq) * ((x >> (N_BITS + 1)) & mask011)); // JJJJ ``` ## 測驗 `4` * 運作原理 一開始先將 x 減去 1 ,這樣就可以將問題轉成尋找 x 之中最高位的 1 。 程式中的 r 用來記錄總向右位移的位元數目。 利用二分搜尋的方式,尋找最高位的 1 所在的位置。 第一次先判斷 x 是否大於 0xff 來得知最高位在第 0~15 bit 之間還是在 16~31 bit 之間。若最高位在 16~31 bit 之間,則將 x 向右位移 16 位,下一次就只考慮最高位是在 0~15 bit 之間的何處,並且同時用 r 紀錄位移的數量。 ```c r = (x > 0xFFFF) << 4; x >>= r; ``` 接著以此類推,將 x 與 0xff 來做比較,藉此得知最高位在 0~7 bit 之間或是 8~15 bit 之間,並且用 r 紀錄位移的數量。 ```c shift = (x > 0xFF) << 3; x >>= shift; r |= shift; ``` 接著重複此過程直到範圍內剩下二個 bit 為止。 ```c shift = (x > 0xF) << 2; x >>= shift; r |= shift; shift = (x > 0x3) << 1; x >>= shift; ``` 此時範圍內只剩下 0~1 bit 兩個位元。 首先將 r 跟 shift 的值相加 ( 運用 bitwiese or 運算速度較快 ) ,若最高位發生在第 1 個 bit 則要將答案再加一,因此再跟 ```x >> 1```做 or 運算。最後由於是向上取整,因此整個答案要再加上 1 。 ```c return (r | shift | x >> 1) + 1; ``` * 支援x = 0 再最一開始用一個變數紀錄 x 是否為 0,若 x 為 0 則將答案乘以 0 ,若不為 0 則乘以 1 。 ```c int ceil_log2(uint32_t x) { uint32_t r, shift; uint32_t y = x != 0; x--; r = (x > 0xFFFF) << 4; x >>= r; shift = (x > 0xFF) << 3; x >>= shift; r |= shift; shift = (x > 0xF) << 2; x >>= shift; r |= shift; shift = (x > 0x3) << 1; x >>= shift; return ((r | shift | x >> 1) + 1) * y; } ```