使用 Cloud Profiler 分析實際工作環境的效能

1. 總覽

雖然用戶端應用程式和前端網頁開發人員經常使用 Android Studio CPU 分析器Chrome 內含剖析工具等工具來改善程式碼效能,但仰賴後端服務的使用者卻沒能取得或採用同等的技術。Cloud Profiler 可為服務開發人員提供相同的功能,無論程式碼是在 Google Cloud Platform 還是其他地方執行。

95c034c70c9cac22.png

這項工具會從實際工作環境的應用程式收集 CPU 用量和記憶體配置資訊。它會將資訊歸類至應用程式的原始碼,協助您識別應用程式使用最多資源的部分,同時說明程式碼的效能特性。工具採用的收集技術成本較低,因此適合持續在生產環境中使用。

在本程式碼研究室中,您將瞭解如何為 Go 程式設定 Cloud Profiler,並熟悉這項工具可顯示的應用程式效能深入分析資料。

課程內容

  • 如何設定 Go 程式以使用 Cloud Profiler 進行剖析。
  • 瞭解如何透過 Cloud Profiler 收集、檢視及分析效能資料。

軟硬體需求

  • Google Cloud Platform 專案
  • 瀏覽器,例如 ChromeFirefox
  • 熟悉 Vim、EMACs 或 Nano 等標準 Linux 文字編輯器

您會如何使用這個教學課程?

僅供閱讀 閱讀並完成練習

您對 Google Cloud Platform 的使用體驗有何評價?

新手 中級 還算容易

2. 設定和需求

自修環境設定

  1. 登入 Cloud 控制台建立新專案,或是重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請先建立帳戶

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

提醒您,專案 ID 是所有 Google Cloud 專案的專屬名稱 (已經有人使用上述名稱,很抱歉對您不符!)。稍後在本程式碼研究室中會稱為 PROJECT_ID

  1. 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Google Cloud 資源。

執行這個程式碼研究室並不會產生任何費用,如果有的話。請務必依照「清除所用資源」一節指示本節將說明如何關閉資源,這樣您就不會產生本教學課程結束後產生的費用。Google Cloud 的新使用者符合 $300 美元免費試用計畫的資格。

Google Cloud Shell

雖然 Google Cloud 可以從筆電遠端操作,但為了簡化本程式碼研究室的設定,我們會使用 Google Cloud Shell,這是一種在 Cloud 中執行的指令列環境。

啟用 Cloud Shell

  1. 在 Cloud 控制台中,按一下「啟用 Cloud Shell」圖示 4292cbf4971c9786.png

bce75f34b2c53987.png

如果您先前從未啟動 Cloud Shell,您會看見中繼畫面 (需捲動位置),說明螢幕內容。如果出現這種情況,請按一下「繼續」 (之後不會再顯示)。以下是單次畫面的外觀:

70f315d7b402b476.png

佈建並連線至 Cloud Shell 只需幾分鐘的時間。

fbe3a0674c982259.png

這個虛擬機器搭載您需要的所有開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。在本程式碼研究室中,您的大部分作業都可以透過瀏覽器或 Chromebook 完成。

連線至 Cloud Shell 後,您應會發現自己通過驗證,且專案已設為您的專案 ID。

  1. 在 Cloud Shell 中執行下列指令,確認您已通過驗證:
gcloud auth list

指令輸出

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. 在 Cloud Shell 中執行下列指令,確認 gcloud 指令知道您的專案:
gcloud config list project

指令輸出

[core]
project = <PROJECT_ID>

如果尚未設定,請使用下列指令進行設定:

gcloud config set project <PROJECT_ID>

指令輸出

Updated property [core/project].

3. 前往 Cloud Profiler

在 Cloud 控制台中按一下「Profiler」,前往 Profiler UI,方法如下:

37ad0df7ddb2ad17.png

您也可以使用 Cloud 控制台的搜尋列前往 Profiler UI,只要輸入「Cloud Profiler」即可然後選取找到的項目無論您使用哪一種方式,您應該都會在 Profiler UI 中看到「No data to display」(沒有可顯示的資料)如下所示。專案是新的,因此尚未收集任何剖析資料。

d275a5f61ed31fb2.png

現在就開始建立個人檔案吧!

4. 剖析基準

我們會使用 GitHub 上提供的簡易合成 Go 應用程式。在 Cloud Shell 終端機中,執行下列指令,在 Profiler UI 仍顯示「No data to display」(沒有可顯示的資料) 訊息時,請執行下列指令:

$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...

接著切換至應用程式目錄:

$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp

目錄中包含「main.go」檔案,也就是已啟用剖析代理程式的合成應用程式:

main.go

...
import (
        ...
        "cloud.google.com/go/profiler"
)
...
func main() {
        err := profiler.Start(profiler.Config{
                Service:        "hotapp-service",
                DebugLogging:   true,
                MutexProfiling: true,
        })
        if err != nil {
                log.Fatalf("failed to start the profiler: %v", err)
        }
        ...
}

根據預設,剖析代理程式會收集 CPU、堆積和執行緒設定檔。這裡的程式碼可用來收集 Mutex (也稱為「爭用」) 設定檔。

現在,請執行程式:

$ go run main.go

在程式執行期間,剖析代理程式會定期收集五種設定類型的剖析。資料收集作業會隨著時間隨機隨機挑選 (每種類型每分鐘的平均剖析資料比率),因此系統收集每種類型資料最多可能需要三分鐘的時間。程式會通知您何時建立個人資料。訊息已透過上述設定中的 DebugLogging 旗標啟用。否則,代理程式會在不發出通知的情況下執行:

$ go run main.go
2018/03/28 15:10:24 profiler has started
2018/03/28 15:10:57 successfully created profile THREADS
2018/03/28 15:10:57 start uploading profile
2018/03/28 15:11:19 successfully created profile CONTENTION
2018/03/28 15:11:30 start uploading profile
2018/03/28 15:11:40 successfully created profile CPU
2018/03/28 15:11:51 start uploading profile
2018/03/28 15:11:53 successfully created profile CONTENTION
2018/03/28 15:12:03 start uploading profile
2018/03/28 15:12:04 successfully created profile HEAP
2018/03/28 15:12:04 start uploading profile
2018/03/28 15:12:04 successfully created profile THREADS
2018/03/28 15:12:04 start uploading profile
2018/03/28 15:12:25 successfully created profile HEAP
2018/03/28 15:12:25 start uploading profile
2018/03/28 15:12:37 successfully created profile CPU
...

收集第一個設定檔後,使用者介面很快就會更新。之後不會自動更新,因此如要查看新資料,請手動重新整理 Profiler UI。如要這麼做,請在時間間隔挑選器中按兩下「Now」按鈕:

650051097b651b91.png

重新整理使用者介面後,您會看到以下內容:

47a763d4dc78b6e8.png

設定檔類型選取器會顯示可用的五種剖析類型:

b5d7b4b5051687c9.png

接著來看看每種設定檔類型和一些重要使用者介面功能,然後執行一些實驗。在這個階段,不再需要使用 Cloud Shell 終端機,因此只要按下 CTRL-C 鍵並輸入「exit」即可退出。

5. 分析 Profiler 資料

系統已經收集了一些資料,接下來會進一步說明。我們使用的是合成應用程式 (此原始碼位於 GitHub 上),可模擬實際工作環境中不同種類的效能問題。

CPU 密集型程式碼

選取 CPU 設定檔類型。UI 載入後,您就會在火焰圖中看到 load 函式的四個分葉區塊,而這統整了所有 CPU 耗用量:

fae661c9fe6c58df.png

特別編寫了這項函式,執行緊密迴圈以耗用大量 CPU 週期:

main.go

func load() {
        for i := 0; i < (1 << 20); i++ {
        }
}

系統會透過四個呼叫路徑從 busyloop() 間接呼叫這個函式:busyloop → {foo1, foo2} → {bar, baz} → load。函式方塊的寬度代表特定呼叫路徑的相對成本。在本例中,這四個路徑的費用大致相同。因為在實際計畫中,您的目標著重於改善對成效最重要的來電路徑。火焰圖 (視覺化) 強調擁有較大盒子且費用較高的路徑,所以能輕易識別這些路徑。

您可以使用設定檔資料篩選器,進一步縮小顯示範圍。舉例來說,您可以嘗試新增「顯示堆疊」指定「baz」的篩選器做為篩選器字串。您應該會看到類似下方的螢幕截圖,其中只會顯示四個 load() 呼叫路徑中的兩個。只有這兩個路徑是透過含有字串「baz」字串的函式執行的路徑名稱。當您想專心處理大型程式的其中一部分時 (例如,您只有部分項目擁有者),這種篩選功能就很有用。

eb1d97491782b03f.png

記憶體密集型程式碼

現在切換到「堆積」以及設定檔類型請務必移除您在先前實驗中建立的所有篩選器。您現在應該會看到火焰圖,其中 allocImpl 是由 alloc 呼叫,並顯示為應用程式記憶體的主要取用端:

f6311c8c841d04c4.png

火焰圖上方的摘要表格指出,應用程式的記憶體總量平均約為 57.4 MiB,大部分是由 allocImpl 函式分配。此函式的實作結果並不令人意外:

main.go

func allocImpl() {
        // Allocate 64 MiB in 64 KiB chunks
        for i := 0; i < 64*16; i++ {
                mem = append(mem, make([]byte, 64*1024))
        }
}

這個函式會執行一次,並將 64 MiB 分配在較小的區塊中,然後將指標儲存至全域變數中,以免被垃圾收集。請注意,分析器顯示的記憶體容量與 64 MiB 略有不同:Go 堆積分析器是一種統計工具,因此測量結果是低負載而非位元組準確。就算成效差異接近 10%,也不用感到意外。

IO 密集型程式碼

如果選擇「Threads」在設定檔類型選取器中,顯示畫面會切換為火焰圖,其中大部分的寬度是用 waitwaitImpl 函式:

ebd57fdff01dede9.png

在火焰圖上方的摘要中,您可以看到有 100 個 Go 常式會透過 wait 函式擴充呼叫堆疊。沒錯,啟動這些等待的程式碼應如下所示:

main.go

func main() {
        ...
        // Simulate some waiting goroutines.
        for i := 0; i < 100; i++ {
                go wait()
        }

這個設定檔類型有助於瞭解計畫是否會花費任何非預期的等待時間 (例如 I/O)。由於這類呼叫堆疊不會耗用任何 CPU 作業時間,因此通常不會由 CPU 分析器取樣。您需要使用「隱藏堆疊」功能篩選器含有 Threads 設定檔的篩選器 - 舉例來說,如要隱藏所有結尾為 gopark, 的堆疊,因為這些堆疊通常是閒置的常式,與在 I/O 上等待的常式比較不有趣。

執行緒設定檔類型還有助於識別程式中有多少點等待程式中其他部分擁有的互斥鎖,但以下設定檔類型對此功能更實用。

大量爭用程式碼

Contention 剖析檔類型代表程式鎖定在程式內。這個設定檔類型適用於 Go 程式,但你必須指定「MutexProfiling: true」來明確啟用這個設定檔類型當中包含錯誤資訊集合的運作方式是記錄 (在「爭用情況」指標底下) 透過「外出日常安排 A」解鎖特定鎖定的次數,也就是另一個例行程序 B 等待鎖定解鎖的次數。在「延遲」指標下,也會記錄遭封鎖的常態等待鎖定的時間。在本範例中,有一個爭用堆疊,鎖定的總等待時間是 10.5 秒:

83f00dca4a0f768e.png

產生這個設定檔的程式碼包含 4 個針對互斥鎖的 Goroutine:

main.go

func contention(d time.Duration) {
        contentionImpl(d)
}

func contentionImpl(d time.Duration) {
        for {
                mu.Lock()
                time.Sleep(d)
                mu.Unlock()
        }
}
...
func main() {
        ...
        for i := 0; i < 4; i++ {
                go contention(time.Duration(i) * 50 * time.Millisecond)
        }
}

6. 摘要

在本研究室中,您已瞭解如何設定 Go 程式,以便與 Cloud Profiler 搭配使用。此外,您也學會如何使用這項工具收集、查看及分析成效資料。現在,您可以將新技能應用在 Google Cloud Platform 上實際運作的服務。

7. 恭喜!

您已學會如何設定及使用 Cloud Profiler!

瞭解詳情

授權

這項內容採用的是創用 CC 姓名標示 2.0 通用授權。