成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一行代碼提升 30%+ TS 類型檢查性能

開發 前端
先說結論,強烈建議在所有復雜泛型場景中,顯式提供泛型參數,這能夠非常顯著降低泛型類型推斷的復雜度,進而提升 TS 性能,幅度甚至可能達到50%!

前言

先說結論,強烈建議在所有復雜泛型場景中,顯式提供泛型參數,這能夠非常顯著降低泛型類型推斷的復雜度,進而提升 TS 性能,幅度甚至可能達到50%!例如,在使用 @douyin-fe/semi 庫的 Form 組件時:

  • 未提供泛型參數:

圖片圖片

圖片圖片

  • 提供泛型參數:

圖片圖片

圖片圖片

在未顯式提供泛型參數時,構建耗時大約為2.3s,其中有 850ms 消耗在 checkSourceFile 節點上;而主動提供泛型參數后,構建總耗時下降至 1.5s,降幅達到 34%,而這僅僅只需要修改一行代碼即可實現!

那么,為什么會有如此巨大的提升呢?接下來,我會詳細總結整個分析排查問題的過程與工具,以及后續在工程層面,可以做那些事情防止再次出現同類問題。

TS Check 性能排查方法

工欲善其事必先利其器,首先,我們需要學習如何獲取 TSC 執行的性能數據,而這需要用到兩個 TSC 命令行參數:

  • --generateTrace:用于 trace-xxx.json 文件,包含 TSC 編譯過程中關鍵節點的性能數據,可使用 SpeedScope 工具可視化分析:

圖片圖片

  • --generateCpuProfile:用于生成詳細的 CPU 執行堆棧信息,同樣可以使用 SpeedScope 工具做可視化分析:

圖片圖片

關于這兩個參數更詳細的解釋,可參考 TS 官方文檔 Performance Tracing。回到項目中,使用這兩個參數執行類型檢查,并將結果寫出到 ts-trace 目錄:

tsc -b tsconfig.build.json --generateTrace ./ts-trace --generateCpuProfile ./ts-trace/ts.cpuprofile --force

之后打開 SpeedScope 工具,選擇相應文件即可。順便提一下, SpeedScope 是我用過最好的 CPU Profile 分析工具,比 TS 文檔推薦 chrome://tracing 效率高很多,建議優先使用。

我個人的使用經驗:先看 trace-xxx.json 文件,再看 cpuprofile 文件。因為 trace-xxx.json 信息更聚焦一些,相對能直觀發現問題,例如上圖中 checkSourceFile 節點明顯比其他節點長很多,肉眼可見是一個異常點;而 cpuprofile 包含了 TSC 執行過程中大部分調用堆棧,信息更全,更適合深入分析執行細節,定位問題的具體原因,例如識別出上述 trace-xxx.json 中的 checkSourceFile 異常點后,可在 cpuprofile 中找到對應函數執行堆棧,向下分析具體性能卡點。

問題分析

基于上述生成的數據,我們可以初步定位到 checkExpression 節點有明顯的性能問題,在示例中消耗 607ms,占比 25% 之久:

圖片圖片

根據堆棧信息中 path/pos 等字段,可定位到問題出現在下圖第 13 行:

圖片圖片

據此可初步推斷,tsc 在檢查表達式 <Form onSubmit={handleSubmit}> 語句時存在較大的性能損耗,而這段代碼與其他代碼最大的差異在于:1. 它用了 Form 元素;2. 它沒有顯式聲明 Form 泛型參數。

至此,答案就大概可以“猜”出來了,試著補上泛型參數,這段 checkExpression 的時間直接從 607ms 降低到 79ms:

圖片圖片

原理淺析

到這里,已經初步找到這個問題的表征答案,但更重要的是:為什么一個泛型參數的缺失會導致如此嚴重的性能問題?只有透徹地理解性能卡點的底層原理,才能推導出正確且完善的解決方案,而要分析問題的根因,有兩種方法,一是從頭開始仔細閱讀并理解源碼,但 TS 項目太大,成本太高;二是分析上述 --generateCpuProfile 參數所生成的 Cpu 調用棧文件,理解這部分耗時操作里都做了那些事情,這明顯性價比要高出許多。

所以,接下來使用 SpeedScope 打開 CpuProfile 文件后,根據時間定位到 checkExpression 對應的 CPU 堆棧節點:

圖片圖片

可以看到,這下面有一個非常長的函數堆棧列表,特別是遞歸出現了許多次 checkExpression、 instantiateXXX 等函數,性能問題應該就出現在這里。作為對比,補充泛型類型后,相應調用堆棧簡化為:

圖片圖片

仔細對比發現,兩者邏輯分叉點主要出現在 chooseOverload 函數上:

  • 優化前:

圖片圖片

  • 優化后:

圖片圖片

接著嘗試斷點調試 chooseOverload 函數,排查過程比較繁瑣,就不展示了,直接拋結論,該函數大致做了下面這些事情:

  1. TS 執行過程中,遇到泛型定義時調用 chooseOverload,函數內判斷是否傳入泛型參數(下圖 75424 行);若參數為空,則調用 inferJsxTypeArguments 推斷類型(下圖 75436 行);

圖片圖片

  1. 而 inferJsxTypeArguments 內部遍歷 jsx 定義的 attributes ,逐步校驗各個組件 Props 的類型定義;

圖片圖片

  1. 當遇到 onValueChange、onSubmit 等函數類型的 props 時,TS 內部需要進一步推斷這類函數簽名,最終走到 checkFunctionExpressionOrObjectLiteralMethod 函數;
  2. 而 checkFunctionExpressionOrObjectLiteralMethod 內部會遞歸調用多次 checkExpression 函數,經過一段非常復雜的計算后,最終推斷出函數簽名,之后再與 Form 元素的 Value 泛型對比檢查類型匹配度。

由此可推斷,此處性能卡點主要出現在 Form 元素的 Value 泛型推斷,以及對傳遞給 Form 元素的各類 onValueChange 等函數類型的 Props 的泛型推斷與檢測上,只需要簡單提供 Value 泛型,即可繞過許多推斷步驟,進而提升效率。

需要注意的是,這一問題目前只在 Form 組件出現,其它多數帶泛型參數的簡單組件即使觸發了推斷邏輯,由于類型邏輯相對簡單許多,校驗鏈路較短,并不會導致性能問題。

圖片圖片

另外還需要注意,chooseOverload 函數中還包含了另一層用于處理函數重載的循環邏輯:

圖片圖片

實測發現,函數重載數量越多,參數形態越復雜,此處性能越差,例如下面例子中:

圖片圖片

圖片圖片

這里的卡點在于 I18nKeysNoOptionsType 是一個非常長達 12000+ 的靜態字符串數組,在上述實例中,TS 需要循環校驗 t 函數的重載簽名,并在每次校驗時遍歷驗證這 12000+ 靜態字符串,兩相疊加導致性能成本居高不下:

圖片圖片

防劣化

到此,我們已經完全可以確定問題根因出在源碼中泛型參數缺失,導致 Typescript 需要做 復雜泛型類型的推導與檢查,引發性能問題,只需借助 Typescript 的 Performance Trace 找出這類性能卡點,補充相應泛型參數即可。但更重要的是,修復存量問題后,后續如何防止這類問題再次出現呢?有幾種方案:

  • 文檔化,約束代碼規范;
  • ESLint 檢測并攔截特定模式代碼;
  • CI 階段分析 TS 性能數據,攔截導致長任務的代碼;

首先,最簡單也是成本最低的方法,可以將相關規則提升為團隊開發規范,明確要求開發者在那些情況下必須補充完備的泛型參數,但這種方式本質上屬于“軟性約束”,執行與否完全取決于開發者的狀態,考慮到人類智能的隨機性,最終效果往往并不理想,更好的方式是使用自動化工具在 CI 階段自動檢測問題實現更“強”的約束。

具體來說,可以選擇編寫 ESLint 規則,限定某些 Case 必須提供泛型參數,例如:

import { Rule } from 'eslint';

export const enforceTsGenericRule: Rule.RuleModule = {
  meta: {
    type: 'problem',
    // ...
  },
  create(context) {
    return {
      JSXOpeningElement(node) {
        if (
          node.name.type === 'JSXIdentifier' &&
          node.name.name.toLowerCase() === 'form'
        ) {
          const hasGeneric =
            node.typeParameters && node.typeParameters.params.length > 0;
          if (!hasGeneric) {
            context.report({
              node,
              message: 'Form elements must have generic parameters.',
            });
          }
        }
      },
    };
  },
};

但問題在于,這種方式必須先提前找出所有可能引發性能劣化問題的代碼模式,整體僵化不靈活,容易導致遺漏或誤傷,相對還不夠極致。

更好的方式是在 CI 環境增量分析 TS 執行性能數據,分析并攔截導致長任務的代碼,實現邏輯:

  1. CI 環境中執行 tsc -b tsconfig.build.json --generateTrace ./ts-trace,生成性能數據,注意不要加 --force 參數;

圖片圖片

  1. 遍歷 trace-xx.json 文件,找到所有 name === "checkExpress" && dur > threshold 的節點,取出對應 path 與 pos 數值;
  2. 根據 path 與 pos 數值定位對應代碼行, 調用 git diff source-branch...target-branch 取得增量內容,之后判斷長任務對應代碼行是否為本次更新代碼,若命中則調用 CI 接口進行攔截。

總結

對于大規模項目而言,Typescript 很好,我認為幾乎是必選技術棧之一,并且有必要在開發環境、CI/CD 各個環節設置卡口,驗證代碼的正確性,其本身性能也做的非常極致,但架不住大型項目代碼量上來之后,任務復雜度過高導致類型檢測成本也居高不下,此時就必須從代碼本身著手,做好各類性能優化,保證時間復雜度在合理范圍內。

但這個方向資料并不多,很少能找到現成且有效的解決方案,多數時候需要自己摸索。過去這段時間,我們團隊也做了許多這方面的嘗試,除了本文提到的這種顯式定義泛型參數的方法外,其他值得分享的性能優化手段包括:

  • 使用 tsc 緩存,復用舊的結果;
  • 使用 ts project references,實現分片檢測;
  • 正確配置 watchOption 屬性,減少文件監聽復雜度;
責任編輯:武曉燕 來源: Tecvan
相關推薦

2016-12-02 08:53:18

Python一行代碼

2025-02-12 09:55:01

Java代碼性能

2021-09-22 09:43:47

Python 開發編程語言

2014-02-12 13:43:50

代碼并行任務

2022-04-09 09:11:33

Python

2017-04-05 11:10:23

Javascript代碼前端

2020-07-22 08:30:02

代碼開發工具

2020-03-26 12:38:15

代碼節點數據

2022-12-30 08:08:30

2020-02-06 11:15:58

Python命令代碼

2021-08-31 09:49:37

CPU執行語言

2017-04-13 19:20:18

Python代碼并行任務

2023-09-12 10:10:57

開發者工具開源

2020-08-19 10:30:25

代碼Python多線程

2020-09-09 16:00:22

Linux進程

2021-11-02 16:25:41

Python代碼技巧

2023-10-23 09:56:00

2025-04-16 09:21:00

2019-12-25 14:08:50

Pandas數據計算

2020-08-12 14:54:00

Python代碼開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品av久久久久电影 | 久久com| 日韩精品专区在线影院重磅 | 久久视频一区 | 欧美精品一二三 | 日韩国产在线 | 欧美中文字幕一区二区 | 日韩欧美国产一区二区 | 亚洲日本欧美日韩高观看 | 日韩a在线 | 成人av电影在线 | 精品欧美激情在线观看 | av黄色在线| 理论片午午伦夜理片影院 | 日韩精品免费一区二区在线观看 | 国产精品毛片一区二区在线看 | 亚洲在线日韩 | 伊人久久精品 | 嫩草影院网址 | 国产一区二区精品在线观看 | 亚洲精品视频在线 | 日本免费一区二区三区视频 | 免费精品视频一区 | 一区二区精品 | 久久久久久国产精品 | 日韩在线中文字幕 | 欧美精品乱码久久久久久按摩 | 亚洲国产精品一区二区www | 91.色 | 男人天堂99 | 91av大全 | 中文字幕亚洲一区二区va在线 | 免费视频99 | 久久久久国产 | 精品欧美一区二区三区久久久 | 91精品久久久久久久久中文字幕 | 在线观看免费高清av | 久久久久国产一区二区三区四区 | 欧美在线天堂 | 美女天天操 | 免费中文字幕 |