Go語言基準測試(benchmark)三部曲之三:提高篇

来源:https://www.cnblogs.com/bolingcavalry/archive/2023/11/03/17724780.html
-Advertisement-
Play Games

歡迎訪問我的GitHub 這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos 本篇概覽 -《Go語言基準測試(benchmark)三部曲》已近尾聲,經歷了《基礎篇》和《記憶體篇》的實戰演練,相信您已熟練掌握了基準測試的常規操作以及各種 ...


歡迎訪問我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

-《Go語言基準測試(benchmark)三部曲》已近尾聲,經歷了《基礎篇》和《記憶體篇》的實戰演練,相信您已熟練掌握了基準測試的常規操作以及各種參數的用法,現在可以學習一些進階版的技能了,在面對複雜一些的場景也能高效完成基準測試,另外還有幾個坑也要提前瞭解,避免以後掉進去

ResetTimer

  • 有時候,在基準測試前會有些準備工作,這些準備工作的耗時會影響基準測試的結果,舉例如下,BenchmarkFib是常規的基準測試,而BenchmarkFibWithPrepare多了八百毫秒的準備時間
func BenchmarkFib(b *testing.B) {
	for n := 0; n < b.N; n++ {
		fib(30)
	}
}

// BenchmarkFibWithPrepare 進入正式測試前需要耗時做準備工作的case
func BenchmarkFibWithPrepare(b *testing.B) {
	// 假設這裡有個耗時800毫秒的初始化操作
	<-time.After(800 * time.Millisecond)

	// 這下麵才是咱們真正想做基準測試的代碼
	for n := 0; n < b.N; n++ {
		fib(30)
	}
}
  • 同時執行上述兩個基準測試,命令和結果如下,可見因為準備工作的耗時,BenchmarkFibWithPrepare方法的測試結果遠不及BenchmarkFib,這與事實是不符合的,因為BenchmarkFibWithPrepare方法的測試目標沒有變化,但是因為自身的準備工作導致測試結果出現較大偏差
go test -bench='BenchmarkFib|BenchmarkFibWithPrepare' benchmark-demo
goos: darwin
goarch: arm64
pkg: benchmark-demo
BenchmarkFib-8                       325           3637442 ns/op
BenchmarkFibWithPrepare-8             50          20173566 ns/op
PASS
ok      benchmark-demo  14.871s
  • 解決上述問題的思路是不要將準備工作的耗時算入基準測試,實現起來很簡簡,如下圖黃色箭頭所示,b.ResetTimer()重置了計時器,前面的耗時都與基準測試無關
    在這裡插入圖片描述
  • 再做一次基準測試,結果如下,可見800毫秒帶來的偏差已被去除
go test -bench='BenchmarkFib|BenchmarkFibWithPrepare' benchmark-demo
goos: darwin
goarch: arm64
pkg: benchmark-demo
BenchmarkFib-8                       325           3616239 ns/op
BenchmarkFibWithPrepare-8            316           3729323 ns/op
PASS
ok      benchmark-demo  5.628s

StopTimer & StartTimer

  • 前面通過ResetTimer消除了基準測試前的多餘耗時,但是如果多餘的耗時出現在基準測試過程中呢?代碼如下所示,fib是本次測試的目標,如果每次fib結束後都要做一些耗時的清理工作(這裡用10毫秒延時來模仿),才能再次fib,那又該如何消除這10毫秒對基準測試的影響呢?
func BenchmarkFibWithClean(b *testing.B) {
	// 這下麵才是咱們真正想做基準測試的代碼
	for n := 0; n < b.N; n++ {
		fib(30)

		// 假設這裡有個耗時100毫秒的清理操作
		<-time.After(10 * time.Millisecond)
	}
}
  • 先來看看每次fib之後的10毫秒是否會影響基準測試,執行測試的命令和測試結果如下,可見,和沒有任何耗時的BenchmarkFib方法相比,BenchmarkFibWithClean的測試結果與fib的真實性能相去甚遠
go test -bench='BenchmarkFib$|BenchmarkFibWithClean' benchmark-demo
goos: darwin
goarch: arm64
pkg: benchmark-demo
BenchmarkFib-8                       322           3610100 ns/op
BenchmarkFibWithClean-8               81          16139196 ns/op
PASS
ok      benchmark-demo  3.002s
  • 對於這種每次調用fib之前或者之後都會出現的額外耗時操作,可以用b.StartTimer()b.StopTimer()的組合來消除掉,簡單的說就是StartTimer會開啟基準測試的計時,StopTimer會暫停計時,具體的使用方法如下
// BenchmarkFibWithClean 假設每次執行完fib方法後,都要做一次清理操作
func BenchmarkFibWithClean(b *testing.B) {
	// 這下麵才是咱們真正想做基準測試的代碼
	for n := 0; n < b.N; n++ {
		// 繼續記錄耗時
		b.StartTimer()

		fib(30)

		// 停止記錄耗時
		b.StopTimer()

		// 假設這裡有個耗時100毫秒的清理操作
		<-time.After(10 * time.Millisecond)
	}
}
  • 再次測試,結果如下,去除了多餘耗時的基準測試結果,從之前16139196ns恢復到7448678ns,然而,和原始的沒有任何處理的BenchmarkFib結果相比依然有一倍左右的差距,看來StartTimer和StopTimer本身也會帶來耗時,而且在納秒級別的測試中會顯得非常明顯
go test -bench='BenchmarkFib$|BenchmarkFibWithClean' benchmark-demo
goos: darwin
goarch: arm64
pkg: benchmark-demo
BenchmarkFib-8                       325           3631020 ns/op
BenchmarkFibWithClean-8              241           7448678 ns/op
PASS
ok      benchmark-demo  7.751s

危險用法,提前避開

  • 現在咱們對benchmark的瞭解已經比較全面了,可以覆蓋大多數單元測試場景,下麵有兩個反面教材,希望咱們將來都能提前避免類似錯誤
  • 這兩個反面教材比較類似:對b.N的錯誤使用
  • 第一個錯誤用法如下所示,在執行b.N次迴圈的時候,將當前是第幾次作為入參傳入了被測試的方法fib
// BenchmarkFibWrongA 演示了錯誤的基準測試代碼,這樣的測試可能無法結束
func BenchmarkFibWrongA(b *testing.B) {
	for n := 0; n < b.N; n++ {
		fib(n)
	}
}
  • 上述代碼在基準測試的時候可能永遠不會結束,這是因為b.N的值並不固定,可能超出了fib方法的設計範圍,這樣就導致出現意料之外的結果(本意是性能測試,fib的入參應該是設計範圍內的),實際運行效果如下,紅色箭頭指向的狀態一直在等待中,只能強行關閉了
    在這裡插入圖片描述
  • 第二種反面教材也類似,不過更簡單,直接拿b.N作為入參,只調用一次fib方法,代碼如下所示
func BenchmarkFibWrongB(b *testing.B) {
	fib(b.N)
}
  • 和前面的BenchmarkFibWrongA比,fib的執行次數似乎少了,但是請註意:b.N到底是多少呢?是否在fib方法的設計範圍內?依舊沒有明確答案,因此,代碼也有可能永遠不會結束
  • 以本例中的fib為例,實際功能是斐波那契數列,我這邊入參等於50的時候,fib方法的耗時是54秒,所以,如果b.N的值再大一些,例如等於100的時候,fib方法就要計算很久了,而計算較大值並不是我們做基準測試的意圖
  • 至此,Go語言基準測試(benchmark)三部曲就全部完成了,相信此刻的您對除了信心滿滿,還有就是迫不及待的想去寫上一段benchmark代碼,看看自己的方法函數究竟性能如何吧
  • 希望這三篇文章能給您帶來一些參考,golang學習路上,欣宸一路相伴

歡迎關註博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文將從三個方面來講解如何便捷配置出頁面,第一部分從數據、事件、業務支持三個方面進行分析,第二部分從模板與頁面收藏與升級、頁面UI結構、畫布功能三個方面進行分析,第三部分從監控、頁面配置、頁面數據導入導出以及其他能力四個方面進行分析。 ...
  • 案例一: 一百個和尚分一百個饅頭,大和尚一人分三個,小和尚三人分一個,正好分完。問大、小和尚各幾人? var num = 100; var people = 100; var big,small; for(big=0;big<=33;big++){ small=people-big; if(big* ...
  • QQ空間自動點贊評論腳本 F12 控制台 對於主頁用以下代碼 var x=5,y=10; function autoClick() { y=y+10+Math.floor(Math.random()*10); var zan = document.getElementsByClassName('it ...
  • 退款業務強耦合到售後系統中,並且業務代碼分散到各個業務層,嚴重缺乏系統的領域邊界和分層設計,重構後退款業務邏輯不強依賴售後核心業務邏輯,做到可以獨立部署。 ...
  • 什麼是 PIP? PIP 是 Python 包管理器,用於管理 Python 包或模塊。註意:如果您的 Python 版本是 3.4 或更高,PIP 已經預設安裝了。 什麼是包? 一個包包含了一個模塊所需的所有文件。模塊是您可以包含在項目中的 Python 代碼庫。 檢查是否安裝了 PIP 在命令行 ...
  • Callable(簡單) callable介面和runnable介面類似,都是為了執行另外一條線程而設計的,區別是Runnable不會返回結果也不會拋出異常。 1、可以有返回值 2、可以拋出異常 3、方法不同;run()/call(); Runnable 實現Runnable介面,重寫run方法,無 ...
  • 記得學妹剛畢業那天,為了不讓學妹畢業就失業,連夜我就用Python採集了上萬份崗位,分析出最合適她的工作。 為此,學妹連夜來我家表示感謝😍 我們開始今天的正題吧 首先要準備這些 軟體 Python 3.8 Pycharm 模塊使用 requests # 數據請求模塊 pip install req ...
  • 說明 1. 或許是全網首發,我翻過很多文章,從未有一個博主講過這個東西,很多博主只講了IOC、DI和反射機制的常見用法,因類類型形參反射的巧妙用法有相當高的難度和學習盲區,所以從未有人講過類類型的形參它怎麼就被自動實例化的。 2. 在Laravel框架,或者是其它框架中,類的成員方法中形參的類型定義 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...