各プログラミング言語で バイナリファイルの中にある 1 のビットの数をカウントする (その2)
こちらのエントリの続き
mog project: Counting Bits in Python, C++ and Scala (Pt.1)
C++ 実測 (3)
方法(1)を途中の段階まで実施して配列を横断して加算していく方法。
Hacker's Delight の実装を参考にした。
8ビット単位まで各個計算を行うバージョンを METHOD 3、16ビット単位のバージョンを METHOD 4 としている。
コード
これまでのコードも含め、全体を掲載する。
#define METHOD 3 #include <fstream> #include <vector> #include <string> #include <ctime> #include <cassert> using namespace std; int const kChunkSize = 1 << 14; // METHOD 1 int POP(unsigned long long x) { unsigned long long const y = 0x3333333333333333ULL; x -= (x >> 1) & 0x5555555555555555ULL; x = (x & y) + ((x >> 2) & y); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; x += x >> 8; x += x >> 16; x += x >> 32; return x & 0x7f; } // METHOD 2 int table[1 << 16]; void init_table() { for (int i = 0; i < (1 << 16); ++i) table[i] = POP(i); } int POP2(unsigned long long x) { return table[x & 0xffff] + table[(x >> 16) & 0xffff] + table[(x >> 32) & 0xffff] + table[(x >> 48)]; } // METHOD 3 int POP_ARRAY(unsigned long long a[], int n) { unsigned long long s, s8, x; unsigned long long const y = 0x3333333333333333ULL; s = 0; for (int i = 0; i < n; i += 31) { int lim = min(n, i + 31); s8 = 0; for (int j = i; j < lim; ++j) { x = a[j]; x -= (x >> 1) & 0x5555555555555555ULL; x = (x & y) + ((x >> 2) & y); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; s8 += x; } x = (s8 & 0x00ff00ff00ff00ffULL) + ((s8 >> 8) & 0x00ff00ff00ff00ffULL); x += x >> 16; x += x >> 32; s += x & 0xffff; } return s; } // METHOD 4 int POP_ARRAY2(unsigned long long a[], int n) { unsigned long long s, s16, x; unsigned long long const y = 0x3333333333333333ULL; s = 0; for (int i = 0; i < n; i += 4095) { int lim = min(n, i + 4095); s16 = 0; for (int j = i; j < lim; ++j) { x = a[j]; x -= (x >> 1) & 0x5555555555555555ULL; x = (x & y) + ((x >> 2) & y); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; x = (x + (x >> 8)) & 0x00ff00ff00ff00ffULL; s16 += x; } x = (s16 & 0x0000ffff0000ffffULL) + ((s16 >> 16) & 0x0000ffff0000ffffULL); x += x >> 32; s += x & 0xffffffff; } return s; } template <typename T, typename U> double time_it(int iter, int count, T func, U val) { double elapsed = 1e30; for (int i = 0; i < iter; ++i) { clock_t t = clock(); for (int j = 0; j < count; ++j) { func(val); } clock_t s = clock(); elapsed = min(elapsed, static_cast<double>(s - t) / CLOCKS_PER_SEC); } return elapsed; } int count_bit(string path) { int ret = 0; unsigned long long x[kChunkSize]; ifstream fin(path.c_str(), ios::in | ios::binary); while (true) { fin.read(reinterpret_cast<char *>(&x), sizeof(unsigned long long) * kChunkSize); if (fin.eof()) break; #if METHOD == 1 for (int i = 0; i < kChunkSize; ++i) ret += POP2(x[i]); #elif METHOD == 2 for (int i = 0; i < kChunkSize; ++i) ret += POP2(x[i]); #elif METHOD == 3 ret += POP_ARRAY(x, kChunkSize); #elif METHOD == 4 ret += POP_ARRAY2(x, kChunkSize); #endif } fin.close(); return ret; } int main(int argc, char *argv[]) { string paths[] = {"00.bin", "01.bin", "02.bin", "03.bin", "04.bin", "05.bin", "06.bin", "07.bin", "08.bin", "09.bin", "10.bin", "11.bin"}; string prefix = "../data/"; int expected[] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1 << 30}; init_table(); for (int i = 0; i < 12; ++i) { string path = prefix + paths[i]; assert(count_bit(path) == expected[i]); printf("%s: %f sec\n", paths[i].c_str(), time_it(3, 10, count_bit, path)); } return 0; }
実行結果
00.bin: 0.571133 sec 01.bin: 0.563557 sec 02.bin: 0.562696 sec 03.bin: 0.559637 sec 04.bin: 0.563524 sec 05.bin: 0.567378 sec 06.bin: 0.567318 sec 07.bin: 0.563231 sec 08.bin: 0.561556 sec 09.bin: 0.563927 sec 10.bin: 0.566986 sec 11.bin: 0.563692 sec
1 回の処理あたり 約 56ms。テーブル検索版よりも速い結果となった。
C++ 実測 (4)
実測(3) の配列横断処理を行う前処理の単位を 8ビットから 16ビットに変更して計測。
コードの1行目を #define METHOD 4 とする
実行結果
00.bin: 0.646128 sec 01.bin: 0.645220 sec 02.bin: 0.640301 sec 03.bin: 0.638049 sec 04.bin: 0.636916 sec 05.bin: 0.634473 sec 06.bin: 0.638034 sec 07.bin: 0.649389 sec 08.bin: 0.642163 sec 09.bin: 0.639769 sec 10.bin: 0.635853 sec 11.bin: 0.637499 sec
1 回の処理あたり 約 63ms。8ビット版よりも遅くなってしまった。
Scala の並列処理の実測はまた後日。
0 件のコメント:
コメントを投稿