sysprog
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Linux 核心專題: 位元運算 > 執行人: alexc-313 > [解說錄影](https://youtu.be/n0v-YSs6-Ns) ### Reviewed by `MikazukiHikari` > 牛頓迭代法 ```c // 檢查是否收斂 if (new_guess == guess || (new_guess > guess && new_guess - guess <= 1) || (guess > new_guess && guess - new_guess <= 1)) { // 返回較小的整數作為平方根的整數部分 return (new_guess * new_guess <= x) ? new_guess : new_guess - 1; } ``` 原判斷條件三重且略微冗餘,是否可簡化為: ```c if (abs(new_guess - guess) <= 1) ``` > [name=alexc-313] > 經過測試後發現此方法是可行的,感謝回饋。 ### Reviewed by `rota1001` 在浮點數開平方根的部份,是否有注意到指數部份是奇數的情況?在程式碼部份我有看到這個問題的處理,不過文字敘述方面沒有講到這個,如果補上會比較完整。 > [name=alexc-313] > 已補上文字說明,感謝回饋。 ### Reviewed by `thwuedwin` > 我們可以用最簡單的二分查找法來實作開平方根,此方法簡單但運用多次除法,成本較高。 你的 `x` 是整數,所以你判斷 `mid <= x / mid` 這個是不是可以換成 `mid * mid <= x`。頂多確認一下是否有溢位,但有溢位就代表 `mid` 還太大。除以 2 也可以換成位元運算。解決了除法的問題,二分查找法還有什麼問題? > [name=alexc-313] > 就算換成 `mid * mid <= x` 且假設檢查溢位的成本可忽略,以及用位元運算取代除法,相比於位元運算法,二分查找法需要利用乘法實際計算平方根,且在最差情況下,位元運算法需要\~15次迴圈,而二分查找法需要~31次迴圈。 另外你確定你的浮點數平方根是正確的嗎?你只計算 mantissa 的部分,再乘上 exponent 的部分,你算出來的平方根值都會是偶數吧?會有一堆精度被吃掉。 > [name=alexc-313] > 已補上測試結果,若使用 64 位元則不會損失任何精度,也不會有都是偶數的問題。 ### Reviewed by `Ian-Yen` > int mid = left + (right - left) / 2; 這邊程式碼中使用要 `/ 2` 的部分,能夠使用位元運算應盡量使用位元運算,也就是 ((right - left) >> 1) > [name=alexc-313] > 感謝回饋,在二分查找法與牛頓迭代法的程式碼中確實有許多能改進的地方。 ### Reviewed by `Max042004` > 好難,還在看 > 情境 0 的 `while (bit > x) bit >>= 2` 也可以用 clz,最差情況下應該會比一次移兩位好。 > [name=alexc-313] > 感謝您的回饋,已用 clz 對程式做出改進,並確認使用 __builtin_clzll 能夠達到與 sqrtf 相近的效能與精度。 ### Reviewed by `LeoriumDev` > `int_sqrt` 函式當中 while 迴圈判斷式可簡化為: > ```diff > - while (bit != 0) { > + while (bit) { > ``` > > 可利用位元運算將程式碼從 3 行簡化為 2 行: > ```diff > - uint64_t bit = (uint64_t)1 << 62; > - int lz = __builtin_clzll(x); > - bit >>= lz - (lz & 1); > > + int shift = (63 - __builtin_clzll(x)) & ~1; > + uint64_t bit = 1ULL << shift; > ``` > [name=alexc-313] > 感謝回饋,確實此做法會讓程式更精簡。 ### Reviewed by `h0w726` >牛頓迭代法 ```c // 初始猜測值,可以選擇 x/2 或其他合理的值 int guess = x / 2; ``` 選擇 $\frac{2}{x}$ 的原因是什麼,其他合理的值要怎麼猜測? > [name=alexc-313] > 感謝回饋,選擇 $\frac{x}{2}$ 的原因是因為其最直觀,也可以選擇其他初始值,例如當 $\ x \in [1, 100]$ 時使用 `guess = 0.4 * x + 0.6` ## TODO: 以定點數實作開平方根 > 情境 0: 輸入是 int 型態 (採用 LP64 data model),輸出也是 int 型態 > 情境 1: 輸入是 IEEE float (單精度),輸出是 int 型態 > 情境 2: 輸入是 IEEE float,輸出也是 float 型態 > 針對上述情境,皆不可使用 FPU,只能藉由定點數予以計算,務必降低誤差並比較 glibc 的效能表現,提出改進方案。要有數學分析,並提供多種實作手法 ### 情境 0 #### 二分查找法 ```c int bin_sqrt(int x) { if (x < 0) return 0; if (x <= 1) return x; int left = 0, right = x; int result = 0; while (left <= right) { int mid = left + (right - left) / 2; if (mid <= x / mid) { left = mid + 1; result = mid; } else { right = mid - 1; } } return result; } ``` 我們可以用最簡單的二分查找法來實作開平方根,此方法簡單但運用多次除法,成本較高。 #### 牛頓迭代法 ```c int new_sqrt(int x) { if (x < 0) return 0; if (x <= 1) return x; // 初始猜測值,可以選擇 x/2 或其他合理的值 int guess = x / 2; // 避免無限循環,設置最大迭代次數 int max_iterations = 20; int iteration = 0; while (iteration < max_iterations) { int new_guess = (guess + x / guess) / 2; // 檢查是否收斂 if (new_guess == guess || (new_guess > guess && new_guess - guess <= 1) || (guess > new_guess && guess - new_guess <= 1)) { // 返回較小的整數作為平方根的整數部分 return (new_guess * new_guess <= x) ? new_guess : new_guess - 1; } guess = new_guess; iteration++; } return (guess * guess <= x) ? guess : guess - 1; } ``` #### 位元運算法 ```c int bit_sqrt(int x) { uint32_t res = 0; uint32_t bit = 1UL << 30; while (bit > x) bit >>= 2; while (bit != 0) { if (x >= res + bit) { x -= res + bit; res = (res >> 1) + bit; } else res >>= 1; bit >>= 2; } return res; } ``` 此方法基於逐位試探法,從最高有效位開始,逐步向下檢查每個可能的位是否應該包含在平方根中。它類似於長除法的手動計算方式。 * 初始化: `uint32_t bit = 1UL << 30;`,$2^{32}$ 的平方根為 65536 ,需要 16 位元來表示,所以再任意 32 位元的整數中,最大的可能平方根需要用 15 位元來表示,又因為開平方後的位元大約為原本的兩倍,所以從 `1 << 30` 開始。接下來用 `while (bit > x) bit >>= 2;` 快速移動 `bit` 至初始位置。 * 主迴圈: 1. `if (x >= res + bit)` → 測試是否可以加入當前 bit。 2. `x -= res + bit` → 減去對應的平方貢獻。 3. `res = (res >> 1) + bit` → 更新平方根。 4. `bit >>= 2` → 處理下一個更低的位。 測試程式: ```c bool err = false; for (size_t i = 0; i < INT_MAX; i++) { int a = my_sqrt(i), b = sqrt(i); if (a != b) { printf("your answer: %d, correct answer: %d\n", a, b); err = true; } } if (!err) { printf("all correct\n"); } ``` 以上方法皆通過此測試。 #### 效能測試 ![Figure_2](https://hackmd.io/_uploads/rynBTpWrxe.png) ### 情境 1 首先我們先分析單精度 IEEE 754: ![upload_2c378b894fc4efabab3388eae732bc4f](https://hackmd.io/_uploads/BJXyh1lBle.png) 單精度 IEEE 754 可分為三個部分,分別為 Sign (S)、 Exponent (E)、 Mantissa (M),以下分別討論各部分在求平方根時需做的處理。 由於我們不討論虛數,所以可忽略 S,而 E 及 M 可以表示為: $$\sqrt{M \cdot 2^{E-127}} = \sqrt{M} \cdot 2^{\frac{E-127}{2}}$$由此可知,我們只須求出 M 的平方根,再將其乘上 $2^{\frac{E-127}{2}}$ 就可得出浮點數的平方根。 在不可使用 FPU,只能藉由定點數予以計算的條件下,我們先將 M 轉換為定點數,利用上述的位元運算法求出其平方根,再將其轉換為 int 即可得到浮點數的平方根。若遇到 $2^{\frac{E-127}{2}}$ 為奇數的狀況,則將 M 用位移方式乘二,就可避免 `e >>= 1` 奇數時損失精度。 ```c int my_sqrt(float x) { if (x < 1) return 0; if (x == 1.0f) return 1; uint32_t bits; memcpy(&bits, &x, sizeof(x)); int e = ((bits >> 23) & 0xFF) - 127; uint32_t m = (bits & 0x7FFFFF) | 0x800000; //Q16.16 m >>= 7; if (e & 1) m <<= 1; uint32_t root = int_sqrt(m); //Q8.8 e >>= 1; root <<= 8; //convert from Q8.8 to Q16.16 uint64_t root64 = (uint64_t)root; root64 <<= e; return root64 >> 16; } ``` 測試結果如下: ``` Input my_sqrt sqrtf (int) Abs Error --------------------------------------------------------------- 0.000000e+00 0 0 0 -1.000000e+00 0 -2147483648 -2147483648 1.000000e-10 0 0 0 1.000000e-04 0 0 0 2.500000e-01 0 0 0 5.000000e-01 0 0 0 7.500000e-01 0 0 0 1.000000e+00 1 1 0 1.500000e+00 1 1 0 2.000000e+00 1 1 0 3.141590e+00 1 1 0 4.000000e+00 2 2 0 9.000000e+00 3 3 0 1.600000e+01 4 4 0 1.000000e+02 10 10 0 1.234568e+04 111 111 0 1.000000e+10 99840 100000 -160 ``` 首先當 x 為 $-1.000000 \times 10^{0}$ 時,sqrtf (int) 回傳的數值很詭異,這是因為原本回傳值為 `nan` ,再轉換為 int 所造成的結果。 再來看到當 x 為 $1.000000 \times 10^{10}$ 時,此方法產生了顯著的誤差,推斷是我使用 Q16.16 表示原有23位 mantissa 的浮點數,在 `m >>= 7` 時損失了後七位元提供的精度。 為了確認我的推斷,我改進了 `int_sqrt` 以及 `my_sqrt`,用 uint64_t 讓我能夠用 Q32.32 表示浮點數。 ```c uint64_t int_sqrt64(uint64_t x) { uint64_t res = 0; uint64_t bit = (uint64_t)1 << 62; while (bit > x) bit >>= 2; while (bit != 0) { if (x >= res + bit) { x -= res + bit; res = (res >> 1) + bit; } else res >>= 1; bit >>= 2; } return res; } int my_sqrt64(float x) { if (x < 1) return 0; if (x == 1.0f) return 1; uint64_t bits; memcpy(&bits, &x, sizeof(x)); int e = ((bits >> 23) & 0xFF) - 127; uint64_t m = (bits & 0x7FFFFF) | 0x800000; //Q32.32 m <<= 9; if (e & 1) m <<= 1; uint64_t root = int_sqrt64(m); //Q16.16 e >>= 1; root <<= 16; //convert from Q16.16 to Q32.32 root <<= e; return root >> 32; //convert from Q32.32 to int } ``` 測試結果如下: ``` Input my_sqrt64 sqrtf (int) Abs Error --------------------------------------------------------------- 0.000000e+00 0 0 0 -1.000000e+00 0 -2147483648 -2147483648 1.000000e-10 0 0 0 1.000000e-04 0 0 0 2.500000e-01 0 0 0 5.000000e-01 0 0 0 7.500000e-01 0 0 0 1.000000e+00 1 1 0 1.500000e+00 1 1 0 2.000000e+00 1 1 0 3.141590e+00 1 1 0 4.000000e+00 2 2 0 9.000000e+00 3 3 0 1.600000e+01 4 4 0 1.000000e+02 10 10 0 1.234568e+04 111 111 0 1.000000e+10 100000 100000 0 ``` 可見此版本能夠通過 x 為 $1.000000 \times 10^{10}$。 #### 效能測試 ![Figure_1](https://hackmd.io/_uploads/HJ13sgMBeg.png) 由於效能並不理想,我再次審視程式碼後發現,由於 m = (bits & 0x7FFFFF) | 0x800000 ,只會用到 24 位元,這代表不需要使用 uint64_t ,可以使用 uint32_t 並採用 Q8.24 表示法,這樣不僅節省記憶體的使用,也可以增加效率 ```c int my_sqrt_new(float x) { if (x < 1) return 0; if (x == 1.0f) return 1; uint32_t bits; memcpy(&bits, &x, sizeof(x)); int e = ((bits >> 23) & 0xFF) - 127; uint32_t m = (bits & 0x7FFFFF) | 0x800000; //Q16.16 m <<= 1; //Q8.24 if (e & 1) m <<= 1; uint32_t root = int_sqrt(m); //Q4.12 e >>= 1; root <<= e; return root >> 12; } ``` #### 效能測試 ![Figure_1](https://hackmd.io/_uploads/rJVVR-fHlx.png) 可見效能相較於先前有顯著的進步,與 glibc 相近。 ### 情境 2 ```c float my_sqrt_f(float x) { if (x < 0) return NAN; uint64_t bits; memcpy(&bits, &x, sizeof(x)); int e = ((bits >> 23) & 0xFF) - 127; uint64_t m = (bits & 0x7FFFFF) | 0x800000; //Q32.32 m <<= 9; if (e & 1) m <<= 1; uint64_t root = int_sqrt64(m); //Q16.16 e >>= 1; // Reconstruct float manually uint32_t new_bits = ((e + 127) << 23) | ((root << 7) & 0x7FFFFF); float result; memcpy(&result, &new_bits, sizeof(result)); return result; } ``` 這裡和情境 1 所用的方法類似,差別在於最後不是將定點數轉為 int 表示,而是重組成 float。 `(root << 7)` 會將原本 Q16.16 轉換為 23 位元的 mantissa ,再搭配 `(e + 127)` 的位移,就完成 float 的重組了。 測試結果如下: ``` Input my_sqrt_f sqrtf Abs Error --------------------------------------------------------------- 0.000000e+00 0.000000 0.000000 0.000000 -1.000000e+00 nan nan nan 1.000000e-10 0.000010 0.000010 -0.000000 1.000000e-04 0.010000 0.010000 -0.000000 2.500000e-01 0.500000 0.500000 0.000000 5.000000e-01 0.707100 0.707107 -0.000007 7.500000e-01 0.866020 0.866025 -0.000005 1.000000e+00 1.000000 1.000000 0.000000 1.500000e+00 1.224731 1.224745 -0.000013 2.000000e+00 1.414200 1.414214 -0.000014 3.141590e+00 1.772446 1.772453 -0.000008 4.000000e+00 2.000000 2.000000 0.000000 9.000000e+00 3.000000 3.000000 0.000000 1.600000e+01 4.000000 4.000000 0.000000 1.000000e+02 10.000000 10.000000 0.000000 1.234568e+04 111.110352 111.111107 -0.000755 1.000000e+10 100000.000000 100000.000000 0.000000 ``` 可見在非整數時會有些許誤差,我推斷是 `uint64_t root = int_sqrt64(m); //Q16.16` ,由於開根號後會從原本的 Q32.32 降成 Q16.16 ,此時只有 16 bit 來表示原本 23 位元的 mantissa ,犧牲的精度會造成些微誤差。 於是我決定採用 Q18.46 來表示 float ,這樣開根號後就變成 Q9.23 ,能夠有效的保留小數點後的精度。 ```c float my_sqrt_f_new(float x) { if (x < 0) return NAN; uint64_t bits; memcpy(&bits, &x, sizeof(x)); int e = ((bits >> 23) & 0xFF) - 127; uint64_t m = (bits & 0x7FFFFF) | 0x800000; //Q18.46 m <<= 23; if (e & 1) m <<= 1; uint64_t root = int_sqrt64(m); //Q9.23 e >>= 1; // Reconstruct float manually uint32_t new_bits = ((e + 127) << 23) | ((root ) & 0x7FFFFF); float result; memcpy(&result, &new_bits, sizeof(result)); return result; } ``` 測試結果如下: ``` Input my_sqrt_f_new sqrtf Abs Error --------------------------------------------------------------- 0.000000e+00 0.000000 0.000000 0.000000 -1.000000e+00 nan nan nan 1.000000e-10 0.000010 0.000010 0.000000 1.000000e-04 0.010000 0.010000 0.000000 2.500000e-01 0.500000 0.500000 0.000000 5.000000e-01 0.707107 0.707107 0.000000 7.500000e-01 0.866025 0.866025 0.000000 1.000000e+00 1.000000 1.000000 0.000000 1.500000e+00 1.224745 1.224745 -0.000000 2.000000e+00 1.414214 1.414214 0.000000 3.141590e+00 1.772453 1.772453 -0.000000 4.000000e+00 2.000000 2.000000 0.000000 9.000000e+00 3.000000 3.000000 0.000000 1.600000e+01 4.000000 4.000000 0.000000 1.000000e+02 10.000000 10.000000 0.000000 1.234568e+04 111.111107 111.111107 0.000000 1.000000e+10 100000.000000 100000.000000 0.000000 ``` 可見所有測試皆與 `sqrtf` 的結果相同。 #### 效能測試 ![Figure_1](https://hackmd.io/_uploads/By4UYlfrxx.png) 由於需使用 64 位元的 `int_sqrt64` ,所以效能與 `sqrtf` 有顯著差距。 若要增加效能,我們可以只用 uint32_t ,並用 Q2.30 來表示 float ,但就會損失 8 位元的精確度。 ```c float my_sqrt_f32(float x) { if (x < 0) return NAN; uint32_t bits; memcpy(&bits, &x, sizeof(x)); int e = ((bits >> 23) & 0xFF) - 127; uint32_t m = (bits & 0x7FFFFF) | 0x800000; //Q2.30 m <<= 7; //Q2.30 if (e & 1) m <<= 1; uint32_t root = int_sqrt(m); //1.15 e >>= 1; // Reconstruct float manually uint32_t new_bits = ((e + 127) << 23) | ((root << 8) & 0x7FFFFF); float result; memcpy(&result, &new_bits, sizeof(result)); return result; } ``` 測試結果如下: ``` Input my_sqrt_f32 sqrtf Abs Error --------------------------------------------------------------- 0.000000e+00 0.000000 0.000000 0.000000 -1.000000e+00 nan nan nan 1.000000e-10 0.000010 0.000010 -0.000000 1.000000e-04 0.010000 0.010000 -0.000000 2.500000e-01 0.500000 0.500000 0.000000 5.000000e-01 0.707092 0.707107 -0.000014 7.500000e-01 0.866013 0.866025 -0.000013 1.000000e+00 1.000000 1.000000 0.000000 1.500000e+00 1.224731 1.224745 -0.000013 2.000000e+00 1.414185 1.414214 -0.000029 3.141590e+00 1.772430 1.772453 -0.000023 4.000000e+00 2.000000 2.000000 0.000000 9.000000e+00 3.000000 3.000000 0.000000 1.600000e+01 4.000000 4.000000 0.000000 1.000000e+02 10.000000 10.000000 0.000000 1.234568e+04 111.109375 111.111107 -0.001732 1.000000e+10 100000.000000 100000.000000 0.000000 ``` 但在效能上就能與 glibc 相提並論 ![Figure_3](https://hackmd.io/_uploads/SkjY-WfHxe.png) #### 感謝 `Max042004` 提出可以使用 clz 改進 `while (bit > x) bit >>= 2` 。 ```c uint64_t int_sqrt(uint64_t x) { uint64_t res = 0; uint64_t bit = (uint64_t)1 << 62; /*while (bit > x) bit >>= 2;*/ //replaced by __builtin_clzll int lz = __builtin_clzll(x); bit >>= lz - (lz & 1); while (bit != 0) { if (x >= res + bit) { x -= res + bit; res = (res >> 1) + bit; } else res >>= 1; bit >>= 2; } return res; } ``` 這邊我使用 `<x86intrin.h>` 中的 `__builtin_clzll` 快速得出 clz ,取代原本的 while 迴圈。 測試結果如下: ``` Input my_sqrt_f_new sqrtf Abs Error --------------------------------------------------------------- 0.000000e+00 0.000000 0.000000 0.000000 -1.000000e+00 nan nan nan 1.000000e-10 0.000010 0.000010 0.000000 1.000000e-04 0.010000 0.010000 0.000000 2.500000e-01 0.500000 0.500000 0.000000 5.000000e-01 0.707107 0.707107 0.000000 7.500000e-01 0.866025 0.866025 0.000000 1.000000e+00 1.000000 1.000000 0.000000 1.500000e+00 1.224745 1.224745 -0.000000 2.000000e+00 1.414214 1.414214 0.000000 3.141590e+00 1.772453 1.772453 -0.000000 4.000000e+00 2.000000 2.000000 0.000000 9.000000e+00 3.000000 3.000000 0.000000 1.600000e+01 4.000000 4.000000 0.000000 1.000000e+02 10.000000 10.000000 0.000000 1.234568e+04 111.111107 111.111107 0.000000 1.000000e+10 100000.000000 100000.000000 0.000000 ``` #### 效能測試 ![Figure_1](https://hackmd.io/_uploads/B1dFt0zSgl.png) 可以看到此版本在精度與效能都達到與 glibc 中的 sqrtf 相近的表現,再次感謝 `Max042004` 提出改進的想法! > 以定點數計算 EWMA,闡述其原理、適用場景,並探討誤差,要有數學分析。 ## TODO: 探討上述在 Linux 核心的應用 > 搭配課程教材,理解開平方根和 EMWA 如何應用於 Linux 核心 (如 CPU 排程器),予以探討其實作手法

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    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.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully