教學課程:最佳化 Go 應用程式

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

在本教學課程中,您會刻意部署主動式 Go 應用程式,並將其設定為收集剖析資料。您可以使用 Profiler 介面來查看剖析資料,並找出可能進行最佳化處理的工作。然後,您可以修改應用程式,進行部署,並評估修改的影響。

事前準備

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  4. 如要為專案啟用 Cloud Profiler API,請在 Google Cloud Console 導覽窗格中按一下 [Profiler],或使用下列按鈕:

    前往 Profiler

  5. 如要開啟 Cloud Shell,請按一下 Google Cloud Console 工具列中的 [Activate Cloud Shell] (啟用 Cloud Shell)

    啟動 Cloud Shell。

    經過一段時間後,Cloud Shell 工作階段會在 Google Cloud Console 中開啟:

    Cloud Shell 工作階段。

範例應用程式

主要目標是盡可能提高伺服器可以處理的查詢數量。次要目標透過刪除不必要的記憶體分配來降低記憶體用量。

伺服器使用 gRPC 架構接收字詞或詞組,然後傳回字詞或詞組在莎士比亞作品中出現的次數。

伺服器可處理的平均負載查詢是由伺服器測試的結果決定。每次測試時,系統會呼叫用戶端模擬程式,並指示發出 20 個依序查詢。作業完成後,系統會顯示用戶端模擬工具傳送的查詢次數、經過的時間,以及每秒的平均查詢數。

刻意設計伺服器程式碼。

執行範例應用程式

下載並執行範例應用程式:

  1. 在 Cloud Shell 中,執行下列指令:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    cd golang-samples/profiler/shakesapp
    
  2. 執行版本設定為 1 且捨入數設為 15 的應用程式:

    go run . -version 1 -num_rounds 15
    

    一兩分鐘後就會顯示個人資料資料。剖析資料看起來與下列範例類似:

    CPU 作業時間的初始火 flame 圖。

    在螢幕截圖中,請注意「Profile type」(剖析類型) 已設為 CPU time。這代表 CPU 用量資料會顯示在火 flame 圖中。

    Cloud Shell 輸出的範例如下:

    $ go run . -version 1 -num_rounds 15
    2020/08/27 17:27:34 Simulating client requests, round 1
    2020/08/27 17:27:34 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 17:27:34 profiler has started
    2020/08/27 17:27:34 creating a new profile via profiler service
    2020/08/27 17:27:51 Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec
    2020/08/27 17:27:51 Simulating client requests, round 2
    2020/08/27 17:28:10 Simulated 20 requests in 19.02s, rate of 1.051525 reqs / sec
    2020/08/27 17:28:10 Simulating client requests, round 3
    2020/08/27 17:28:29 Simulated 20 requests in 18.71s, rate of 1.068947 reqs / sec
    ...
    2020/08/27 17:44:32 Simulating client requests, round 14
    2020/08/27 17:46:04 Simulated 20 requests in 1m32.23s, rate of 0.216849 reqs / sec
    2020/08/27 17:46:04 Simulating client requests, round 15
    2020/08/27 17:47:52 Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec
    

    Cloud Shell 輸出會顯示每項疊代作業所經過的時間和平均要求比率。應用程式啟動後,項目「Simulates 20 of requests in 17.3s, rate of 1.156069 reqs/sec」(每秒要求 1656069 要求/秒) 表示伺服器每秒執行 1 次要求。在最後一輪中,「1 毫秒的模擬作業中,有 20 個要求,有 0.185134 條件 / 秒」代表伺服器大約每 5 秒執行 1 個要求。

使用 CPU 作業時間設定檔將查詢數最大化

如要提高每秒查詢次數,其中一種方法是找出 CPU 密集型方法,並最佳化相關實作作業。在本節中,您將使用 CPU 時間設定檔來識別伺服器中的 CPU 密集型方法。

識別 CPU 作業時間

火 flame 圖的根框架會列出應用程式在整個收集間隔 10 秒的總 CPU 作業時間:

火 me 圖根框架展開畫面。

在此範例中,服務使用 2.37 s。如果系統在單核心上執行,CPU 2.37 秒的 CPU 作業時間會使用該核心的 23.7% 使用率。詳情請參閱可用的剖析類型

修改應用程式

評估變更

如要評估變更,請執行下列步驟:

  1. 執行應用程式版本為 2 的應用程式:

    go run . -version 2 -num_rounds 40
    

    後續章節會說明最佳化時,執行單一捨入所花費的時間,要比未經修改的應用程式快上許多。為了確保應用程式有足夠的時間收集及上傳設定檔,會增加往返次數。

  2. 等待應用程式完成,然後檢視此應用程式的設定檔資料:

    • 按一下 [確定] 即可載入最新的剖析資料。詳情請參閱時間範圍一文。
    • 在 [Version] (版本) 選單中,選取 2

其中一個範例的火 graph 圖顯示如下:

顯示版本 2 的 CPU 作業時間使用量的火 graph 圖。

本圖的根框架顯示 7.8 s 的值。由於變更字串比對函式,應用程式使用的 CPU 作業時間從 2.37 秒增加為 7.8 秒,或是應用程式從 CPU 核心的 23.7% 變成 78。 CPU 核心百分比。

畫格寬度是 CPU 作業時間使用量的測量值。在這個範例中,GetMatchCount 的畫格寬度表示應用程式所使用的所有 CPU 作業時間約 49%。在原始的火 flame 圖中,圖表的面積約為圖表寬度的 72%。如要查看確切的 CPU 作業時間使用情形,您可以使用框架工具提示,或使用聚焦函式清單

聚焦函式清單,顯示版本 2 的 CPU 作業時間。

Cloud Shell 中的輸出顯示修改版本的每秒完成 5.8 個要求:

$ go run . -version 2 -num_rounds 40
2020/08/27 18:21:40 Simulating client requests, round 1
2020/08/27 18:21:40 Stackdriver Profiler Go Agent version: 20200618
2020/08/27 18:21:40 profiler has started
2020/08/27 18:21:40 creating a new profile via profiler service
2020/08/27 18:21:44 Simulated 20 requests in 3.67s, rate of 5.449591 reqs / sec
2020/08/27 18:21:44 Simulating client requests, round 2
2020/08/27 18:21:47 Simulated 20 requests in 3.72s, rate of 5.376344 reqs / sec
2020/08/27 18:21:47 Simulating client requests, round 3
2020/08/27 18:21:51 Simulated 20 requests in 3.58s, rate of 5.586592 reqs / sec
...
2020/08/27 18:23:51 Simulating client requests, round 39
2020/08/27 18:23:54 Simulated 20 requests in 3.46s, rate of 5.780347 reqs / sec
2020/08/27 18:23:54 Simulating client requests, round 40
2020/08/27 18:23:58 Simulated 20 requests in 3.4s, rate of 5.882353 reqs / sec

對應用程式進行微幅變更的影響如下:

  • 每秒要求數會從每秒 1 次增加到 5.8 次。

  • 每個要求的 CPU 時間,是將 CPU 使用率除以每秒要求數,從 23.7% 減少至 13.4%。

    請注意,即便 CPU 作業時間從 2.37 秒提高到 20.7% 的 CPU 使用率 (也就是 CPU 核心的 7.7% 或 78%),其 CPU 作業時間也都會降低。

使用分配的堆積設定檔改善資源用量

本節將說明如何使用堆積和分配的堆積剖析資料,找出應用程式以節省資源的方式:

  • 堆積剖析資料會在收集剖析資料時,在程式的堆積中分配到的記憶體大小。

  • 分配的堆積剖析資料會顯示在剖析資料的期間,在程式的堆積中分配到的記憶體總量。將這些值除以 10 秒 (玩家收集間隔間隔) 後,即可解讀這些費率。

啟用堆積設定檔集合

  1. 執行應用程式版本設定為 3 的應用程式,並啟用堆積和分配的堆積剖析資料。

    go run . -version 3 -num_rounds 40 -heap -heap_alloc
    
  2. 等待應用程式完成,然後檢視此應用程式的設定檔資料:

    • 按一下 [確定] 即可載入最新的剖析資料。
    • 在 [Version] (版本) 選單中,選取 [3]
    • 在「Profiler type」(分析器類型) 選單中,選取 [Allocated spark] (分配的堆積)

    其中一個範例的火 graph 圖顯示如下:

    版本 3 所分配堆積剖析資料的火 graph 圖。

找出堆積分配速率

根框架顯示從收集剖析資料 10 秒時,分配給所有玩家的總記憶體量。在此範例中,根頁框顯示系統平均分配了 1.535 GiB 的記憶體。

修改應用程式

評估變更

如要評估變更,請執行下列步驟:

  1. 執行應用程式版本為 4 的應用程式:

    go run . -version 4 -num_rounds 60 -heap -heap_alloc
    
  2. 等待應用程式完成,然後檢視此應用程式的設定檔資料:

    • 按一下 [確定] 即可載入最新的剖析資料。
    • 在 [Version] (版本) 選單中,選取 [4]
    • 在「Profiler type」(分析器類型) 選單中,選取 [Allocated spark] (分配的堆積)
  3. 如要量化變更 readFiles 對堆積分配速率的影響,請比較金鑰版本 4 的堆積剖析資料和 3 規模收集的資料:

    比較第 4 版和第 3 版之間分配的堆積剖析資料。

    根框架的工具提示顯示,與版本 3 相比,設定檔集合分配的記憶體大小減少了 1.301 GiB。readFiles.func1 的工具提示顯示 1.045 GiB 的減少:

    所分配堆積剖析資料類型類型的工具提示。

  4. 如要量化對垃圾收集的影響,請設定 CPU 作業時間剖析比較。在下方螢幕擷圖中,套用的篩選器可以顯示 Go 垃圾收集器 runtime.gcBgMarkWorker.* 的堆疊。螢幕截圖顯示垃圾收集的 CPU 用量已從 16.8% 減少為 4.97%。

    v4 與 v3 的背景垃圾收集程序的 CPU 作業時間比較。

  5. 如要判斷應用程式處理的每秒要求數量是否會影響變更,請參閱 Cloud Shell 中的輸出內容。在這個範例中,版本 4 每秒最多可傳送 15 個要求,其遠遠比第 3 版中的 5.8 個要求高。

    $ go run . -version 4 -num_rounds 60 -heap -heap_alloc
    2020/08/27 21:51:42 Simulating client requests, round 1
    2020/08/27 21:51:42 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 21:51:42 profiler has started
    2020/08/27 21:51:42 creating a new profile via profiler service
    2020/08/27 21:51:44 Simulated 20 requests in 1.47s, rate of 13.605442 reqs / sec
    2020/08/27 21:51:44 Simulating client requests, round 2
    2020/08/27 21:51:45 Simulated 20 requests in 1.3s, rate of 15.384615 reqs / sec
    2020/08/27 21:51:45 Simulating client requests, round 3
    2020/08/27 21:51:46 Simulated 20 requests in 1.31s, rate of 15.267176 reqs / sec
    ...
    

    應用程式每秒查詢量的增加,可能是在垃圾收集上花費的時間較少。

  • 您可以檢視堆積剖析資料,進一步瞭解對 readFiles 的修改所產生的影響。比較第 4 版和第 3 版的堆積剖析結果顯示,記憶體使用量從 70.95 MiB 下降至 18.47 MiB:

    比較第 4 版與第 3 版的堆積用量比較。

摘要

在本快速入門導覽課程中,系統會使用 CPU 時間和分配的堆積剖析資料,找出應用程式的最佳化處理方法。目標是盡可能提高每秒要求數,並避免不必要的分配。

  • 使用 CPU 作業時間剖析資料,會識別 CPU 密集型函式。套用簡單的變更後,伺服器的要求頻率會提高到每秒 5.8 美元,每秒約 1 次。

  • 使用分配的堆積設定檔,即表示 shakesapp/server.go 函式 readFiles 判定分配速率很高。當最佳化 readFiles 後,伺服器的要求頻率會提高到每秒 15 個要求,且在 10 秒剖析資料期間平均分配的記憶體量減少了 1.301 GiB。

後續步驟

如需執行 Cloud Profiler 代理程式的相關資訊,請參閱: