演算法(一)簡易 Timer
阿新 • • 發佈:2020-08-14
目錄
我打算積累一些演算法。不過剛一動手就遇到了大麻煩,在 Windows 上配置一個簡單的 Benchmark 似乎有點困難。Google Benchmark 我觀察了半天也沒研究明白怎麼安,於是想了想,不如自己寫一個簡易 Timer,作為一切的起點!
Timer 的功能
- 首先,Timer 有開始計時和終止計時的功能。一個 Timer 可以多次計時,並支援取平均數。以後我可能加上點極值啊迴歸啊什麼的(
無限期鴿鴿了)。 - 其次,Timer 應該可以打包計時,就是一次計時中做多次運算。這是因為有的運算需要的時間非常少,所以需要測多次再平均才能準一點。
- 最後,還要能生成一個 string report,方便輸出。
Timer 的宣告
有了上面的想法,可以簡單設計出一個 Timer 類:
// seideun/include/auxiliary/Timer.hpp // // Created by Deuchie on 2020/8/14. #ifndef SEIDEUN_TIMER_HPP #define SEIDEUN_TIMER_HPP #include <string> #include <chrono> namespace seideun::auxiliary { /** A simple timer * * # Functionalities * * - count time with high resolution, generate report in nanoseconds * - count the same task multiple times to get average time cost * - bulk-count low cost tasks to get higher accuracy and more simplicity * - convenient well-formatted report */ class Timer { public: Timer(); explicit Timer(std::string task_name); void start(); void stop(uint32_t repetitions_taken = 1); auto get_latest_duration() -> std::chrono::nanoseconds; auto get_average_duration() -> std::chrono::nanoseconds; auto report() -> std::string; void clear(); // clear all saved records private: std::string const task_name_ = "Anonymous Task"; std::chrono::nanoseconds average_duration_{}; std::chrono::nanoseconds latest_duration_{}; std::chrono::time_point<std::chrono::high_resolution_clock> start_time_{}; uint32_t total_repetitions_ = 0; }; } #endif //SEIDEUN_TIMER_HPP
Timer 的實現
// seideun/src/auxiliary/Timer.cpp // // Created by Deuchie on 2020/8/14. #include "auxiliary/Timer.hpp" #include "fmt/format.h" void seideun::auxiliary::Timer::start() { start_time_ = std::chrono::high_resolution_clock::now(); } seideun::auxiliary::Timer::Timer(std::string task_name) : task_name_(std::move(task_name)) {} void seideun::auxiliary::Timer::stop(uint32_t repetitions_taken) { auto this_duration = std::chrono::high_resolution_clock::now() - start_time_; auto new_total_repetitions = total_repetitions_ + repetitions_taken; latest_duration_ = this_duration / repetitions_taken; average_duration_ = (total_repetitions_ * average_duration_ + this_duration) / new_total_repetitions; total_repetitions_ = new_total_repetitions; } auto seideun::auxiliary::Timer::get_latest_duration() -> std::chrono::nanoseconds { return latest_duration_; } auto seideun::auxiliary::Timer::get_average_duration() -> std::chrono::nanoseconds { return average_duration_; } auto seideun::auxiliary::Timer::report() -> std::string { char static constexpr format_string[] = R"({}: total repetition times: {} average duration: {} ns)"; return fmt::format(format_string, task_name_, total_repetitions_, average_duration_.count()); } void seideun::auxiliary::Timer::clear() { total_repetitions_ = 0; } seideun::auxiliary::Timer::Timer() = default;
測試 Timer
這裡我沒有用 Google Test,就簡單寫了一個 Demo 看看輸出正不正常即可
#include "auxiliary/Timer.hpp"
#include "auxiliary/primitive_aliases.hpp"
#include "fmt/format.h"
#include <fstream>
int main() {
std::ofstream out("temp.txt"); // I used this to prevent compiler optimization
seideun::auxiliary::Timer timer;
timer.start();
for (u32 i = 0; i != 10000; ++i) {
out << i * 32 - 7 << ' ';
}
timer.stop(10000);
fmt::print("# Latest duration: {} ns\n# Report:\n{}\n", timer.get_latest_duration().count(), timer.report());
// Let's try another cycle
timer.start();
for (u32 i = 0; i != 5000; ++i) {
out << i * i * i + 41976283 << ' ';
}
timer.stop(5000);
puts("\nAnother Test:");
fmt::print("# Latest duration: {} ns\n# Report:\n{}\n", timer.get_latest_duration().count(), timer.report());
}
輸出是這樣的:
>>> ./seideun.exe
# Latest duration: 313 ns
# Report:
Anonymous Task:
total repetition times: 10000
average duration: 313 ns
Another Test:
# Latest duration: 367 ns
# Report:
Anonymous Task:
total repetition times: 15000
average duration: 331 ns
Process finished with exit code 0
PS: primitive_alias 裡的宣告如下:
using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using i8 = int8_t; using i16 = int16_t; using i32 = int32_t; using i64 = int64_t;
我個人覺得這個寫起來方便,而且定長好處多多啊朋友們!