如何實現(xiàn)一個簡易的benchmark

目標

Benchmarking in C++中描述了一個簡易的benchmark嘹叫,將逐步分析如何實現(xiàn)該benchmark嫩絮,及模板化思路丛肢。

應用場景

衡量算法其中一種方法是BigO,以n為因子剿干,判斷隨著數(shù)量級增大耗時增加情況蜂怎,而且考慮到屏蔽一些隨機擾動,需要進行多次采樣比較置尔。
譬如文中舉例比較std::vectorstd::list時杠步,進行了10次采用,n的數(shù)量級從10的1次方直到10的5次方,輸出了2種情況下的耗時信息幽歼,再配合其可視化腳本輸出比較圖朵锣。

也就是說,這個benchmark將會支持多次采樣试躏、多個因子猪勇、執(zhí)行多種算法測量。

最小實現(xiàn)

測量時颠蕴,需要在執(zhí)行前記錄當前時間戳泣刹,然后執(zhí)行完成后根據(jù)結束的時間戳計算出耗時。

auto start = std::chrono::high_resolution_clock::now();
//執(zhí)行函數(shù);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);

以上的代碼得出的duration就是毫秒量級的耗時記錄結果犀被。

一個最小的耗時測量實現(xiàn)如下:

//invoke function and return time used
template<typename TTime = std::chrono::milliseconds>
struct measure
{
    template<typename F, typename ...Args>
    static typename TTime::rep execution(F func, Args&&... args)
    {
        auto start = std::chrono::high_resolution_clock::now();
        func(std::forward<Args>(args)...);
        auto duration = std::chrono::duration_cast<TTime>(std::chrono::high_resolution_clock::now() - start);
        return duration.count();
    }
};

其中用到了完美轉發(fā)std::forward來處理參數(shù)傳遞椅您。

使用方法為:

auto oVal =  measure<>::execution(CommonUse<std::vector<int>>,1000);

這樣就得到了n=1000時的耗時毫秒數(shù)。

支持多種測量

當希望支持多種測量時寡键,就需要將測量結果存儲到map之中掀泳;實現(xiàn)時將其拆分為三塊:

  1. 執(zhí)行函數(shù)獲取耗時
  2. 耗時信息保存
  3. 整合執(zhí)行和保存

執(zhí)行函數(shù)獲取耗時

struct measure
{
    //measure function implement
    template<typename F, typename TFactor>
    inline static auto duration(F&& callable, TFactor&& factor)
    {
        auto start = std::chrono::high_resolution_clock::now();
        std::forward<decltype(callable)>(callable)(std::forward<TFactor>(factor));
        return (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start));
    }
}

保存測量結果

//save results
template<typename TFactor>
struct experiment_impl
{
    std::string _fctName;
    std::map<TFactor,std::vector<std::chrono::milliseconds>> _timings;
    experiment_impl(const std::string& factorName):_fctName(factorName){};
 
protected:
    ~experiment_impl() = default;
};

整合執(zhí)行和保存

定義了experment_model來在構造時完成函數(shù)執(zhí)行和結果保存:

template<typename TFactor>
struct experment_model final :detail::experiment_impl<TFactor>
{
    //invoke function and save result
    template<typename F>
    experment_model(std::size_t nSample, F&& callable, const std::string& factorName, std::initializer_list<TFactor>&& factors)
        :experiment_impl<TFactor>(factorName)
    {
        for (auto&& factor:factors)
        {
            experiment_impl<TFactor>::_timings[factor].reserve(nSample);
            for (std::size_t i = 0 ; i < nSample ; i++)
            {
                experiment_impl<TFactor>::_timings[factor].push_back(measure::duration(std::forward<decltype(callable)>(callable),factor));
            }
        }
    }
 
};

封裝成benchmark來支持多次、多因子西轩、多種測量:

template<typename TFactor>
class benchmark
{
    std::vector<std::pair<std::string,std::unique_ptr<detail::experment_model<TFactor>>>> _data;
public:
    benchmark() = default;
    benchmark(benchmark const&) = delete;
public:
    template<class F>
    void run(const std::string& name, std::size_t nSample, F&& callable,
        const std::string& factorName, std::initializer_list<TFactor>&& factors)
    {
        _data.emplace_back(name,std::make_unique<detail::experment_model<TFactor>>(nSample,
            std::forward<decltype(callable)>(callable),factorName,
            std::forward<std::initializer_list<TFactor>&&>(factors)));
    }
}

使用方法

bmk::benchmark<std::size_t> bm;
bm.run("vector",10, CommonUse<std::vector<int>>,"number of elements",{10,100,1000,10000});
bm.run("list",10, CommonUse<std::list<int>>,"number of elements",{10,100,1000,10000});

支持無因子測量

那么當要測試的是普通函數(shù)時员舵,并沒有因子輸入,只是執(zhí)行了多次藕畔,那么就需要做出調(diào)整:

  1. 執(zhí)行無因子函數(shù)獲取耗時
  2. 耗時信息保存
  3. 提供無因子版experiment_model
  4. 提供無因子版benchmark.run

執(zhí)行無因子函數(shù)

//measure function void version
template<typename F>
inline static auto duration(F&& callable)
{
    auto start = std::chrono::high_resolution_clock::now();
    std::forward<decltype(callable)>(callable)();
    return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
}

調(diào)整的方式為移除測量時的因子參數(shù)輸入马僻。

保存測量結果

測量結果只是一個vector<milliseconds>,提供experment_impl的偏特化版本:

//save result void version
template<>
struct experiment_impl<void>
{
    std::vector<std::chrono::milliseconds> _timings;
    experiment_impl(std::size_t nSample):_timings(nSample){};
protected:
    ~experiment_impl() = default;
};

提供無因子版experiment_model

//invoke function and save result [void version]
template<typename F>
experment_model(std::size_t nSample, F&& callable)
    :experiment_impl<void>(nSample)
{
    for (std::size_t i = 0; i < nSample; i++)
    {
        experiment_impl<TFactor>::_timings.push_back(measure::duration(std::forward<decltype(callable)>(callable)));
    }
}

提供無因子版benchmark.run

由于benchmark需要支持無因子注服,而其存儲的內(nèi)容為experiment_model韭邓,那么需要提供基類,保證experiment_model在兩種情況下都適用:

struct experiment
{
    virtual ~experiment() {};
};
template<typename TFactor = void>
struct experment_model final :detail::experiment,detail::experiment_impl<TFactor>
{
  ......
};

同樣,benchmark需要移除模板參數(shù)TFactor

class benchmark
{
    std::vector<std::pair<std::string,std::unique_ptr<detail::experiment>>> _data;
   ......
}

然后是無因子版的run

template<class F>
void run(const std::string& name, std::size_t nSample, F&& callable)
{
    _data.emplace_back(name, std::make_unique<detail::experment_model<>>(nSample,
        std::forward<decltype(callable)>(callable)));
}

支持多分辨率測量

之前一直采用的是毫秒溶弟,在一些情況下函數(shù)執(zhí)行很快女淑,需要采用微秒、納秒級別辜御,那么就需要把之前寫死的std::chrono::milliseconds替換成模板參數(shù)鸭你,同步修改所有位置:

template<typename TTime ,typename TFactor>
struct experiment_impl
{
   ......
}
   ......
template<typename TTime>
struct measure
{
   ......
}
   ......
template<typename TTime,typename TFactor = void>
struct experment_model final :detail::experiment,detail::experiment_impl<TTime,TFactor>
{
   ......
}
   ......
template<typename TTime>
class benchmark
{
   ......
}

支持多種clock

在之前都采用的是std::chrono::high_resolution_clock,但是對MSVC2013來講存在問題:
Time measurements with High_resolution_clock not working as intended
這個版本可以采用std::chrono::steady_clock擒权,那么實現(xiàn)多種clock即可苇本,與多分辨率的方式一致,提供模板參數(shù)TClock替換std::chrono::high_resolution_clock,并將默認參數(shù)設置為std::chrono::high_resolution_clock菜拓。

其它

  • 輸出結果
  • tic/toc
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市笛厦,隨后出現(xiàn)的幾起案子纳鼎,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贱鄙,死亡現(xiàn)場離奇詭異劝贸,居然都是意外死亡,警方通過查閱死者的電腦和手機逗宁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門映九,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞎颗,你說我怎么就攤上這事件甥。” “怎么了哼拔?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵引有,是天一觀的道長。 經(jīng)常有香客問我倦逐,道長譬正,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任檬姥,我火速辦了婚禮曾我,結果婚禮上,老公的妹妹穿的比我還像新娘健民。我一直安慰自己抒巢,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布荞雏。 她就那樣靜靜地躺著虐秦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凤优。 梳的紋絲不亂的頭發(fā)上悦陋,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音筑辨,去河邊找鬼俺驶。 笑死,一個胖子當著我的面吹牛棍辕,可吹牛的內(nèi)容都是我干的暮现。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼楚昭,長吁一口氣:“原來是場噩夢啊……” “哼栖袋!你這毒婦竟也來了?” 一聲冷哼從身側響起抚太,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤塘幅,失蹤者是張志新(化名)和其女友劉穎昔案,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體电媳,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡踏揣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匾乓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捞稿。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拼缝,靈堂內(nèi)的尸體忽然破棺而出娱局,到底是詐尸還是另有隱情,我是刑警寧澤珍促,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布铃辖,位于F島的核電站,受9級特大地震影響猪叙,放射性物質發(fā)生泄漏娇斩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一穴翩、第九天 我趴在偏房一處隱蔽的房頂上張望犬第。 院中可真熱鬧,春花似錦芒帕、人聲如沸歉嗓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鉴分。三九已至,卻和暖如春带膀,著一層夾襖步出監(jiān)牢的瞬間志珍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工垛叨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伦糯,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓嗽元,卻偏偏與公主長得像敛纲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子剂癌,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 接著上節(jié) condition_varible 淤翔,本節(jié)主要介紹future的內(nèi)容,練習代碼地址佩谷。本文參考http:/...
    jorion閱讀 14,778評論 1 5
  • 接著上節(jié) atomic办铡,本節(jié)主要介紹condition_varible的內(nèi)容辞做,練習代碼地址。本文參考http://...
    jorion閱讀 8,475評論 0 7
  • 不講語言特性寡具,只從工程角度出發(fā),個人覺得C++標準委員會在C++11中對多線程庫的引入是有史以來做得最人道的一件事...
    stidio閱讀 13,256評論 0 11
  • 失戀早已過33天稚补,卻依然會想念童叠,不知從何說起,不知何時才會結束课幕,這種你已離開厦坛,向前向后向左右都不見你的迷離。
    四九月閱讀 181評論 0 0
  • 姓名:楊忠誠 公司:慧友冠源科技&272期六項精進努力二組&廣東盛和塾稻牙二組 【日精進打卡第27天】' 【知~學...
    楊忠誠閱讀 284評論 0 0