--- tags: linux2023 --- # 2023q1 Homework2 (quiz2) contributed by < [Jerejere0808](https://github.com/Jerejere0808) > ## 測驗一 將最高位元數下的 bits 改為 1 ,最後再加 1 進位,得到大於其值最接近的2的冪。 AAAA = 1 BBBB = 32 CCCC = x + 1 用 __builtin_clz() 簡化程式碼 ```c #include <stdint.h> #include <stdio.h> uint64_t next_pow2(uint64_t x) { int lz = __builtin_clz(x); return 1 << (64 - lz); } ``` :::spoiler ``` .file "q1.c" .text .p2align 4 .globl next_pow2 .type next_pow2, @function next_pow2: .LFB13: .cfi_startproc endbr64 bsrl %edi, %edi movl $64, %ecx movl $1, %eax xorl $31, %edi subl %edi, %ecx sall %cl, %eax cltq ret .cfi_endproc .LFE13: .size next_pow2, .-next_pow2 .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" .section .note.GNU-stack,"",@progbits .section .note.gnu.property,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4: ``` ::: 上為產生的 x86 指令 其中指令 "bsrl %edi, %edi" 在暫存器 %edi 的值執行了 bit scan reverse operation ,尋找 %edi 值中最高位的設定位元(即最左邊的1位元),並將其位置存儲在 %edi 中。 在 Linux 核心原始程式碼找出類似的使用案例並解釋: ## 測驗二 DDD = i & (i - 1) == 0 判斷 i 是否為二的冪,ex: 100 & 011 = 0。 若為真 len 就加一,因為會多出一個bit EEEE = (ans<<len) 移出 len 個 bits 的空間 用 __builtin_ctz() 改寫,若目前 len 跟 二進位的 i 右側連續的 0 bits 數量一樣就代表 len 必須加一 ans 才可以挪出足夠的空間給二進位的 i (因為i最前面會多出一個 1 bit) ``` int concatenatedBinary(int n) { const int M = 1e9 + 7; int len = 0; /* the bit length to be shifted */ long ans = 0; for (int i = 1; i <= n; i++) { len = len + (__builtin_ctz(i) == len); ans = (i | (ans<<len)) % M; } return ans; } ``` 改進 mod 1e9 + 7 的運算 (還在思考) ## 測驗三 因為用了 SWAR 增加效率,變成一次處理 64 bits(8 個 bytes),所以先算最大 8 的倍數的 bytes 數量其已經利用 SWAR 算出 continuation byte(s) 數量 所以 count 就變成總量減去continuation byte(s) 數量也就是去除 continuation byte(s) 的bytes數 剩下不能利用 SWAR 的就使用 count_utf8 求出去除 continuation byte(s) 的bytes數,最後將其相加。 AAAA = 3 BBBB = 7 CCCC = 7 ```c count = (1 << 3) * (len / 8) - count; count += (len & 7) ? count_utf8((const char *) end, len & 7) : 0; return count; ``` 在 Linux 核心原始程式碼找出 UTF-8 和 Unicode 相關字串處理的程式碼(還在找應該會從/include/linux/unicode.h 挑選) ## 測驗四 ```c bool is_pattern(uint16_t x) { const uint16_t n = ~x + 1; return (n ^ x) < x; } ``` EEEE = ~x + 1 FFFF = x 符合 pattern 的二進位數在有效最大位數一定是 1' bit,且跟最低位的 1 之間不能有 0' bit,如也就是說若出現 1...01...0 這個情況就不符合,~x + 1 會讓 1…01…0 變成 1...11...0 透過(n ^ x) 會變成 1...10...0 會比原本 x 大。若是原本符合 pattern 的 x 透過這個操作就會變小。(想很久= =)