需要澄清的一個誤區是,擁塞絕不是發送的數據量太大導致,而是數據在極短的時間段內到達了同一個地方以至于超過了網絡處理容量導致,擁塞的成因一定要考慮時間因素。換句話說,擁塞由大突發導致。
只要 pacing,再多的數據量也不會擁塞,1GB,100GB,100TB 的數據遵循同一 pacing 發送只是需要不同時間平滑送完,然而只要 100MB 的數據同時到達就可能導致擁塞,即使再大的瓶頸帶寬,再大的 buffer,更大的突發也會導致擁塞必然發生,擁塞絕不是資源不足導致。
如今帶寬已足夠大,配合丟包,整型,限速,足以應對任何程度的擁塞,但任意一個上述措施都將壓力給到了端主機。按 1970 年代網絡和主機環境的假設,傳輸層需要模擬一個無損網絡細水長流地送達,端主機必須處理丟包以及波動,這種假設下,tcp 順勢而生的彼時需重點考慮稀缺的帶寬和內存,長肥管道怎么辦也一直是 tcp 擔心遇見卻很長一段時間都不存在的環境,可如今的廣域網幾乎都很長肥,多虧了 cdn。
本節我以不同的視角給我的孩子們描述一個不同的網絡傳輸假設。
現在的帶寬足夠大且便宜,內存也足夠大且便宜,1970 年代的假設已經不復存在,改變主要表現在三方面。
首先,數據的獲取方式變成了實時傳輸,而不再從硬盤上獲得。曾經的先下載后使用的方式徹底被改變,曾經的下載過程不需要關注時延抖動,如今的實時傳輸則要同時關注速率和抖動。其次,足夠大且便宜的帶寬允許主機在更長的時間段發送更大的數據,數據編碼不再一字千金。最后,足夠大且便宜的內存允許主機在展示數據之前緩存足夠久的時間,這段時間則為糾錯等預處理提供了操作空間。
因此,始自 1970 年代的傳輸方式需要改變,不能通過重傳的方式加劇時延抖動,不必過于在意傳輸數據量大小,通過更冗余的編碼在 receiver 糾錯。
在繼續這個話題之前,我并沒有給孩子們過多灌輸 tcp/quic/rdma 這類協議是如何做的,因為如果他們對此太了解了反而不好,如果以近乎完美的 tcp 攜帶 bbr 為標桿,我需要不斷解釋下面這段話:
冗余編碼肯定會更長,這個必須要接受,tcp 以重傳構建無損的優化方式是引入 fec 而減少重傳,但所有聲稱具有靈活切換重傳(術語叫 arq)和 fec 的協議都是在僥幸賭博,設計者的心理向著壓縮數據量靠攏,企圖以最少的數據量傳輸信息,在不得不加入冗余的時刻再加入,而需要加入冗余的時刻恰恰就是擁塞的時刻,但擁塞期間提高冗余只能更擁塞,這也是他們所看到的結果,他們會告訴你,fec 在擁塞期間效果并不好,最終還是要靠重傳。他們接受不了將冗余平滑到整個傳輸期間,因為他們接受不了或者說理解不了在不擁塞的時候為什么還要冗余,他們希望得到免費的午餐,不想增加一點點他們知道哪怕只是可能會輸掉的籌碼。
…
我讓孩子們試著去理解英文和中文的差別,英文聽錯一個音節無礙于聽懂整個詞,中文聽錯一個音節就損失整個詞,這是為什么。因為英文單詞是多音節編碼,多音節自帶了冗余,而漢字是單音節編碼,沒有冗余。英文以多音節單詞為書寫單位,單詞之間有空格,以連讀關聯兩個甚至多個單詞來提供語音的黏連冗余,而漢字以單音節單字為書寫單位,每個漢字之間有空格并不區分詞,連讀黏連也就很自然向整體拼讀方向壓縮,比如 “之于” 本來還是兩個音節,被壓縮成了 “諸”,“什么” 變成了 “啥”,還有一些正在發生的,比如 “這樣” 和 “醬”,“不好” 和 “表”(其實本來就有連音字 “嫑”),還有 “只因” 和 “雞”。
映射到如今的計算機編碼和傳輸,很容易發現始自英語國家的計算機反而使用中文的編碼和傳輸方式。單獨編碼字母而不是單詞,每個字母是一個單獨的 ascii/unicode,字母與字母之間平行無關聯,顯然就是一種 “單音節,無黏連” 的方式。
用自然語言的編碼和傳輸方式作為模板去設計計算機(特別是 ai 領域)網絡傳輸方式,雖不一定最優,至少合理。
作為 80 后互聯網工人,我必須試著從 “壓縮數據量結合重傳” 的保守策略轉換到 “增加冗余量允許丟包” 的激進策略。前者說的是 “壓縮數據量也有利于避免擁塞,但不幸擁塞發生了,重傳兜底”,后者則意味著 “無需太在意數據量大小,丟包就丟包吧”。
仍以現有的 unicode 碼為基礎,假想一種簡單的拉鏈式黏連編碼,后一個符號編碼有一部分是前一個符號編碼的糾錯碼,或者更簡單一點,以連續兩個字符為單位編碼成 “x|y|x + y”,如果 y 丟了,用 x + y - x 來恢復,這些都是很自然的方案。
但考慮到計算機網絡傳輸的特殊性,依然需要更加復雜的處理。
考慮兩個環境特征,首先,數據以數據包(packet)為單位傳輸,每個數據包有 mtu 字節的大小,超過 1KB,連續編碼的原始數據和糾錯碼幾乎是一丟全部丟;其次,擁塞非均勻發生,擁塞本身具有突發性和連續丟包概率。在實際傳輸前需要做兩層隨機,將上述兩件不幸的事平滑開來。第一,將連續編碼隨機到不同數據包,第二,將擁塞丟包的糾錯碼隨機到非擁塞時期。
下面是一個孩子們能理解的簡單方案:
這里仍然存在兩個問題,第一個問題,x,y,x+y 所在的數據包丟了兩個,就無力恢復了,但這個問題可以緩解,比如引入拉鏈,“a|b|a+b|c|b+c|d|c+d|…”,但依然無法徹底解決,所以轉變觀念很重要,也許大不了還是可以反饋一個 nack 的。第二個問題是傳輸數據量會成倍增加。第二個問題解決后,第一個問題就能進一步緩解,因為可以容納更多冗余了。
如果全世界只有英文,用計算機網絡傳輸英文和用嘴說英文沒有什么不同,人們用 26 個字母排列組合成所有的文字資料,但記憶單位卻是多個字母組成的多音節單詞甚至短語,習慣用語,人們編碼的是單詞。計算機也可以直接編碼單詞,比如用 1 個碼來表示 “skinshoe”,而不是 8 個碼,這就極大的壓縮了編碼空間。
如果引入其它字符,比如中文,編碼詞語依然比編碼單個漢字更有效,比如 “皮鞋” 可以只用 1 個碼,而現有方案卻需要 2 個碼,但由于中文本來就已經極度壓縮了,比如 “之于” 壓縮成了 “諸”,這種詞語編碼顯然沒有英文的單詞編碼性價比更高。
chatgpt 在神經網絡的意義空間里就是類似的編碼方式,字,單詞,詞組,短語,句子,段落,文章等信息均被編碼到一個用意義關聯的多維空間,“h” 和 “e” 不再是孤立的字符,“he” 和 “him”,“his” 距離很近,同理,“桌子” 和 “椅子” 距離很近,因為它們都是家具。這是從意義中恢復的依據,所謂的意義就是個概率空間。“我去你媽的” 和 “我去你奶奶的” 不同,但意思一樣。但有點跑題,今天的話題是冗余傳輸。
一條直線由 2 個點確定,每個單詞都可以編碼到一條直線,只需要給 2 個點 4 個坐標即可,如果加入冗余,可以傳輸該直線上的 3 個點,4 個點,甚至更多點,只要 receiver 收到任意兩個點,就能恢復這個單詞,4 個坐標編碼一個單詞看起來并不便宜,如果 3 個,5 個點提供冗余就更昂貴了,但如果編碼更長的單詞呢,就會變得更劃算,借鑒哈夫曼編碼思想,直線可以編碼 “常用但長” 的單詞,短語。
m 次曲線需要 m + 1 個點唯一確定,只要被編碼數據量和 m + 1 + k 個點的坐標數據量在同一量級,其比值越大越劃算。
只是人們并不習慣這種方法,如果仔細觀察網絡上傳輸的任何文字類數據,幾乎沒有不是單詞,詞組構成的無意義字符串,如果非要傳輸類似 “dqgfbkjfwkbswnfegeg” 的字符串,自然回退到 ascii/unicode 的代價就是冗余本身的代價。
至于圖片,音樂的編碼,它們本質上都是對模擬信息的數字化,而模擬信號都自帶冗余,藍天背景缺失的一塊依然是藍天,極小概率是一只恰好飛過的鳥。
講給小孩子開個腦洞完全 OK,也能指引他們重新思考未來的傳輸協議,但回到現實中,工程化需要成熟和穩定的兼容性支撐,idea 隨時都有,但落地的事只能踩著大規模部署技術的腳印前行。
剛剛跟博士聊到,那么多學界的 idea 其實都只是 idea 和論證,落地的東西還是傳統 tcp/ip 相關的那套,實驗室的假設在工程實踐上很難被滿足,好多新玩意兒最初都是軍事目的,從軍界需求擴散到學界在到民用的轉化周期并不短,比如 tcp/ip 本身就經歷過這樣的過程。
所以,工人好好做工,經理好好開會,士不可以不弘毅,君子不能不經理。
浙江溫州皮鞋濕,下雨進水不會胖。