FizzBuzz関数処理時間計測クラス
FizzBuzz高速化プログラムを書くに当たり計測クラスを書いてみた。
windows用です。
計測対象の関数は
・第一引数 FILE* : データ出力ファイル
・第二引数 unsigned int : 出力の上限値
で定義されている必要があります。
※引数の順序を入れ替えました
使い方
//////////////////////////////////////////////////// // 使用例 //////////////////////////////////////////////////// #include <iostream> #include "TimeChecker.h" // プロトタイプ(別ファイルで定義されている関数のプロトタイプ。 void FizzBuzzSimple(FILE* fp, unsigned int count); extern "C" void FizzBuzzSimpleC(FILE* fp, unsigned int count); // ↑拡張子 .c のプログラムはこうする using namespace std; int main(void){ const unsigned int N = 999999;// FizzBuzz出力最大値 const unsigned int TIMES = 10;// 実行回数 // マクロを使うとこういう風に書けます cout << TIMECHECK_N(FizzBuzzSimple, N, TIMES) << endl; cout << TIMECHECK_N(FizzBuzzSimpleC, N, TIMES) << endl; // 1回だけ実行用のマクロもあります cout << TIMECHECK(FizzBuzzSimple, N) << endl; cout << TIMECHECK(FizzBuzzSimpleC, N) << endl; return 0; }
ポイントは
・使いやすいようにクラスを包んだマクロを用意した。
・演算子オーバーロードで cout へ << で結果出力できる。
・出力は 関数名.txt のファイルに出力してくれる。
問題点は、関数名の長さが不揃いだと表示したとき数値のでるカラム位置が一定しないこと。ここは改善の余地があります。
FizzBuzz関数は別ファイルにこんな感じで定義してあります。
/////////////////////////////////////////// // 単純な FizzBuzz C++版 (FizzBuzzSimple.cpp) /////////////////////////////////////////// #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> void FizzBuzzSimple(FILE* fp, unsigned int count){ unsigned int i = 0; for(i=1; i<=count; i++){ if(!(i%15)) fputs("FizzBuzz\n", fp); else if(!(i%5)) fputs("Buzz\n", fp); else if(!(i%3)) fputs("Fizz\n", fp); else fprintf(fp, "%d\n", i); } }
C言語版もほぼ同じなので省略。
クラスの実装
定義はこんな感じ、ほぼ全て .h の中に書いてしまっています。
#pragma once //////////////////////////////////////////////////// // FizzBuzz用時間計測クラス (TimeChecker.h) //////////////////////////////////////////////////// #include <cstdio> #include <windows.h> #include <iostream> #include <vector> #include <sstream> #include <string> #pragma comment(lib,"winmm") // FizzBuzz 関数の型 typedef void(*tFunc)(FILE*, unsigned int); // 処理時間計測クラス class TimeChecker { // 処理時間情報 struct TimeSpan{ double startTime; // 処理開始時刻 double endTime; // 処理終了時刻 }; tFunc m_func; // 実行する関数 const char *m_prompt; // 計測時間表示の左に出力するプロンプト const char *m_filename; // 出力ファイル名 //結果を蓄積しておくベクタ std::vector<TimeSpan> m_timeSpans; public: // コンストラクタ TimeChecker(tFunc func, const char *prompt, const char *filename) : m_func(func), m_prompt(prompt), m_filename(filename) { } private: // プログラム実行 void do_execute(unsigned int limit){ TimeSpan span; FILE *fp; if(0 != fopen_s(&fp,m_filename, "w")){ throw std::exception("fopen error"); } timeBeginPeriod(1); span.startTime = timeGetTime(); m_func(fp, limit); span.endTime = timeGetTime(); timeEndPeriod(1); fclose(fp); m_timeSpans.push_back(span); } public: // 回数を指定してプログラム実行 TimeChecker &execute(unsigned int limit, const int times){ int n = static_cast<int>(times); for(; n; n--){ do_execute(limit); } return *this; } // 実行結果の平均値を返す double get_average(){ double sum = 0; for(std::vector<TimeSpan>::iterator iter = m_timeSpans.begin(); iter != m_timeSpans.end(); ++iter) { sum += iter->endTime - iter->startTime; } return (sum / m_timeSpans.size()); } // プロンプト取得 const char *get_prompt(){ return m_prompt; } // 結果を文字列へ変換 std::string to_string(){ std::stringstream ss; ss << get_prompt(); ss << get_average(); ss << " msec"; if(m_timeSpans.size() > 1){ ss << " (" << m_timeSpans.size() << " avrg)"; } return ss.str(); } }; // 演算子オーバーロード // << で結果をストリームへ出力できるように std::ostream& operator<<(std::ostream& os, TimeChecker& tc); /////////////////////////////////////////// // ヘルパーマクロ /////////////////////////////////////////// // times回実行して平均を表示 #define TIMECHECK_N(f, n, times) TimeChecker(f, "[" #f "] ", #f ".txt").execute(n, times) // 1回のみ実行 #define TIMECHECK(f, n) TIMECHECK_N(f, n, 1)
.cppもあります、使うときはこちらもプロジェクトに含めてコンパイルする必要があります。書いてあるのは演算子オーバーロードだけです。
//////////////////////////////////////////////////// // FizzBuzz用時間計測クラス (TimeChecker.cpp) //////////////////////////////////////////////////// #include "TimeChecker.h" std::ostream& ::operator<<(std::ostream& os, TimeChecker& tc){ os << tc.to_string(); return os; }
できればFizzBuzz以外でも使える汎用的なものに改造したいですね。