Veternal1226
    • Create new note
    • Create a note from template
      • 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
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me 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
    • Save as template
    • 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 Create Help
Create Create new note Create a note from template
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
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me 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
    # 進階電腦系統理論與實作 ###### tags: `sysprog2020` `lesson` --- [TOC] --- # Lesson_1 P19 把每個位元反過來 bit order 用於網際網路傳輸時 將每個位元順序反轉 P20 把負有號整數最小值取絕對值 會得到負值 一補數有在用 用在網際網路 ```c void remove_list_node(List *list, Node *target) { Node *prev = NULL; Node *current = list->head; // Walk the list while (current != target) { prev = current; current = current->next; } // Remove the target by updating the head or the previous node. if (!prev) list->head = target->next;//可能是開頭 else prev->next = target->next; } ``` ```c void remove_list_node(List *list, Node *target) { // The "indirect" pointer points to the *address* // of the thing we'll update. Node **indirect = &list->head; // Walk the list, looking for the thing that // points to the node we want to remove. while (*indirect != target) indirect = &(*indirect)->next; *indirect = target->next; } ``` 指標的指標不是成對的 不是double 有從屬關係 排程用linked list的空間效率很好 下次測驗題會考加前面的addentry 指標的指標不用return就能更新頭 如何用指標的指標改寫swap_pair 讓使用時不用head=swap_pair(head) 變成swap_pair(&head) 9/20作業deadline O(1)指的不是都是固定執行時間,而是隨著資料量input增加而不會影響執行時間的分布 --- # Lesson_2 Xor Filter integer overflow ``` copy_from_kernel(void *user_dent,int maxlen) ``` maxlen傳入負數就可以拿到一大塊kernel資料 bitwise 1000 & 0111 =0 ->power of 2 'A'與'a'剛好差32 bitmask '\0'叫編號0 16進位的0x80很重要,因為位於0x00~0xFF中央 一口氣處理4byte 裡面是不是有編號0 XOR在加密學很重要 用於圖片可使肉眼看不出 cloc main.c 用來看有幾行 sizeof使用size_t 定義在typedef'd 為無號 XOR 32相當於大小寫轉換 jitblit半透明字 下下週 亞馬遜工程師上課 SIMD (Single Instruction, Multiple Data) MMM=dd MASK=cc AAA=aa BBB=db XXX=bf YYY=ac VV1=e VV2=b VV3=a VV4=e KKK=dc JJJ=cd in=100 0110 1000110+shift=00001111 shift=1000001 110 0001 100 0001 000 0001 MASK找AAABBB共同點 AB剛好差2^5 MASK不能大於a跟A 但又不能小於16 3跟6是湊出來的 延伸: MASK=0x20會不會shift不了 single number III 260: 有兩個只出現一次 其他明確只出現兩次 要constant space linear runtime complexity leetcode 137: 2次可以只用XOR 3次會需要狀態 這題有三個變形 lower跟1次有關 higher跟2次有關 eg:[9,3,3,3,7,7,7] i=0 lower=1001(bitwise XOR) 1001(bitwise AND) higher=1001(bitwise XOR) 1001(bitwise AND) 第3題 快速除法 128事為了考慮乘的範圍 處理掉jemalloc內的magic 把str[]改成str* 會有string litera問題 因為會是read only 實作會有修改 --- # Lesson_3 \*void與\*char行為相同 從void轉成其他型態行為會相同 但從其他型態轉回void行為就可能不同 sizeof()指標一定是bus寬度 Word 早期:有36位元的電腦,出現的比32位元還早 (36->16->32) 因為長度剛好適合浮點數 他的word就是36bit 若要取1~4 要先取0~3再取4~8 取兩次 如果硬體支援unaligned access 則可以只取1次 ```cpp=0 void func(int *x) { return *x; } int n = 0; printf("%d %d %d\n", n++, n++, n++); printf("%d %d %d\n", func(&n), func(&n), func(&n)); ``` 第2行,不知道是哪個n會先被++ 地區不同: 123456.789 美制 123,456.789 歐制 123.456,789 --- 5=0...0101 -5=1...1011 -3=1...1101 4=00000100 8=00001000 16=00010000 32=00100000 64=01000000 ``` (num & (num - 1))==0 && !(__builtin_ctzl(num) OPQ); ``` __builtin_ctzl(num) OPQ=FALSE 2=0x10 10&01=0 10^01=0 4=0x100 100&001=0 100^001=1 ->expect 0 6=0x110 8=0x1000 3=0x11 11&01=1 11^01=10 ->expect 1 5=0x101 101&001=1 101^001=100 bfg ctz = count tail 0 4位元clz = count leading 0 001=2 010=1 Expo Gol coding跟資料壓縮有關 3. 某數值 除幾次2會變0 pop clz 延伸:最多1分支 KKK:因為每次改是要1->0 0->1所以XOR OP1bb OP2ac m=0 OPQff AAAaa BBBbb XXXdb v YYYbe u <\<shift KKKde bitset & -bitset 5.bitarray 輸入是bitmap size=64 作用是把bitmap的 如果bitmap全0 回傳的pos=0 如果最低位元是1 回傳的pos=64 第1個被set t = bitset & -bitset bitset=1111 t=0001 (1111-1110) 再利用XOR把該bit反向 KKK要找的是最低位在哪 處理指定區塊(block) 4.GCD 原: 取餘數 直到v==0 改成沒有使用取餘數與乘法的方式 第一個for (u|v)&1 其中一個是不是奇數 !((u|v)&1) 檢查兩個是不是都是偶數 可以先把2提出來 while(!(u&1)) u/=2 確認u一定是奇數 一偶一奇 奇數那邊一定沒有二 BMI=Bit Manipulation Instruction https://en.wikipedia.org/wiki/Bit_manipulation_instruction_set 以CPU優化很常會造成漏洞 --- # Lesson_4 OP=cc ^ AAA=ab int **parent BBB=db-1 CCC=bf1 << i KK1=aa div5 KK2=ad div3 KK3=dc 2 CASES= (bcf) 第三題fizzbuzz C-style string 做reverse+可讀性 要寫會不會回傳新head 如果不回傳 要解釋做完後head就會動(pointer to pointer版) sort也會 如果size=2也不用用到遞迴&merge sort >linked list多大才需要用到merge sort? 寫程式前就要考慮範圍 git commit --amend insert & delete 有修改size卻不在同一次commit -> 不好 不好追溯 散落在好幾個commit commit message 寫出限制,TODO... ### 浮點數 bfloat16 長度 16bytes長 --- # Lesson_5 (10/6) render 修正牆壁透視錯誤 修正鋸齒畫面 左邊(鋸齒化)用fixed point 實作 右邊(牆壁透視)用floating point實作 程式碼內有其他錯 要找出來 下周 開始講自駕車 Software Define Everything 資料量(傳輸&處理)每秒1.8G ICS NCKU 拿物普通價廉的商業處理器作工業控制 偶爾會當機,但這不是工業控制要的 自駕車 難的是軟體驗證(模擬器模擬情景要夠多,但又不能太延遲) #### 浮點數 符合交換律:屬於阿貝爾群 不用FPU浮點數運算單元,只用ALU對浮點數\*2 出現場景:懷疑FPU有設計錯、需要製作硬體電路 實驗不能只用快跟慢,還要分析能跟不能 10進位的7轉二進制會是111,但轉成浮點數不會是111 --- 隨堂考 1.遞迴&除法 有前提 是浮點數除"整數" 可搭配上課的浮點數\*2 想法:若能先除二就先除(右移) https://hackmd.io/@billsun/B1gii716N 2.實作根號2 3.Leetcode 829 想法:從N到1 i要用減的 D1=cc 2 D2=dd 1 QQ0=aa 0x01000000 QQ1=dd 0x3f000000 QQ2=gg 23 ZZZ=dd 1-i 2. 牛頓法比較快逼近的原因 QQ0後目的:逼近(牛頓法) leetcode 69 fail原因:逼近速度太慢 3. 改進點:除法可以改進 --- 美國FAANG FB討論區 ##### Dict 用bloom filter預測是不是在dict裡 時間複雜度 缺陷:都市很多會產生hash碰撞,會有false positive 因為有幾個hash func是固定的 所以要分析裡面兩個hash func分散夠不夠平均 原本的bloom,受到資料刪除時會不知道有被刪 如果刪除不用bit set check(?),bloom會花更多搜尋tst時間 XOR filter 有4次hash >fingerprinthash: >作用: >把元素變成hash >與bit set做XOR比對 > >同學實驗的是64-bit,空間浪費很多,但hash很平均 優點:建立的過程 Algorithm 3會做隨機,連seed一起包進去 把set做XOR 補充:deletable bloom filter perf refcnt: ReferenceCounted 下周可能用線上上課 --- # Lesson_6(10/13) Create Read Update Delete compiler 也有分frontend與backend Mars Pathfinder priority inversion gcc只是compiler driver 真的編譯器是cc1 render翻為算繪 不翻為渲染 用在牆壁叫粉刷 database render展現特定搜尋結果 技巧篇 BB1=f(c) 0xff800000 BB2=e(e) 0xffff0000 RB1=a(a) 1 RB2=a(a) 1 A=a(d) 7 B=b(c) 4 C=c(b) 5 D=d(a) 9 SS1=d(d) `node->next = *head` SS2=b(b) `*head = node` CCC=b(b) c1 < c2 (struct llist[]){ccc,D} 525 k num[k] bit=1 ,2 ,3 ,4 0000 0101 0 1 0 0 0 1 0 0 0001 0010 1 1 0 1 0 1 0 0 0010 0101 1 2 1 1 0 2 0 0 1010 ### 3 先拉node->next = head 在換head =node ## 討論 ### 1 `*pr &= BB1;` 作用:取出sign+exp 所以選==0xff800000== 目標:把23位元砍到變7位元,且不能動到sign&exp 因為bitwise是對整數 所以要強制轉型(`int *pr = (int *) &r;`) 23到7是右位移16 所以除以256(`r /= 256;`) 如果不能表示到10萬或1萬等高位數會有更嚴重的誤差 把最低的16bit去掉 選==0xffff0000== ### 2 問題:沒有malloc 放在哪? 答:`static T static_ringbuf_mem[S + 1];` 問:如果把static拿掉會怎樣 答:init兩個以上會出事,此處舉例因為沒有重新進入同一塊所以沒事 c static ``` static int x; int main(){ static int y; y++; } ``` x只能在此檔看的到(visibillity:private(only visible in the file)) y在此function外的也是用同一塊,例如遞迴,沒static會一直用新的,有static會用同一塊,所以會一直++ 大括號好處:會是獨立scope ``` { int i=9; i++ } { int i=99; i++ } ``` 這樣寫會合法,不會有重複宣告問題 gcc -E | less 停在preprocessor觀察展開後結果 ### 3 先把A串到null 再串B到list 再串C到list (struct llist[]){{y, x}} :compound literal compound:由兩個或多個東西組合而來的東西 描述時型態使用指標 將數值跟指標結合,即能實作list 所以要倒過來插入 依序為 7 4 5 9 ``` if (!*head || (*head)->val >= node->val) { SS1; SS2; return; } ``` 把新節點插入已經排序好的list insertion sort: avg:$O(n^2)$ worse:$O(n^2)$ best:$O(n)$ tile:確定剩下的很少時換成insertion sort等較快的 ## 作業 ### render 因為圖檔是用cpp寫的所以用cpp RayCaster: floatCaster fixedCaster main loop: ``` while(!isExiting){ floatRenderer.TraceFrame(...) ... } ``` 兩邊能同步的原因: 處理好後都丟到一段buffer fix放一邊float放一邊 更新螢幕時使用這段buffer做更新 因此能同步更新 延伸:手機/電腦更新螢幕原理:ping pong buffer 一邊拿來顯示 另一邊拿來放處理完的新資料,然後交換顯示跟新資料 作業:理解複雜程式執行 ### kcalc 計算機 必須使用bfloat16,原程式碼是用32bit float 執行在linux kernel,因為kernel不建議(不允許)使用float --- # Lesson_7 (10/20) ## linker linker分兩種:動態/靜態 dynamic linker是OS的一部份 mazu rocket simulator ``` static int x; ``` >Visibility ``` int func(){ static int y; } ``` >靜態配置空間,不會隨func離開而釋放 降低symbol(控制visibility)可以改善50%的速度 ## 靜態Linker init script 常用於安裝檔 都內嵌在執行檔內 建立一個session做前處理 ## Linker 在最佳化的角色 firefox底層(引擎):libxul 黑線:讀取執行檔的哪個區 ## CRT ### C語言特色 相對低階、關鍵字少且與I/O無關 #### libgcc 硬體沒有/不能用fpu,只能用libgcc Linux kernel是不能直接做浮點數運算的,會造成context switch成本上升 浮點數參數傳遞 GPR 通用暫存器 FPR 浮點數暫存器 GPR->FPR若FPR不能用則直接接到libgcc --- T=c(c) funcB 是 Tail recursion,但 funcA 不是 Tail recursion Q=a(c) 只要函式 g 沒有對 global_var 指標作 dereference,那麼 TCO 就有機會 P=a(b) 不影響,依然可產生符合預期的輸出 MASK=d(a) ~((1U << count) - 1) RRR=b(b) i + j - 1 ### 1 funcA回來要再乘5 TCO是為了變成for迴圈,因此留在stack上的不必要留 funcB會有五層0 funcA回傳非遞減值 因此每層都必須保留 因此必須留在stack 因此不能TCO優化 ### 2 c語言的func是stateful的 rand()可能會有一樣的結果,也可能不一樣 不給/使用固定seed就會一樣 上面: 因為x的lifetime到了(只在該層有效) 回上一層時deference會抓不到目的地 unspecify behevior 下面: g還是在範圍內 ### 3 ### 4 2^31 5&6&7=4 ### 5 有沒有可能把byte array改成bit array 能不能用clz之類的? dp: >查表,用來存下之前狀態,減少運算 >經典題:KMP (Knuth-Morris-Pratt) >給字串找子字串 此題dp用法 一開始預設0原因:假設兩個輸入的是空字串即成立 後面的迴圈 j>0段:兩個字串交錯,但順序一定相同,因此做比較 --- ## 最佳化 micro(謬)arch 基因(gene)演算法 >基因演算法元素: >>crossover >>fitness function >Branch Prediction(BP) --- # Lesson_8(10/27) INIT=b(a) 0x5F COND=e(b) !(i & (1 << j)) HHH=d(d) h = ++(d->height) III=b(a) iterator = iterator->next[0]; callback(iterator) ## 1 因為會涉及到儲存表格 BFS/DFS 圖論:directed graph 一個有加不用考慮的condition 1什麼都不慮 OR跟AND沒過濾掉太多 e跟b是反效果 b的作用: 把比對結果存在bit array i跟j需要32位元長紀錄 16門課有C16取4種組合 如果用二進制就可以用bitwise處理排列組合 DP不是萬靈丹 要看狀態能不能存 INIT還可以取更大的值 必須要大於n的上界 不然初始化時可能會小於minimal day ## 2 效能問題:cache line夠不夠放資料 若不夠大可能會有大量cache miss mm256 (intrinsic function) 有256/8=32個bytes 透過SIMD一次讀好幾個元素(SIMD Friendly) >AVX可以處理mm512,512/8=64bytes ## ROS SLAM ## 現代處理器 prefetch 要求先載入到cache cache有時候會簡寫成$ software pipeline ## code review ### Week 4 #### 第二題 找兩節點的祖先跟子孫關係 如何解? 要先找到限制與條件 先找階層數 表格空間範圍就是深度, 深度$n$的完全二元樹有$2^n-1$個節點 表格只要記祖先, 要回推自己是第幾代 找不找的到 max_level作用: 以2為底的對數,用clz數 這邊的+1是不需要的 改進方式可以把這邊的+1拿掉 目前優點:沒遞迴 有沒有機會用更少的空間實作? obj->parent[i] a pointer to a pointer to integer 存在parent的值都知道範圍,範圍小於輸入節點數的最大值 如果對空間很在意,連malloc都要考慮 ### quiz2 #### 第三題 >洗牌程序 >要注意分布,不能有偏頗(bias) 此題要避免除法 為什麼能把除法換成乘法? 數學推導 128位元存在的原因? 因為在intel處理器上可以做64位元乘法 有128才不會overflow 問題:整數運算導致每次計算可能有不整除誤差 要做補償 (5+3)/2 =5/2+3/2 =2+1 =3 但實際是4 想要改進除法與取餘數,因此改用處理器有優化的乘法 side effect --- # Lesson_9 (11/3) 計算機組織 軟硬體介面 計算機結構 pipeline、super scalor multi-core 翻譯為"核" kernel 翻譯為"核心" AVX-512 SIMD Phase-Locked Loop(PLL) overclock:把PLL拉高 時脈增加->熱 超頻性能增加13% 但耗能增加73% 降頻性能降為87% 耗能為原本的50% 把處理器降頻+多個處理器合作 耗能相同而效能提升 big.LITTLE --- ## quiz ### 1 關鍵在輸入 8組輸入 8組輸出 multiplexer 用random做random search ### 2:數獨 WWW=b(b) hidden + 1 RRR=b(c) ANN_RAND() - 0.5 MOVE=d(b) posmoves - 1 MMM=b(e) mask - 1 ### 1 激勵函數 有給-15~+15的範圍 這之間可微 adaptive適應性 RRR:調權重 double sum = *w++ * (-1.0); ctx->weight[i] = ANN_RAND() - 0.5; /* weights from -0.5 to 0.5 */ weight-0.5對收斂有幫助 WWW:推到下個layer ### 2 MOVE: posmoves &= posmove -1 -> clear LSB 原本:DFS實作 boardSize、*boardColSize完全沒用到 故意湊出連續的記憶體 --- Code Review ## render 在2D畫面呈現3D效果 要克服的點: 定點數可表示範圍沒浮點數那麼廣,要如何讓兩者接近 ray casting texture 牆壁只是範圍,哪邊開始哪邊結束,最後在貼材質 改彩色:改RGB值,原程式碼將RGB用相近值,所以會像黑白 怎麼畫到螢幕上的? 兩個buffer一起更新 位置在main loop 94行&97行 115&116更新buffer 再利用SDL API呼叫,畫在螢幕上 SDL為什麼不讓人直接更新pixel/畫布? 讓寫程式的人決定什麼時候要更新 更新到畫面是慢的,更新到主MEM是快的 CPU是最快的 ->double/triple buffering 螢幕為什麼速度沒那麼快: - 人眼看不出 - 耗電 (video RAM是用DRAM) DRAM不能頻繁充放電,會燙 >VSync >電視牆 >需要9條線,不可能在完全一樣的時間更新 >要看起來順暢 >在不同timing時調整,像對時那樣 >AR >同時要攝影+繪圖/特效 >Triple buffer >A跟B buffer能不能同步VSync 122行才真正更新到video上 怎麼修正錯誤? #### 右邊牆壁不連續 ->範圍不一樣的問題 座標(0,0)正常會在左上角 screenY是從中心點往上跟往下延伸多少要畫東西 原因:投影效果 screenY的意義是真正有效的範圍,不用更新所有的範圍 screenY是uint8 INV_FACTOR / distance 在distance接近0時會overflow 所以先用uint16接,最後再轉成uint8 pixel-art scaling algorithm 反鋸齒 會變平順,但是有點假 hq2x 彩色反鋸齒 ->用來讓畫面變好看 #### 角落的牆壁 看很遠的地方 少了else #### 消失的牆壁 人靠近牆壁到某個特定角度 牆會消失 邏輯判斷時少判斷了近的判斷點 tan(1)在浮點數沒問題,在定點數會有問題 因為不能表示正/負無窮大 解法:運算時避開這種範圍,可能需要改寫數學式 要考慮可能的範圍 把1改成0 合併成一個式子 #### 其中兩面的牆比例異常 邏輯判斷多寫了等號 定點數如何處理三角函數? table Ray casting會用到 tan cot sin cos 裡面把360切成了1024個等分 只要算90度就好,等於256等分 表格該設計多大? 有機會用內插法減少表格大小 deltaAngle 從人的視角對物體要看的範圍 table大小是動態的,隨螢幕大小不同 --- # Lesson_10 (11/10) 記憶體階層(Hierarchy) 傳統硬碟讀取: 變異量(variance)過大(20ms~16sec) Locality temporal locality在意的是時間,現在用的到下個時間也用的到,(昨天讀今天出)。 Spatial locality想要找一筆資料,旁邊的資料剛好有用一起拿 Locality Example >for是德語fur(介係詞) / ALGO 對陣列取值 剛好在附近:空間 要讀寫的地方總是固定為sum:時間 指令觀點(branch,counter...) row major: ```c=1 int sum_array_rows(int a[M][N]) { int sum = 0; for (int i = 0; i < M; i++) for (int j = 0; j < N; j++) sum += a[i][j]; return sum; } ``` 這樣的locality好嗎? >a[M][N]:subscripting,實作依然是線性 column major: ```c=1 int sum_array_cols(int a[M][N]) { int sum = 0; for (int j = 0; j < N; j++) for (int i = 0; i < M; i++) sum += a[i][j]; return sum; } ``` 效能較差 |SRAM|DRAM| |-|-| |靜態(static)|動態(dynamic)| |不須隨時充電|需時刻反覆充電| |速度快|速度慢| |良率低|良率高| cache miss 為什麼只討論miss不討論hit? 因為miss的成本(penalty)較嚴重 1999吞吐量不到1GB/s 2008吞吐量達到20GB/s 2013吞吐量達到35GB/s 2016樹梅派吞吐量5GB/s 處理器->微架構microarchitecture CS:APP 第9章 你看到的記憶體不是真的記憶體 MMU記憶體管理單元 創造假象:記憶體看起來是線性的,總是可以用指標,總是可以存取...等 都是因為MMU才能辦到 代價:page,TLB(cache for page) k-level page table 現代電腦很多都4-level ## quiz ### 1 紅綠藍RGBA轉灰階 /256 = >>8 NEON:ARM SIMD 實作:一次載入多筆資料 做乘加Vector Multiply Accumulate ### 2 狀態壓縮:什麼都用0,1,要有明確區分 OFFSET1=d(d) 0 OFFSET2=g(g) 4 COND1=b(a) conn_nxt == ctmp COND2=a(c) ~ctmp & S ITER=e(b) --ret COND1我猜是代表不能連下去conn找不到下個 COND2也是 出來時如果是0出來的就不處理 ### 1 SIMD寬度視處理器架構而定:64b/128b R2Y是無號整數,使用GPR v開頭都跟向量/浮點數有關 vdup:從GPR搬到SIMD/浮點數暫存器(FPR)使用 vst1q_f32:作用:store 本來是126bit 也就是32bit*4 看手冊重點:寬度 所以寬度是4 ### 2 ```c=1 #include <stdint.h> #include <stdlib.h> #include <string.h> #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define ITERATE(i, begin, end) \ for (int i = (begin), i##_end_ = (end); i < i##_end_; i++) int *countSubgraphsForEachDiameter(int n, int **edges, int edgesSz, int *edges0sz, int *rsz) { uint8_t d[n][n]; /* distance */ memset(d, 0xFF, sizeof(d)); uint16_t conn[n]; ITERATE (i, 0, n) conn[i] = 1 << i; for (int z = 0; z < edgesSz; z++) { int *e = edges[z]; int u = e[0] - 1, v = e[1] - 1; d[u][v] = d[v][u] = 1; conn[u] |= 1 << v, conn[v] |= 1 << u; } ITERATE (k, 0, n) ITERATE (i, 0, n) ITERATE (j, 0, n) d[i][j] = MIN(d[i][j], d[i][k] + d[k][j]); int *rv = calloc(n - 1, sizeof(int)); ITERATE (S, 1, (1 << n)) { int ctmp = 1 << __builtin_ctz(S); while (1) { int conn_nxt = ctmp; ITERATE (d, 0, n) if ((ctmp >> d) & 1) conn_nxt |= conn[d]; conn_nxt &= S; if (COND1) break; ctmp = conn_nxt; } if (COND2) continue; uint8_t ret = 0; ITERATE (i, 0, n) if ((S >> i) & 1) ITERATE (j, 0, i) if (((S >> j) & 1) && (ret < d[i][j])) ret = d[i][j]; if (!ret) continue; rv[ITER]++; } *rsz = n - 1; return rv; } ``` conn[]那些點之間有連結 20行ITER後初始化 不管其他節點,先標記自己就是節點 >另一種寫法:用OR做bitset COND1 第35行開始做的是 狀態壓縮 41行是精隨 如果不壓縮,可能要極大空間 41行在算出下個連接點 題目只要總數 若要印出所有subtree則要BFS/DFS 只要不是唯一路徑就是無效的 所以能連就跳出(COND1) ## 舊題目寫好 ### dict [範例](https://hackmd.io/@eecheng/HJd1sde8v) 建一個特別的dict 自動補完 能用的資料結構 - TST - Trie TST空間效率高,但搜尋時間長 CPY REF CPY: 利用strdup複製字串 REF: 會從mem poll拉 效能差異結論:跟cache有關 不管走到哪裡,都會用指標指到正確資料位置再存取 XOR filter 找字串有沒有在樹中 最怕走到底發現不存在 解決:bloom filter TST:一個字一個字找 XOR:拿到3個hash值,再對值做XOR就能找到 >gnuplot:boxplot >可用來圖表化分布情況 bloom要一個一個做hash*3次 XOR是分開做3次hash XOR建表比較久,有時候還會失敗 有找到跟沒找到的差異 都沒找到的情況: - 第一個字就錯 - 最後一個字才錯 bloom filter hash完會作取餘數,是程式瓶頸(hotspot) 改成快速module ->bit distributed 看得出要多複雜的dataset,哪個結構比較適合? --- # Lesson_11 (11/17) CS:APP 第9章 VM兩個簡稱都行 Virtual Machine Virtual Memory VM Management -> VMM 也都能用 Addressing定址 過去的電腦定址有限制,所以不能亂指/指太遠 near pointer: 指標所指的範圍窄 far pointer: 指標範圍寬 huge pointer: 指標範圍更大 來自硬體限制會造成程式開發困擾 突破點:virtual memory 這邊的虛擬是跟實體physical相比的,實際上跟存在的,跟虛擬寶物等用在不存在的東西的用法不同 ## Virtual Memory 把MEM描述成線性來存取 存在意義:可以把整個mem用c指標做存取,不用考慮定址範圍的問題 具有兩個address - physical memory - virtual memory 利用MMU進行轉換與管理 MMU: swap mem資料到disk A槽B槽以前都是軟碟 C槽開始才是硬碟 -> 磁碟編號從C開始原因 ### Page Fault 當kernely在用Virtual address時,分為兩種space:user/kernel permission對等的==選擇==(男/女廁)(讀取/寫入) privileged==與生俱來的==等級就有高低(天才/凡人)(user/kernel) 在user space使用privileged instruction會送警告/報錯 ARM64跟x86_64切 user/kernel 的位置不同 0x8000000000000000 – ARM64 0xffff880000000000 – x86_64 P.27 針對32位元x86架構 kernel:user = 1:3 user的空間是kernel的3倍 沒有caching的話檔案系統效率會變低 >GPU:圖形處理單元,可能==沒有圖形顯示能力== >GPU: 有特製的記憶體管理器- Graphics Execution Manager(GEM) >Direct Rendering Memory: DRM 連續的physical memory是很珍貴的資源 user space中的mmap mmap:memory map記憶體映射 mmap回傳的是個指標 page fault有三種: - Minor: 不影響正確,只影響讀取時間 - Major: (騎驢找馬:mem不夠借disk來用) - Invalid: 用到invalid的位置 程式執行到某個程度後,OS透過MMU把位置0的位置設為無效(invalid) ==malloc不是syscall== anonymous mapping ## Transalting MMU是數學上的virtual->physical 2^64 -> 160 TB+... VPO PPO VPN Copy-on-write (COW) ## Linux記憶體管理 Overcommit: 要的資源超過能給的,造成資源衝突(承諾太多,但全都要) linux賭你要了記憶體後不會立刻用 ->OOM killer buddy system 配置空間時確認空間有效 slab 向 buddy system 去「批發」一些記憶體,加工切塊以後「零售」出去 零售賣不掉可以賣回批發 overcommit相當於借貸,相信未來能拿到 ### slub/slab/slob 記憶體管理器 左最新又最舊 First-Fit Best-Fit (很難達到) 參考歷史行為 Worse-Fit slob: First-Fit多半表現不錯 slab: 針對cache做調整(cache-friendly) slub: 因應多核處理器,不是為了benchmark,是為了execution time overcommit: 過度使用系統可用記憶體 在web server / 高頻率存取資料庫 > 夢之大地BBS ## Functional Programming pure function 在哪個core運算結果都相同 >ex: sqrt(4) C語言的function函式隱含著狀態 跟數學上的function函數不同 函數只要管定義域與值域 C中的function的y可能是變數甚至是未定義 >twitter:scala語言 >scala >function OO programming language >需要大量的filter/access database >用於社交網路 isLower(int) 特別為了例外處理,不用char直接傳入 ## Stream I/O, EOF, 例外處理 River:不會斷流 stream:涓涓細流,有可能會停 ## signal C語言標準函式庫的一部份 不在UNIX中 在POSIX下也會有 C語言中的比較少 windows等的signal列表比較完整 是由C語言的列表進行擴充的 ## exception CS:APP第8章 kill: list/send signal 不是為了殺掉process 一開始只是為了傳signal到process ## 高效能 Web 伺服器開發 平行運算不一定高效率 有可能會有人沒事做 要搞懂瓶頸 TCP/IP是通訊協定的組合 在低層級的IP上加一層高層級的TCP 瓶頸:減少等待時間 Blocking I/O Model 等待事件跟處理事件都要花時間 改成一次等待好幾個事件 epoll用來新增想監視的file descriptor priority queue 用來解決timer問題 單工程式情況下執行多工:維護timer ex:什麼時候播音樂、什麼時候接收移動指令、什麼時候更新畫面 --- # Lesson_12 (11/24) linux VMA process活的 program死的/未活 共享機制有很多種 SysV (SysFive) 共享記憶體機制 >linux常會有被取代的東西的遺跡 被POSIX(share memory)取代 mmap 可把兩塊虛擬記憶體對應到同一塊實體記憶體 ![](https://i.imgur.com/TBoSwNa.png) 以前:查表(1980) 現在:開發框架 free可以看mem資訊 共享mem沒有人在使用才能釋放掉 ``` $ file /dev/shm /dev/shm: sticky, directory $ mount | grep shm tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev) ``` 把檔案映射到記憶體 把記憶體映射到記憶體 memfd 什麼都可以用file descriptor描述 smartphone殺手應用:camera 手機架構中有個CamIF (camera interface) 先做ISP(image signal processing) 做疊合之類的前處理 目前資料都在kernel 若在kernel處理,kernel會太擠 若搬到user處理,搬動成本太大 ->使用zero copy #### Chrome: ![](https://i.imgur.com/8rGW2jY.png) GPU加速compositor 前景/背景 能不能用硬體加速影像疊合,且要處理透明度 >z-order 不是1+1疊合,要變0.7+0.3,要做透明度處理、聲音降低處理 真正做疊合的是Render中的compositor 疊完結果放到compositor context 最後再用OpenGL等繪製到OS的螢幕 ### 檔案系統 VFS可對應到許多實作 例如:網路 ![](https://i.imgur.com/d0B55x1.png) ![](https://i.imgur.com/qtsbKqn.png) >無碟工作站 >利用NFS處理 pseudo看起來是真的但是是假的 virtual跟現實有一對一對應 /tmp是pseudo file system 可以接收但是實際上存到哪不知道 coda檔案系統 改本地端的cache 連上網路後再比較更改的cache 因為網路可能暫時失連 cephFS 容錯系統 分散式物件儲存 RADOS ### quiz Q1&2 buddy Q3 需持續接受連線 telnet 0 <port>相當於連本地端 Q1=c(a) 在一個 buddy system 中,最高可達 50% 的空間可因內部碎片化 (fragmentation) 而被浪費 Q2=a(d) 安照 block size 遞增排序的 free list 上,使用 first-fit 和 best-fit 演算法等價 MASK1=a(a) EPOLLIN MASK2=b(b) EPOLLOUT COUNT=g(g) 86 COUNT我覺得是寫入多少字元 ## 1 bubby system用2的次方做區隔 32位元架構定址空間2^32 64位元架構定址空間只用2^48 48次方夠大且不過大,64次方過大 IA32是intel發明的 IA64是intel發明的 x86-32是intel發明的 x86-64是AMD發明的,跟過去相容,原本叫AMD64 IA64跟IA32等等不相容 AArch64使用多層translation table 若page size訂為4KB,使用3或4層table,定址使用39-bits(512G) / 48-bits(256T) 若page size訂為64KB,使用2層table,定址使用42-bits(4T) ## 1 (b) 要看情境 (c) 如果是按照高低排列,每次都要線性 (d) 不一定,還是有可能有 ## 2 關鍵在freelist跟block有沒有合併 (a) 不能避免 ## 3 nmap <網址> 可觀察port port num < 1024 superuser才能註冊 epoch還是有blocking IO 但有IO multiplexer,可以一次等好幾個人 91行才開始建立epoch epoch是由一堆系統呼叫組成 read是blockingIO 要等到收完資料,把資料copy進userspace才回傳 玄機:回傳時為什麼要回傳size? >因signal打斷可能讀不完/EOF NFS網路檔案系統 read會有更新週期 為了UNIX sematics(風格):關掉了cache,不然要多次read 2038問題(Y2K) UNIX有epoch,從1970開始算 所以是1970+32位元,到2038年就會爆 要兼顧相容的情況下解決此問題 ->關鍵字linaro 引入新的API https://www.slideshare.net/linaroorg/hkg15206-solving-the-year-2038-problem-in-linux errno:EAGAIN read變形:readv(vector) lib要夠新,要維護更複雜的狀態機,可讀寫多buffer >使用thread能優化前提->瓶頸在userspace且可切割 改進: IO multiplex reactor pattern 只檢查完成了沒,需維護非同步事件,是單執行緒程式 可避免context switch 36~47是input 48~59是output COUNT需有足夠長度,不然字串會被截掉 ```c=1 #include <errno.h> #include <fcntl.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/epoll.h> #include <unistd.h> #define MAX_EVENTS 4096 #define BACKLOG 1024 #define PORT 55688 #define CRLF "\r\n" #define DOUBLE_CRLF CRLF CRLF #define SOCKET_NON_BLOCKING(fd) \ int flags = fcntl(fd, F_GETFL, 0); \ if (flags == -1) \ abort(); \ flags |= O_NONBLOCK; \ if (fcntl(fd, F_SETFL, flags) == -1) \ abort(); #define EPOLL_CTL(efd, a, cfd, evs) \ if (epoll_ctl( \ efd, a, cfd, \ &(struct epoll_event){.events = evs, .data = {.fd = cfd}}) == -1) \ exit(EXIT_FAILURE); static const char *content = "HTTP/1.1 200 OK" CRLF "Content-Length: 18" CRLF "Content-Type: text/html" DOUBLE_CRLF "Users Static Files" DOUBLE_CRLF; static void do_use_fd(int epollfd, struct epoll_event *d) { if (d->events & MASK1) { char buf[512] = {0}; int n = read(d->data.fd, buf, 512); if (n > 0) { EPOLL_CTL(epollfd, EPOLL_CTL_MOD, d->data.fd, EPOLLOUT); } else { if (n == 0) { EPOLL_CTL(epollfd, EPOLL_CTL_DEL, d->data.fd, 0); close(d->data.fd); return; } } } else if (d->events & MASK2) { int n = write(d->data.fd, content, COUNT); if (n > 0) { EPOLL_CTL(epollfd, EPOLL_CTL_MOD, d->data.fd, EPOLLIN); } else { if (n == 0) { EPOLL_CTL(epollfd, EPOLL_CTL_DEL, d->data.fd, 0); close(d->data.fd); } if (errno != EAGAIN && errno != EWOULDBLOCK) close(d->data.fd); } } else { EPOLL_CTL(epollfd, EPOLL_CTL_DEL, d->data.fd, 0); close(d->data.fd); } } int main(void) { int listen_sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (listen_sock == -1) exit(EXIT_FAILURE); struct sockaddr_in server_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), .sin_port = htons(PORT)}; if ((bind(listen_sock, (struct sockaddr *) &server_addr, sizeof(server_addr))) != 0) exit(EXIT_FAILURE); if ((listen(listen_sock, BACKLOG)) != 0) exit(EXIT_FAILURE); struct linger linger = {.l_onoff = 1, .l_linger = 0}; if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &linger.l_onoff, sizeof(linger.l_onoff)) == -1) exit(EXIT_FAILURE); if (setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (const void *) &linger, sizeof(struct linger)) == -1) exit(EXIT_FAILURE); int epollfd = epoll_create1(0); if (epollfd == -1) exit(EXIT_FAILURE); struct epoll_event ev = {.events = EPOLLIN, .data.fd = listen_sock}; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) exit(EXIT_FAILURE); struct epoll_event events[MAX_EVENTS]; for (;;) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) exit(EXIT_FAILURE); for (int n = 0; n < nfds; ++n) { if (events[n].data.fd == listen_sock) { ev.data.fd = accept(listen_sock, NULL, NULL); if (ev.data.fd == -1) exit(EXIT_FAILURE); SOCKET_NON_BLOCKING(ev.data.fd); ev.events = EPOLLIN; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) exit(EXIT_FAILURE); } else do_use_fd(epollfd, &events[n]); } } return 0; } ``` bilabs blapay 用flutter-dart語言 dart: 跨平台框架 JS/TS: 瀏覽器原生語言 Python: 開發方便 Go: C++有效能問題,改用GO OpenCL: 涉及GPU數學運算、hash --- # Lesson_13 (12/1) 拖到論文結束才找工作 表現不好沒有理由 會塞車 主管來不及看 會跟大學部畢業生競爭 ## linux發展 2800萬行 需要有工具才能檢查 驗證產品成本遠大於開發 live migration 為了省電,本來有50台主機的伺服器,改成只開10台 其他的,其他的以VM方式存放,要用時再migrate到實體機 快照 zero-copy 用於DDoS 虛擬化 隔離程度較高 容器化 把OS切成更小部分 同台機器有資料庫有伺服器 eBPF 把一小段程式放到kernel中 可以快速做某些工作(e.x.處理封包) >NIC: network interface controler >IRQ: interupt WIN NT64 windows font 在字型檔動手腳 LINUX歪路 哪裡效能不好就搬 Windows NT = Windows New Technology linux SMP kernel:核心 core:核 及時=in time 即時=real time Linux逐漸朝向多核 CTSS 第一個多人多工作業系統 Compatible Time-Sharing System MS-DOS 單人單工不是作業系統 (不能排程) 在相對廉價的環境能運作 Big Kernel Lock 把所有CPU鎖住才能確保正確執行 ARM server 目標:高強度concurent ### 虛擬化 KVM(Kernel-based Virtual Machine) 只需幾萬行 ### quiz 1. 字元轉bitwise 2. mmap:快速檔案複製 兩次mmap 一次讀一次寫 ACT1=b(b) if (__builtin_popcount(k) != len) continue ACT2=f(f) *maxm = MAX(__builtin_popcount(c), *maxm) ASSIGN=c(a) c |= arr[i] PROP=a(a) PROT_READ | PROT_WRITE #### 2 先把檔案映射到記憶體 lseek先確認檔案有效 src==NULL表示映射失敗 把新建立的檔案映設到記憶體 把檔案-檔案變成記憶體-記憶體 所以跳過檔案系統那層 只要做memcpy就好 也可以適用於SIMD --- # Lesson 14 (12/8) ## quiz PPP=b(a) -1 QQQ=b(e) queries_size - i AAA=a(a) ++vm->object_num BBB=b(b) --vm->object_num CCC=c(f) 7 DDD=c(c) 4 K111=d(d) if (content && len) return gapbuf_insert(*gb, content, len) K222=c(a) gb->size - gb->rb - 1 K333=a(c) gap->lb + new_siz K444=b(d) (gap->rb + 1) ### 1 leetcode 1409 暴力解可解 陷阱: 計算index方式 如果n太大會不能暴力 O(q*m) 關鍵字:線段樹、prefix sum 不是考演算法,是考程式設計 map指的是hash map 記錄目前{queries[i]=2}整個()不是只存2 更新prefixep 根據index進行搬移 ->可用AVX2加速 AVX2提供256bits寬度 SIMD ### 2 CSAPP 第9張 garbage collector mark&sweep 物件的生命週期lifetime ex: malloc/free 保存在heap中 heap只要是有共享記憶體空間的都看的到 lifetime是從呼叫malloc開始到呼叫free結束為止 #### lab0 如何偵測記憶體的 沒有free ->Valgrind Valgrind是虛擬機器 > SQLite 是資料庫也是虛擬機器 經常需要查詢,但沒辦法建太大的DBMS(DataBase Management System) 可以把過去的查詢記錄保存起來(編譯起來) ->stored procedure 針對常常查詢特定條件的路徑進行加速 Google F1 分散式資料庫 DBA(Dynamic Binary Analysis) 在程式間插入程式碼,使其在執行時顯示程式狀態 ### 2 比Valgrind成本低的garbage collector -> mark & sweep sObject 生命週期管理、找鄰近路徑 Tri-colored AAA reference counter > Dict 也有用到reference counting 完全沒人用時才能釋放 多工關鍵:協調、分配、共享 test4雙向指 a指向b,b指向a 要刪掉a跟b ### 3 要有陣列成本低跟linked list可插入刪除的特性 -> gap buffer 難處:size、如何切割 ## 專題 ### 針對 Arm 架構的多工處理實作 ARMMultiTasking 條件:先創造執行單元thread、 描述thread的描述元為TCB 單一處理器多工:切換執行緒/分時多工 使用者看起來 ![](https://i.imgur.com/LUw32BA.png) 在處理器上實際行為 ![](https://i.imgur.com/1DZHxnW.png) AMT message: thread間的溝通 好處:thread間記憶體有共享(tls、stack) 傳訊息:sender->receiver應有順序 recv應創造blocking sender應接收ack exit ### Game Boy 模擬器 改成更易維護debug 全部的實作都放在.h 應該要改成.h宣告.c實作 重構方向 CPU模組內有instruction 改成static編譯->能夠逐行編譯 如何debug模擬器 追蹤關鍵記憶體內容(LCD等等) 如何檢驗狀態機->GBIT Game Boy Instruction Tester --- # Lesson_15 (12/15) ITER1=c(c) for (int k = n - j - 1; k >= 0; k--) res[k + j] = res[k] ITER2=d(b) for (int k = j; k < n; k++) res[k - j] = res[k] DECL=c(c) int cache[N + 1][N] 或(d)int (*cache)[N + 1] INT=e(a) comb[i][j] = (comb[i - 1][j] + comb[i - 1][j - 1]) % MOD P1=a(d) i - prev_start + 1 P2=b(b) i + 1 j為正表示下移 再班的時候得從最下 2猜b/d ### 1 ```c=1 #include <string.h> #define max(a, b) (((a) > (b)) ? (a) : (b)) static inline int overlap_count(unsigned int *AA, unsigned int *BB, int size) { int res = 0; for (int i = 0; i < size; ++i) res += __builtin_popcount(AA[i] & BB[i]); return res; } static void translate(unsigned int *v, int vsize, int i, int j, unsigned int *res) { memcpy(res, v, sizeof(unsigned int) * vsize); int mask[32]; for (int k = 0; k < 32; k++) mask[k] = (1U << k) - 1; int n = vsize; if (i >= 0) { for (int k = 0; k < n; k++) res[k] >>= i; } else { i = -i; for (int k = 0; k < n; k++) res[k] = ((res[k] << i) & mask[n]); } if (j >= 0) { ITER1; for (int k = 0; k < j; k++) res[k] = 0; } else { j = -j; ITER2; for (int k = n - j; k < n; k++) res[k] = 0; } } int largestOverlap(int **img1, int img1Size, int *img1ColSize, int **img2, int img2Size, int *img2ColSize) { int n = img1Size; unsigned int A[n], B[n]; memset(A, 0, sizeof(unsigned int) * n); memset(B, 0, sizeof(unsigned int) * n); for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (img1[i][j] == 1) A[i] |= 1 << j; if (img2[i][j] == 1) B[i] |= 1 << j; } } int res = 0; for (int i = -(n - 1); i <= (n - 1); ++i) { for (int j = -(n - 1); j <= (n - 1); ++j) { unsigned int AA[n]; translate(A, n, i, j, AA); res = max(res, overlap_count(AA, B, n)); } } return res; } ``` ### 2 ```c=1 #include <string.h> static const int MOD = 1e9 + 7; /* prime */ static int recursive(int *nums, int nums_size, int N, DECL) { if (nums_size <= 2) return 1; int left[N + 1], left_size = 0, right[N + 1], right_size = 0; for (int i = 1; i < nums_size; i++) nums[i] > nums[0] ? (right[right_size++] = nums[i]) : (left[left_size++] = nums[i]); int l = recursive(left, left_size, N, cache), r = recursive(right, right_size, N, cache); return (((long) cache[nums_size - 1][left_size] * l % MOD) * r) % MOD; } int numOfWays(int *nums, int N) { int comb[N + 1][N + 1]; /* combination table */ memset(comb, 0, sizeof(comb)); comb[0][0] = 1; for (int i = 1; i <= N; i++) { comb[i][0] = 1; for (int j = 1; j <= N; j++) INIT; } return recursive(nums, N, N, comb) - 1; } ``` ### 3 ```c=1 /* * Hash table is represented as `htable` array of size N (N = number of lines in * dict), where each element either points to the end of a singly-linked list or * is equal to zero. Lists are stored in pre-allocated array `clist` of size N. * * This implementation is memory-efficient and cache-friendly, requiring only * 12N + O(1) bytes of "real" memory which can be smaller than the size of * dict, sacrificing however for request processing speed, which is * O(req_size) in most cases, but may be up to O(dict_size) due to hash collision. */ #include <fcntl.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> /* 2 additional bytes for '\n' and '\0' */ #define MAX_REQUEST_SIZE (128 * 1024 * 1024 + 2) struct htable_entry { uint32_t ptr; }; struct collision_list { uint32_t fpos; uint32_t len; uint32_t prev; }; struct compr_collision_list { uint32_t fpos; uint32_t len; }; typedef struct htable_entry htable_entry_t; typedef struct collision_list collision_list_t; typedef struct compr_collision_list compr_collision_list_t; static char *dict; static htable_entry_t *htable; static collision_list_t *clist; static compr_collision_list_t *cclist; static size_t num_of_bytes, num_of_lines; static size_t num_of_buckets; static int clist_size = 1; static uint32_t hash(char *s, size_t len) { uint32_t hash = 5381; for (size_t i = 0; i < len; i++) hash = ((hash << 5) + hash) + (s[i] - 32); return hash % num_of_buckets; } static void htable_insert(size_t fpos, size_t len) { uint32_t h = hash(dict + fpos, len); clist[clist_size].fpos = fpos; clist[clist_size].len = len; clist[clist_size].prev = htable[h].ptr; htable[h].ptr = clist_size; ++clist_size; } static bool htable_lookup(char *s, size_t len) { uint32_t h = hash(s, len); uint32_t ptr = htable[h].ptr; uint32_t nextptr = htable[h + 1].ptr; for (; ptr < nextptr; ptr++) { if ((len == cclist[ptr].len) && !memcmp(s, dict + cclist[ptr].fpos, len)) return true; } return false; } int main(int argc, char **argv) { if (argc != 2) { printf("usage: %s dictionary_file\n", argv[0]); return 2; } /* Map dictionary file directly to memory for easier access from program */ int dictfd; if ((dictfd = open(argv[1], O_RDONLY)) < 0) return 1; struct stat st; fstat(dictfd, &st); num_of_bytes = st.st_size; if ((dict = mmap(0, num_of_bytes, PROT_READ, MAP_PRIVATE, dictfd, 0)) == MAP_FAILED) return 1; /* Count number of lines in file */ for (size_t i = 0; i < num_of_bytes; i++) { if (dict[i] == '\n') num_of_lines++; } num_of_lines++; num_of_buckets = num_of_lines; htable = calloc(num_of_buckets + 1, sizeof(htable_entry_t)); clist = malloc((num_of_lines + 1) * sizeof(collision_list_t)); size_t prev_start = 0; for (size_t i = 0; i < num_of_bytes; i++) { if (dict[i] == '\n') { htable_insert(prev_start, P1); prev_start = P2; } } /* Compress collision list and update ptrs in htable accordingly */ htable[num_of_buckets].ptr = num_of_lines; cclist = malloc((num_of_lines + 1) * sizeof(compr_collision_list_t)); size_t clines = 0; for (size_t i = 0; i < num_of_buckets; i++) { uint32_t ptr = htable[i].ptr; htable[i].ptr = clines; while (ptr) { cclist[clines].fpos = clist[ptr].fpos; cclist[clines].len = clist[ptr].len; ptr = clist[ptr].prev; clines++; } } free(clist); /* Ready to accept requests */ char *req_buf = malloc(MAX_REQUEST_SIZE); while (!feof(stdin)) { fgets(req_buf, MAX_REQUEST_SIZE, stdin); size_t len = strlen(req_buf); /* Exit on "exit" command */ if (!strncmp(req_buf, "exit\n", len)) break; printf("%s: %s\n", req_buf, htable_lookup(req_buf, len) ? "YES" : "NO"); } free(req_buf); free(htable); free(cclist); munmap(dict, num_of_bytes); close(dictfd); return 0; } ``` --- ## 電腦網路 CS:APP 第11章 transaction 請助教去拿信件 翻成交易不太好 原意:做一件事有往返,會有派送端-回應端 一來一往 可能被撤銷 也可能失敗 更多資源是在遠端 如何描述資源? >URL:Uniform Resource Locator >URI:Uniform Resource Identify arpanet: internet前身 BBN 怎麼訂架構 network global IP internet sockets interface socket簡潔 IP較完整 HTTP/3 下一代網路通訊協定 可以捨棄TCP 提升效率與安全 電腦特性 透過network adapter或NIC(controller) 不是寫成network card IPv6 目的:連結所有裝置(包括藍芽) BLE 屬於藍芽連線 每台裝置都會有IPv6 都可以互相連線 #### 電腦網路 電腦網路串接方式/層次 SAN 一台電腦 LAN 系館同樓層 WAN 對外服務 internet是不同層次的通稱 最小單元:ethernet segment >burst:累積一定量再把frame傳出去,不會一個一個bit傳 下一層次:bridged ethernet segment 同個節點下面集結越多的裝置,速度需越快 下一層次:internet Routing 要建網路還需要協定protocol 兩邊要互動且互信 internet protocol提供 >描述資源 >傳送機制(包括檢驗) 為什麼叫TCP/IP ==stack== ? >TCP/IP checksum使用1補數系統 #### Global IP scheme 計畫、一系列動作、大概樣貌 naming scheme 命名規範、命名準則 光有IP只能解決命名 需要解決更精準地傳遞 IP->UDP->TCP 可靠建立在大量handshake TCP像掛號信 >P2P: 用UDP就行 #### IPv4 & IPv6 Domain Naming System(DNS) 可以視為server的資料庫 跟區塊鍊機制很像 連線存取: >port:港口、端口、埠 >>接口:interface 如何看現在電腦有開那些port? ``` netstat -l ``` #### Socket RIO throughput - latency 拉鋸 >CSAPP第10章 Buffer/Unbuffer Echo buffer >LiFi光照上網 先搞懂來自什麼socket(addr/IP) 什麼port 系統呼叫包括socket、bind、connect...等 SA:address系列的集合 #### Web server >HTTP/1.1 keep alive >把完整內容傳完 層次: ![](https://i.imgur.com/DwFR8pA.png) 動態與靜態內容 靜態: 由文字圖片內容組成 動態: 跳出視窗、畫面互動 >CDN:Content Delivery Network >讓常見的靜態內容在比較近的地方存放 >方向是server端推送而不是client要求 >Common Gateway Interface(CGI) >用於動態網頁 >cgi-bin:可執行,以C語言撰寫 >是獨立程式 >舉例:eserv 網址列傳參數>會有隱私問題(帳號密碼看網址就能看出) 要注意換行符號 在不同OS可能是一個字元/兩個字元 proxies --- # Lesson_16 ## 多處理器架構 seL4 作業系統 以往有未知缺陷 目標:bug-free seL4可用數學方法證明其執行結果符合預期 ->形式化驗證 Operating System==s== ->指的是不同種類不是數量 有些OS不是for通用 有些是for特定應用 ->Android內有Trusty作業系統 Scalability 給5000塊零用錢能考上成大 給30000快能考上台大嗎? ->未知 - Uniprocessor OS 單處理器架構作業系統(OS執行環境) 多處理器但只有單個(硬體)有效 不是單核(封裝) - Multiprocessor OS Correctness of Shared Data涉及共享資料如何確保結果正確 Scalability問題 Correctness of Shared Data Concurrency control - Locks - Semaphores - Transactions - Lock-free data structures Lockfree關鍵:競爭 ex:ring buffer Scalability ![](https://i.imgur.com/1JhJVPb.png) ![](https://i.imgur.com/PGJ5z1f.png) Scalability and Serialisation Parallel 同質的執行單元 有可能會有競爭問題 競爭多會反效果 Serial / sequential 現今的瓶頸不在CPU 在記憶體(cache不是MEM) ex: linux spin lock: ```c=1 while (0 != atomic_dec(&lock->counter)) { do { // Pause the CPU until some coherence // traffic : 考慮到 cache line 的效應!! } while (lock->counter <= 0); } ``` lock->counter不同CPU頻繁存取此段記憶體 會一直進出cache ->可能會競爭/出錯 >真八核-假八核: >硬體是8核,軟體只能控4核 cache miss 指的是多核情況下的 不是以往那個 Multi-what? >封裝 package >晶圓 die CMP 一個晶圓內超過1 core 且core間可互相連接 SMP processor間彼此同質且有連線 SMT 硬體只有一個但軟體開兩個(兩個硬體執行緒) >hyper threading cache line在多核特別重要 micro archtechture ARM 異質多核心虛擬化技術 big.LITTLE 兼顧續航力與效能 ->分析程式規律/效能/行為 能不能把CPU分兩類型:高能耗電/低能省電 ->提升70%效率 問題:比較強的CPU用到一半會被關掉 需做CPU熱插拔(hotplug) 如何確保程式正確?原本在上面跑的OS怎辦 ARM DynamiQ 關鍵:不均衡的 ARM A9 SCU讓cache同步 透過合理的CCI可以控制超過4核 ## 多核處理器與spinlock CSAPP 第12章 Concurrent並行(交錯) Parallel平行 Kernel 名詞 Semphore mutex ### quiz F1=d(d) socket F2=d(d) socket F3=g F4=e F5=f F6= (i) send F7=h(i) send htons 因為TCPIP是big endian 所以要轉換byte order fseek 改變檔案cursor 相關:rewind, fgetpos, ... 上面是讀取網路資料 下面是寫入檔案 peer socket 用來處理當下有沒有新封包進來 server socket 用來傳送檔案 --- NUMA Non-uniform memory access Linux贏在整體行為、綜合表現 異質性 IPC Library OS Unikernel Library OS延伸 直接執行在硬體/hypervisor之上 通常特殊的OS會搭配特殊的硬體/硬體機制 Intel SGX 硬體保護 確保在安全的環境下執行 (資源隔離) KVM Kernel-based Virtual Machine 虛擬化三階級 CPU虛擬化 MEM虛擬化 周邊虛擬化 KVM統治了雲端的作業系統 同時達到了虛擬化的三個階級 VirtIO libVirt 提供一系列函式庫、介面 SPICE protocol 通訊協定,可存取桌面 --- # Lesson_17 12/29 Scalability ![](https://i.imgur.com/QRyB8Q9.png) Amdahl’s Law 整個系統有多少單元可以並行parallel 看起來並行沒有提升很多 但效能提升很多 Gunther’s Law 真實世界的情形 並行到一定程度後 效能提升會受限 甚至collapse ->硬體提升效果不會更好 那如何提升網路流向.網路品質? many-core 大於100 CPU core(數量不用明確) multi-core 2.4.6...都叫multi many-core問題會放大 例:lock 對共享區塊的記憶體存取導致延遲 保持cache一致 等共享硬體資源釋放 spinlock在多核 有cache line bouncing問題 ![](https://i.imgur.com/yTCUpez.png) 兩獨立core的cache內容不一致 ->強迫一致(coherence)可以 但成本高 ->導致更慢 甚至出錯 sloppy counter 不更新整個 只更新部分 降低spinlock成本 ->要求某些變數CPU間不共享 ### Regular expression email表示: /^([a-z0-9_\.-]+)@([a-z0-9\.-]+)\.([a-z\.]{2,6})$/ xxx.xxx@ ``` +:一定要有(1~多) *:可以走好幾次(0~多) \:跳脫字元 .:萬用字元 ``` https://regex101.com https://jex.im/regulex/ 現在的瀏覽器用perl regex 是擴充版的 DFA NFA NFA可轉DFA regex是NFA 編譯是在做NFA轉換 >2021會開linux核心設計 > >linux deadlock detection >恐龍書:可能不處理 >現實:必須處理 lazy/non-greedy operator ### quiz 缺陷:match不是full match LLLLL =(a) < AAAAA =(b) ‘)’ BBBBB =(d) ‘?’ CCCCC =(d) ‘?’ more:我的pattern是否還有效 180行:group pair( ... ) 是否存在 list:用來串接NFA 預先假設list是對的 所以先看NFA部分 中途有匹配就離開 還不匹配就繼續 :::spoiler ```c=1 #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> enum state_behavior { state_single, state_split }; enum special_chars { any_char }; typedef struct state { enum state_behavior type; char matching_value; struct state *output, *output1; } state; typedef struct { state *start; int num_dangling; state ***dangling; } fragment; static state *create_single_state(char matching_value) { state *new_state = malloc(sizeof(state)); new_state->type = state_single; new_state->matching_value = matching_value; return new_state; } static state *create_split_state() { state *new_state = malloc(sizeof(state)); new_state->type = state_split; return new_state; } static fragment *create_fragment(state *start, int num_dangling, state ***dangling) { fragment *frag = malloc(sizeof(fragment)); frag->start = start; frag->num_dangling = num_dangling, frag->dangling = dangling; return frag; } typedef struct { state **list; int capacity; int next_index; } state_list; #define INITIAL_SIZE 100000 static state_list *list_create() { state_list *new_list = malloc(sizeof(state_list)); new_list->capacity = INITIAL_SIZE; new_list->next_index = 0; new_list->list = malloc(sizeof(state *) * INITIAL_SIZE); return new_list; } static void list_grow(state_list *grow) { grow->list = realloc(grow->list, (sizeof(state **) * grow->capacity) * 2); grow->capacity *= 2; } static void list_append(state_list *to_append, state *data) { if (to_append->next_index >= to_append->capacity) list_grow(to_append); to_append->list[to_append->next_index] = data; to_append->next_index++; } static void list_clear(state_list *to_clear) { to_clear->next_index = 0; } #define MAX_STATES 1000000 static int current_index = 0; static char *regex; static bool is_match(state_list *list) { for (int i = 0; i < list->next_index; i++) { state *check = list->list[i]; if (!check) return true; if ((check->type == state_split) && (!check->output || !check->output1)) return true; } return false; } bool nfa_matches(char *string, state *nfa) { state_list *possible = list_create(), *next_possible = list_create(); list_append(possible, nfa); while (*string != '\0') { for (int i = 0; i < possible->next_index; i++) { state *current = possible->list[i]; if (current->type == state_single) { if (current->matching_value == *string || current->matching_value == any_char) { if (!current->output) return true; list_append(next_possible, current->output); } } else if (current->type == state_split) { if (!current->output || !current->output1) return true; list_append(possible, current->output); list_append(possible, current->output1); } } /* swap new list of next states with old clear the new old */ state_list *tmp = possible; possible = next_possible; next_possible = tmp; list_clear(next_possible); string++; } return is_match(possible); } static inline char peek() { return regex[current_index]; } static inline bool eat(char c) { if (regex[current_index] == c) { current_index++; return true; } return false; } static inline char next() { return regex[current_index++]; } static bool more() { return current_index LLLLL strlen(regex); } static void connect_dangling(fragment *frag, fragment *output) { for (int i = 0; i < frag->num_dangling; i++) *(frag->dangling[i]) = output->start; } static fragment *parse_factor(); static fragment *parse_term() { fragment *next = parse_factor(); fragment *first = next; while (more() && peek() != AAAAA && peek() != '|') { fragment *after = parse_factor(); connect_dangling(next, after); first->dangling = after->dangling; first->num_dangling = after->num_dangling; next = after; } return first; } fragment *parse_regex() { fragment *term = parse_term(); if (more() && peek() == '|') { eat('|'); fragment *regex = parse_regex(); state *split = create_split_state(); split->output = term->start, split->output1 = regex->start; state ***dangling = malloc((term->num_dangling + regex->num_dangling) * sizeof(state **)); for (int i = 0; i < term->num_dangling; i++) dangling[i] = term->dangling[i]; for (int i = 0; i < regex->num_dangling; i++) dangling[term->num_dangling + i] = regex->dangling[i]; return create_fragment(split, term->num_dangling + regex->num_dangling, dangling); } return term; } static fragment *parse_base() { char matching_value; switch (peek()) { case '(': eat('('); fragment *regex = parse_regex(); eat(')'); return regex; case '.': matching_value = any_char; next(); break; case '\\': eat('\\'); matching_value = next(); break; default: matching_value = next(); break; } state *single = create_single_state(matching_value); state ***dangling = malloc(1 * sizeof(state **)); dangling[0] = &single->output; single->output = NULL; return create_fragment(single, 1, dangling); } static fragment *parse_factor() { fragment *base = parse_base(); if (more() && peek() == '*') { eat('*'); state *next = create_split_state(); state ***dangling = malloc(1 * sizeof(state **)); dangling[0] = &next->output1; next->output = base->start; next->output1 = NULL; fragment *connected = create_fragment(next, 1, dangling); connect_dangling(base, connected); return connected; } else if (more() && peek() == '+') { eat('+'); state *next = create_split_state(); state ***dangling = malloc(1 * sizeof(state **)); dangling[0] = &next->output1; next->output = base->start; next->output1 = NULL; fragment *connected = create_fragment(next, 1, dangling); connect_dangling(base, connected); base->dangling = connected->dangling; base->num_dangling = connected->num_dangling; return base; } else if (more() && peek() == BBBBB) { eat(CCCCC); state *next = create_split_state(); state ***dangling = malloc((1 + base->num_dangling) * sizeof(state **)); dangling[0] = &next->output1; next->output1 = NULL; next->output = base->start; for (int i = 0; i < base->num_dangling; i++) dangling[1 + i] = base->dangling[i]; fragment *connected = create_fragment(next, 1 + base->num_dangling, dangling); return connected; } return base; } int main(int argc, char **argv) { if (argc < 3) return -1; regex = argv[1]; char *match = argv[2]; fragment *parsed = parse_regex(); /* add .* to make all expressions match inside strings, since we have * basically got an implicit ^ without it. */ state *star_split = create_split_state(); state *star = create_single_state(any_char); star_split->output = star; star_split->output1 = parsed->start; star->output = star_split; bool matches = nfa_matches(match, star_split); printf("Result (%s on %s): %d\n", regex, match, matches); return 0; } ``` ::: 第一次是檢視 第二次是驗收 eecheng 可自我編譯的C編譯器 >A-0 System 人類第一個編譯器 tokenizer>parser>semantics tokenizer看這個字元是哪個類型 跟regular expression 問題:"==" 方法:取到一個=後要繼續檢查 問題:"&"一字兩意-取值、AND、加等號變"&=" parser 組合後確認符不符合 C grammer >BNF >比regular experssion完善 >context free:解析方式與位置無關 > semantics analyzer --- # Lesson 18 ## quiz EXP1=b(d) m[i] || (m[i - 1] && (s[i - 1] == c || c == '.')) EXP2=d(b) m[i - 1] && (s[i - 1] == c || c == '.') COND1=a(a) runner - walker > 4 COND2=a(d) size > 7 COND3=a(a) runner - walker <= 4 COND4=a(d) size == 7 ACT1=a(b) accept_connection() ACT2=c(c) handle_client(events[i].data.fd) ACT3=b(a) send_response(events[i].data.fd) NUM=a(a) 1 --- ### 3 會去讀取檔案"index.html" send_response 嘗試打開檔案讀內容 client連線進來後回復 >在mac上編譯會報錯 epoll.h是linux特有 Mac沒有 epoll重點:I/O multiplexer (blocking) ```c=1 if (events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) { printf("event [%d]: errno %d\n", i, events[i].events); close(events[i].data.fd); perror("closing socket"); } else { if (events[i].data.fd == lfd) ACT1; else if (events[i].events & EPOLLIN) ACT2; else ACT3; } ``` if內都是失敗狀況 EPOLLRDHUP : HUP 對方斷開連線 >grace period 寬限期 策略:趕快關閉連線 lfd accept_connection() accept需用到lfd 到底要處理handle還是send send_response() 讓client看到內容 發生在連線都完成時 第二種條件是連線剛進來所以不是 sendfile 特別的系統呼叫 在兩個fd間傳遞資料 一口氣在kernel處理完 >原本:資料從kernel->user user再呼叫send到kernel傳出 問題:user只能看回傳直不知道確切狀況 kernel越來越大 handle_client() 只檢查client是否可接受 用於連線剛開始時 epoll_create(int size) size需>0 --- ## 2 Leetcode 名題 苦工題 故意寫得很亂 目的:要是不用regex會多亂 (但還有改進空間) regex: ``` reV4 = r'^((([2][0-5][0-5])|([1][0-9][0-9])|([1-9][0-9])|([0-9]))\.){3}(([2][0-5][0-5])|([1][0-9][0-9])|([1-9][0-9])|([0-9]))$' reV6 = '^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$' ``` 想法: 一次多比較好幾個字元 代價:建特別表格、用bit mask >==atoi很危險== >convert string to int >不是早期C規格,C99才進 >"123"->123 >"xyz"->? >man page 完全沒提到轉換失敗回傳結果 >安全: strtol >可指定進位制 >有考慮到失敗跟overflow的情況 regex災難: cloudflare雲端供應商 regex導致非預期狀況 ## 1 regex Leetcode早期題目 >\*:asterisk 一維DP 需二維轉一維 共用片段 --- Not a Number NaN有好幾種 function designator --- rv32emu --- # 並行和多執行緒程式設計 https://hackmd.io/@sysprog/concurrency-concepts semaphore 可不用硬體資源 mutex 需特殊設計硬體支援 https://hackmd.io/@sysprog/linux-sync https://hackmd.io/@sysprog/concurrency-ordering https://hackmd.io/@sysprog/posix-threads --- 1. 不需要popcount 不是統計 不是& 難在可以滑動 今天改成數1 遞迴 3*3分成2*2 結束條件是塞滿/走過了 找出最大重疊 要實驗&修改 37行要改 印出矩陣內容 29行 32~40行是故意的 搞清楚範圍在哪(2倍邊長) 寫出規律的程式碼內容 從sample卻定範圍 改進: translate會多算 用例子說明 更好的是避免重複運算 (對稱性) hashmap是算過的不要重複去算 hashmap=資料庫 key-value 技巧在算過的不用再算 希望看到推理 在特定狀況不用全部跑完 跑到沒有交疊的地方 原本是補0 實際是不用跑 如果增加條件:兩者沒交疊 2. 擔心overflow 用質數原因 對大質數取餘數 EX:360%2=0 不想太複雜操作 hash成本太高 不想看到太多小值 不夠分散 檢查碼是取10餘數->易偽造 質數分布 先做乘法(32->64bit)再取餘數 1開頭的數量比其他數字多 密碼學 把小空間變大空間 要看N的範圍決定要取餘數 排列問題 - 1.組合數 - 2.宣告 對應長度不同 都是地址但有效物件空間不同 15行事記憶體操作 超範圍會報錯 做實驗後推理有效空間 答案只有一個 要實驗 INIT 3. 把檔案操作變成記憶體操作 mmap 避免檔案開啟成本 fopen檔案大大啟動時要讀很久 mmap是對應記憶體 只會有page fault 新酷音 啟動很快 IO最佳化 hashtable 60&70行 最近的查詢不用重新比對 類似cache LRU 1. file->mem 2. hash table 難處一樣在範圍 要求 hash存在的原因 減少重複查詢成本 改進: hash本身改進 hash實作改進 比較差異 19號前交自評 1/25交 只看表面 要實際動過 要看到本質上的問題 --- :::spoiler 筆記: 不需要popcount 難在可以滑動 要實驗&修改 29行 32~40行 是故意的 用實驗&例子確定範圍 改進: translate有多餘運算 兩圖未重疊部分不用計算 原作法是補0實際上是不用經過 ::: --- :::spoiler 擔心overflow 對大質數取餘數原因: hash成本太高 不想太複雜操作 單純取餘數會有不夠分散的問題 ex: 360%2=0 集中在小數值 難在組合數與宣告範圍 選項都是表示位址 但對應有效物件空間範圍不同 做實驗後推理有效空間 答案只有一個 以實驗方式推出正確答案 ::: --- :::spoiler 筆記: I/O最佳化:把檔案操作變成記憶體操作 fopen:詞典檔案太大時啟動時要讀很久 mmap:對應記憶體,發生page fault時才需要讀取成本 hash table 原因: 最近查詢的不用重新比對(類似cache) ::: ---

    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