使用二分法解決擴充套件問題
2021年2月16日,作者:Johannes Rieken, @johannesrieken
“就像 git-bisect 一樣,但這是針對 VS Code 擴充套件的。”
Visual Studio Code 的真正力量在於它的擴充套件:主題擴充套件新增顏色和圖示,語言擴充套件支援智慧程式碼補全(IntelliSense)和導航,偵錯程式擴充套件讓你能夠執行程式碼並輕鬆找到錯誤。有的擴充套件可以播放音樂,有的可以顯示股票行情,還有的擴充套件支援跨地域和跨時區的協作。VS Code Marketplace 上有超過 28,000 個擴充套件,使用者安裝 50 個或更多的擴充套件並不少見。有這麼多的擴充套件,出現問題是不可避免的。我們不想回避問題,而是希望讓故障排查變得簡單。
“壞”擴充套件
我們熱愛擴充套件,並不真的認為有任何“壞”擴充套件。然而,像所有軟體一樣,擴充套件也有 Bug 和功能缺陷。因此,為了便於閱讀和增加戲劇性,我們使用“壞擴充套件”這個詞,它指的是可能會崩潰或僅僅是表現出不希望行為的擴充套件。幸運的是,我們在設計 VS Code 時考慮到了“壞”擴充套件的存在,因此我們將它們執行在一個獨立的程序中。這種隔離保證了 VS Code 能夠持續執行,游標總是在閃爍,並且你總能儲存你的工作。
為了好玩,也為了更容易地演示擴充套件二分法,我們建立併發布了 Extension Bisect Demo 擴充套件。安裝後,每當你的游標到達單詞 "bisect" 時,它都會惱人地重置你的游標。你可以使用這個擴充套件來跟著這篇部落格文章一起操作。
用笨辦法尋找“壞”擴充套件
目前,尋找一個“壞”擴充套件可能很容易,也可能很困難。開啟擴充套件檢視(⇧⌘X (Windows、Linux Ctrl+Shift+X)),停用一個擴充套件,重新載入視窗(Developer: Reload Window),然後檢查問題是否仍然存在。如果問題消失了,那麼那個擴充套件就是“壞”的,你就完成了。否則,重新啟用該擴充套件,並對下一個擴充套件重複此過程。
如果你幸運,第一個擴充套件就是“壞”的;如果你不走運,它就是最後一個。用計算機科學的語言來說,這意味著對於 N
個擴充套件,最壞情況下你需要重複 O(N)
(N 的數量級)次,平均情況下是 O(N/2)
。因為這個演算法是由人(你)來操作的,即使 N
的值很小,也會很費力。這就是擴充套件二分法(extension bisect)工具派上用場的地方。它在最壞和平均情況下的表現要好得多,因為它每次都停用一半的擴充套件。
歡迎使用擴充套件二分法
VS Code 中的擴充套件二分法工具的靈感來源於 git bisect 命令。對於熟悉 Git 的人來說,這個命令有助於找出倉庫中的哪個提交引入了問題。
讓我們舉個例子:我安裝了 24 個擴充套件,第 8 個擴充套件是“壞”的。我們知道,迭代法需要 8 個步驟。那麼二分法呢?
下面的影片展示了透過 Help: Start Extension Bisect 命令啟動擴充套件二分法,然後選擇 Good now(現在好了)或 This is bad(還是有問題),直到找到那個“壞”擴充套件。一旦找到,你可以選擇為該擴充套件報告問題。
以下是找到“壞”擴充套件的詳細步驟
- 二分法將 24 個擴充套件分成兩半,每半 12 個,並停用了後半部分的全部 12 個擴充套件。
- 在這個例子中,第 8 個擴充套件是“壞”的,所以它在前半部分並且沒有被停用。情況仍然沒有像我們預期的那樣工作。因為問題仍然存在,擴充套件二分法重複這個過程,將前半部分的 12 個擴充套件分成兩部分:6 個啟用,6 個停用。所有其他擴充套件也都被重新啟用。
- 現在第 8 個擴充套件被停用了。一切正常了。這意味著二分法可以繼續處理後半部分(擴充套件 6-11),並將它們分為 3 個啟用和 3 個停用。
- 現在,第 8 個擴充套件被重新啟用,問題再次出現。這意味著二分法繼續處理前半部分。它將它們分為 1 個啟用和 2 個停用。
- 現在第 8 個擴充套件被停用了,一切又好了,二分法繼續處理後半部分,將其分為 1 個啟用和 1 個停用。
- 第 8 個擴充套件是唯一被停用的擴充套件,問題消失了。這意味著我們已經找到了“壞”擴充套件,我們完成了。
更快地排查問題
我們看到,在每一步中,二分法都將搜尋空間減少了一半。現在這些步驟以對數時間執行,從而得到平均和最壞情況下的效能為 O(log N)
。這相當不錯,因為它擴充套件性很好。有 24 個擴充套件時,你只需要 4 到 5 步就能找到一個“壞”擴充套件,有 38 個擴充套件時,只需要多 1 步。然而,最好的情況變差了,因為用迭代法,你可能運氣好在第一輪就找到“壞”的。
請記住,擴充套件二分法依賴於你提供正確的反饋。你可以透過總是回答 Good now(會歸咎於最後一個擴充套件)或 This is bad(將找不到任何擴充套件)來輕易地誤導它,也誤導你自己。
另一個有用的見解是,擴充套件二分法開始時會考慮所有已啟用的擴充套件列表。這意味著你可以透過在開始前停用一個已知的“好”擴充套件,並在結束後重新啟用它,來將它從二分法排查中排除。但是,只有在你確定那個擴充套件不是“壞”的時候才這樣做。
最後,你可能會注意到二分法多花了一步(log2(N) + 1
)。這是因為它在第一輪開始時會停用所有擴充套件。這樣做第一步是因為你看到的問題可能是由 VS Code 本身引起的,而不是由擴充套件引起的,我們不想讓你不必要地掉進兔子洞。
就是這樣。我們希望你永遠不需要使用擴充套件二分法。但是,如果你確實遇到了可能與擴充套件相關的問題,那麼我們希望我們能讓故障排查變得更容易、更快、更愉快。
程式設計愉快,
Johannes Rieken,VS Code 首席軟體工程師 @johannesrieken