c++性能測試工具:google benchmark入門(二)

来源:https://www.cnblogs.com/apocelipes/archive/2019/06/22/11067594.html
-Advertisement-
Play Games

"上一篇" 中我們初步體驗了google benchmark的使用,在本文中我們將更進一步深入瞭解google benchmark的常用方法。 本文索引 向測試用例傳遞參數 簡化多個類似測試用例的生成 使用參數生成器 向測試用例傳遞參數 之前我們的測試用例都只接受一個 類型的參數,如果我們需要給測試 ...


上一篇中我們初步體驗了google benchmark的使用,在本文中我們將更進一步深入瞭解google benchmark的常用方法。

本文索引

向測試用例傳遞參數

之前我們的測試用例都只接受一個benchmark::State&類型的參數,如果我們需要給測試用例傳遞額外的參數呢?

舉個例子,假如我們需要實現一個隊列,現在有ring buffer和linked list兩種實現可選,現在我們要測試兩種方案在不同情況下的性能表現:

// 必要的數據結構
#include "ring.h"
#include "linked_ring.h"

// ring buffer的測試
static void bench_array_ring_insert_int_10(benchmark::State& state)
{
    auto ring = ArrayRing<int>(10);
    for (auto _: state) {
        for (int i = 1; i <= 10; ++i) {
            ring.insert(i);
        }
        state.PauseTiming(); // 暫停計時
        ring.clear();
        state.ResumeTiming(); // 恢復計時
    }
}
BENCHMARK(bench_array_ring_insert_int_10);

// linked list的測試
static void bench_linked_queue_insert_int_10(benchmark::State &state)
{
    auto ring = LinkedRing<int>{};
    for (auto _:state) {
        for (int i = 0; i < 10; ++i) {
            ring.insert(i);
        }
        state.PauseTiming();
        ring.clear();
        state.ResumeTiming();
    }
}
BENCHMARK(bench_linked_queue_insert_int_10);

// 還有針對刪除的測試,以及針對string的測試,都是高度重覆的代碼,這裡不再羅列

很顯然,上面的測試除了被測試類型和插入的數據量之外沒有任何區別,如果可以通過傳入參數進行控制的話就可以少寫大量重覆的代碼。

編寫重覆的代碼是浪費時間,而且往往意味著你在做一件蠢事,google的工程師們當然早就註意到了這一點。雖然測試用例只能接受一個benchmark::State&類型的參數,但我們可以將參數傳遞給state對象,然後在測試用例中獲取:

static void bench_array_ring_insert_int(benchmark::State& state)
{
    auto length = state.range(0);
    auto ring = ArrayRing<int>(length);
    for (auto _: state) {
        for (int i = 1; i <= length; ++i) {
            ring.insert(i);
        }
        state.PauseTiming();
        ring.clear();
        state.ResumeTiming();
    }
}
BENCHMARK(bench_array_ring_insert_int)->Arg(10);

上面的例子展示瞭如何傳遞和獲取參數:

  1. 傳遞參數使用BENCHMARK巨集生成的對象的Arg方法
  2. 傳遞進來的參數會被放入state對象內部存儲,通過range方法獲取,調用時的參數0是傳入參數的需要,對應第一個參數

Arg方法一次只能傳遞一個參數,那如果一次想要傳遞多個參數呢?也很簡單:

static void bench_array_ring_insert_int(benchmark::State& state)
{
    auto ring = ArrayRing<int>(state.range(0));
    for (auto _: state) {
        for (int i = 1; i <= state.range(1); ++i) {
            ring.insert(i);
        }
        state.PauseTiming();
        ring.clear();
        state.ResumeTiming();
    }
}
BENCHMARK(bench_array_ring_insert_int)->Args({10, 10});

上面的例子沒什麼實際意義,只是為了展示如何傳遞多個參數,Args方法接受一個vector對象,所以我們可以使用c++11提供的大括弧初始化器簡化代碼,獲取參數依然通過state.range方法,1對應傳遞進來的第二個參數。

有一點值得註意,參數傳遞只能接受整數,如果你希望使用其他類型的附加參數,就需要另外想些辦法了。

簡化多個類似測試用例的生成

向測試用例傳遞參數的最終目的是為了在不編寫重覆代碼的情況下生成多個測試用例,在知道瞭如何傳遞參數後你可能會這麼寫:

static void bench_array_ring_insert_int(benchmark::State& state)
{
    auto length = state.range(0);
    auto ring = ArrayRing<int>(length);
    for (auto _: state) {
        for (int i = 1; i <= length; ++i) {
            ring.insert(i);
        }
        state.PauseTiming();
        ring.clear();
        state.ResumeTiming();
    }
}
// 下麵我們生成測試插入10,100,1000次的測試用例
BENCHMARK(bench_array_ring_insert_int)->Arg(10);
BENCHMARK(bench_array_ring_insert_int)->Arg(100);
BENCHMARK(bench_array_ring_insert_int)->Arg(1000);

這裡我們生成了三個實例,會產生下麵的結果:

pass args

看起來工作良好,是嗎?

沒錯,結果是正確的,但是記得我們前面說過的嗎——不要編寫重覆的代碼!是的,上面我們手動編寫了用例的生成,出現了可以避免的重覆。

幸好ArgArgs會將我們的測試用例使用的參數進行註冊以便產生用例名/參數的新測試用例,並且返回一個指向BENCHMARK巨集生成對象的指針,換句話說,如果我們想要生成僅僅是參數不同的多個測試的話,只需要鏈式調用ArgArgs即可:

BENCHMARK(bench_array_ring_insert_int)->Arg(10)->Arg(100)->Arg(1000);

結果和上面一樣。

但這還不是最優解,我們仍然重覆調用了Arg方法,如果我們需要更多用例時就不得不又要做重覆勞動了。

對此google benchmark也有解決辦法:我們可以使用Range方法來自動生成一定範圍內的參數。

先看看Range的原型:

BENCHMAEK(func)->Range(int64_t start, int64_t limit);

start表示參數範圍起始的值,limit表示範圍結束的值,Range所作用於的是一個_閉區間_。

但是如果我們這樣改寫代碼,是會得到一個錯誤的測試結果:

BENCHMARK(bench_array_ring_insert_int)->Range(10, 1000);

error

為什麼會這樣呢?那是因為Range預設除了start和limit,中間的其餘參數都會是某一個基底(base)的冪,基地預設為8,所以我們會看到64和512,它們分別是8的平方和立方。

想要改變這一行為也很簡單,只要重新設置基底即可,通過使用RangeMultiplier方法:

BENCHMARK(bench_array_ring_insert_int)->RangeMultiplier(10)->Range(10, 1000);

現在結果恢復如初了。

使用Ranges可以處理多個參數的情況:

BENCHMARK(func)->RangeMultiplier(10)->Ranges({{10, 1000}, {128, 256}});

第一個範圍指定了測試用例的第一個傳入參數的範圍,而第二個範圍指定了第二個傳入參數可能的值(註意這裡不是範圍了)。

與下麵的代碼等價:

BENCHMARK(func)->Args({10, 128})
               ->Args({100, 128})
               ->Args({1000, 128})
               ->Args({10, 256})
               ->Args({100, 256})
               ->Args({1000, 256})

實際上就是用生成的第一個參數的範圍於後面指定內容的參數做了一個笛卡爾積。

使用參數生成器

如果我想定製沒有規律的更複雜的參數呢?這時就需要實現自定義的參數生成器了。

一個參數生成器的簽名如下:

void CustomArguments(benchmark::internal::Benchmark* b);

我們在生成器中計算處參數,然後調用benchmark::internal::Benchmark對象的Arg或Args方法像上兩節那樣傳入參數即可。

隨後我們使用Apply方法把生成器應用到測試用例上:

BENCHMARK(func)->Apply(CustomArguments);

其實這一過程的原理並不複雜,我做個簡單的解釋:

  1. BENCHMARK巨集產生的就是一個benchmark::internal::Benchmark對象然後返回了它的指針
  2. benchmark::internal::Benchmark對象傳遞參數需要使用Arg和Args等方法
  3. Apply方法會將參數中的函數應用在自身
  4. 我們在生成器里使用benchmark::internal::Benchmark對象的指針b的Args等方法傳遞參數,這時的b其實指向我們的測試用例

到此為止生成器是如何工作的已經一目瞭然了,當然從上面得出的結論,我們還可以讓Apply做更多的事情。

下麵看下Apply的具體使用:

// 這次我們生成100,200,...,1000的測試用例,用range是無法生成這些參數的
static void custom_args(benchmark::internal::Benchmark* b)
{
    for (int i = 100; i <= 1000; i += 100) {
        b->Arg(i);
    }
}

BENCHMARK(bench_array_ring_insert_int)->RangeMultiplier(10)->Apply(custom_args);

自定義參數的測試結果:

custom_args

至此向測試用例傳遞參數的方法就全部介紹完了。

下一篇中我會介紹如何將測試用例寫成模板,傳遞參數只能解決一部分重覆代碼,對於擁有類似方法的不同待測試類型的測試用例,使用模板將會大大減少我們不必要的工作。


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

-Advertisement-
Play Games
更多相關文章
  • 官方文檔 : https://github.com/yiminghe/async-validator ...
  • 用法 先來看看官網的介紹: 主要有八個生命周期,分別是: beforeCreate、created、beforeMount、mounted、beforeupdate、updated 、beforeDestroy和destroyed,分別對應八個不同的時期,另外還有兩個activated和deacti ...
  • 創造一個自適應的盒子,跟隨瀏覽器寬高,目的是不覆蓋body元素 css部分 html部分 js 部分生成自適應盒子 圖片元素填入盒子,點擊關閉隱藏並還原圖片 若是多張圖片可用for迴圈遍歷元素自行改造 ...
  • 作者 | huashiouhttps://segmentfault.com/a/1190000018626163 1. 概述 本文以淘寶作為例子,介紹從一百個併發到千萬級併發情況下服務端的架構的演進過程,同時列舉出每個演進階段會遇到的相關技術,讓大家對架構的演進有一個整體的認知,文章最後彙總了一些架 ...
  • 一. 本單元兩次作業的架構設計 終於在最後一個單元實現了增量式開發。在本單元第一次作業中,我便構建了圖數據結構,第二次作業變得比較簡單,無需重構,只是增加狀態圖和順序圖即可,下麵分析第二次作業的架構設計。 1. 頂級類 MyUmlGeneralInteraction 負責將輸入歸類,形成3個子Uml ...
  • 模板方法 關註公眾號 JavaStorm 獲取更多精彩。 模板方法模式在一個方法中定義了一個演算法骨架,並且 final 修飾防止子類重寫。方法中包含一些抽象方法,也就是一些步驟延遲到字類實現。模板方法使得在不改變演算法結構的情況下,重新定義演算法中的某些步驟。完整代碼可以查看GitHub: "https ...
  • 為什麼要使用Supervisord 17年第一次寫Go項目的時候,用Go開發項目倒沒沒費多大勁,很快就開發完成了。到了在測試環境部署的時候,由於不知道有 Supervisord 這個軟體,著實花了些功夫。總不能跟開發環境一樣,直接執行編譯生成的二進位文件吧,即使 後臺執行了,萬一它掛了,沒人知道,即 ...
  • 1.首先先安裝python3,安裝完成python3後自然就有了pip 2.然後使用pip install scapy 安裝scapy,如果提示升級pip就先升級pip,升級完pip後再使用這個命令安裝scapy 3.安裝完之後如果再cmd下能敲scapy,但是提示報錯,去這個網站下載源碼https ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...