高速行読込クラス
ベンチマークを取る時には、対象となる部分以外に掛かる処理時間を極力抑えたい。
標準のファイル入力クラスであるifstreamは、結構便利でそこそこ高速ではあるのだが、大量の行を読み込む場合、少なくはないオーバヘッドが出てしまう。
なので、今回は、ベンチマーク用に行読み込みを(特に同じファイルを繰返し読む場合は、極めて)高速に行うクラス(ReadLineクラス)を作成した。
このクラスのデメリットは、一旦ファイルの中身を全てメモリ上にコピーするので、ファイルサイズ+α*1のメモリを消費するのと、初期化に若干時間を使うことくらい。
以下にifstreamとの比較とクラス定義を載せておく。
比較
まずは、ifstream:
// ifstream.cc // g++ -O3 -c ifstream.cc #include <fstream> #include <string> #include <sys/time.h> void dummy_fn(const std::string& s); inline double gettime(){ timeval tv; gettimeofday(&tv,NULL); return static_cast<double>(tv.tv_sec)+static_cast<double>(tv.tv_usec)/1000000.0; } int main(int argc, char** argv) { if(argc != 3) { printf("Usage: %s <filepath> <loop count>\n",argv[0]); return 1; } double beg_t = gettime(); printf("init: %f\n", gettime()-beg_t); beg_t = gettime(); std::string line; for(int i=0; i < atoi(argv[2]); i++){ std::ifstream in(argv[1]); while(getline(in,line)) dummy_fn(line); } printf("loop: %f\n", gettime()-beg_t); return 0; } // dummy1.cc // g++ -O3 -c dummy1.cc #include <string> void dummy_fn(const std::string& s) {} //// // g++ -oifstream ifstream.o dummy1.o
実行:
> ./ifstream /path/to/7million_line_file 10 init: 0.000001 loop: 9.220926
次にReadLine:
// read_line.cc // g++ -O3 -c read_line.cc #include "read_line.h" #include <stdio.h> #include <sys/time.h> void dummy_fn(const char* s); inline double gettime(){ timeval tv; gettimeofday(&tv,NULL); return static_cast<double>(tv.tv_sec)+static_cast<double>(tv.tv_usec)/1000000.0; } int main(int argc, char** argv) { if(argc != 3) { printf("Usage: %s <filepath> <loop count>\n",argv[0]); return 1; } double beg_t = gettime(); ReadLine rl(argv[1]); if(!rl) { printf("Can't open file: %s\n", argv[1]); return 1; } printf("init: %f\n", gettime()-beg_t); beg_t = gettime(); const char* line; for(int i=0; i < atoi(argv[2]); i++) { rl.reset(); while(line=rl.read()) dummy_fn(line); } printf("loop: %f\n", gettime()-beg_t); return 0; } // dummy2.cc // g++ -O3 -c dummy2.cc void dummy_fn(const char* s){} //// // g++ -oread_line read_line.o dummy2.o
実行:
> ./read_line /path/to/7million_line_file 10 init: 0.512597 loop: 0.229856
ループ部分に限れば40倍くらいの差がある。
クラス定義 ※ unix用だが、ファイルの中身を読み込むところさえ変更すればWindowsでも使える。
// read_line.h #ifndef READ_LINE_H #define READ_LINE_H #include <vector> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> class ReadLine { public: ReadLine(const char* filepath) : buf(NULL),index(0) { int f = open(filepath, O_RDONLY); if(f==-1) return; struct stat statbuf; fstat(f, &statbuf); buf = new char[statbuf.st_size+1]; ::read(f, buf, statbuf.st_size); close(f); buf[statbuf.st_size]='\0'; init(buf,buf+statbuf.st_size-1); } ~ReadLine() { delete [] buf; } operator bool() const { return buf!=NULL; } void reset() { index = 0; } const char* read() { if(index == lines.size()) return NULL; return lines[index++]; } private: void init(char* cur,const char* end) { lines.push_back(cur); for(cur=strchr(cur,'\n'); cur; cur=strchr(cur,'\n')) { *cur = '\0'; cur++; if(cur < end) lines.push_back(cur); } } private: char* buf; std::size_t index; std::vector<const char*> lines; }; #endif
*1:ファイルサイズ+行数×sizeof(char*)