c++で行列をバイト列で保存&復帰
巨大な行列を沢山のファイルに保存するととんでもないサイズになるのでどうにかしてディスク容量を節約できないかと考えた結果、行列をバイナリで保存と読み込みをするテンプレートクラスを書けばいいやと考えた。
どの程度ポータブルなのかちょっとわからないので注意して使う。
/* * iomatrix.hpp * 2次元配列のバイナリ形式での保存と読み込み * * double matrix[m][n]; * MatrixReader<double, m, n> raeder("mat.bin"); // MxNサイズの行列リーダ * reader.read(matrix); // matrixがファイルの内容で上書きされる * * MatrixWriter<double, m, n> writer("result.bin"); * writer.write(matrix); * */ // // データ長の違うプラットフォーム間では動かないよなあ // エンディアンの違いを吸収しなければならない? // どの程度可搬性があるのかわからない // #ifndef IO_MATRIX_HPP #define IO_MATRIX_HPP #include<iostream> #include<string> using namespace std; template<class type, int m, int n> class MatrixWriter { string fname; public: MatrixWriter (string name): fname(name){} ~MatrixWriter (){} void write (type p[m][n]) { FILE *fp = fopen(fname.c_str(), "wb"); if (!fp){ perror(fname.c_str()); return; } for (int i = 0; i < m; i++) fwrite(p[i], sizeof(type), n, fp); fclose(fp); }; }; template<class type, int m, int n> class MatrixReader { string fname; public: MatrixReader(string name): fname(name){} ~MatrixReader(){} void read(type p[m][n]) { FILE *fp = fopen(fname.c_str(), "rb"); if (!fp){ perror(fname.c_str()); return; } for (int i = 0; i < m; i++) fread(p[i], sizeof(type), n, fp); fclose(fp); } }; #endif /* IO_MATRIX_HPP */
実行
九九表をバイナリで保存&読み込み
// 九九表をバイナリで保存&読み込み #include<iostream> #include"iomatrix.hpp" using namespace std; void dump(int m[9][9]) { for (int i = 0; i < 9; i++){ for (int j = 0; j < 9; j++) printf("%2d ", m[i][j]); printf("\n"); } printf("\n"); } int main() { // write int a[9][9]; for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) a[i][j] = (i+1)*(j+1); dump(a); MatrixWriter<int, 9, 9> writer("out.bin"); writer.write(a); // read int b[9][9]; MatrixReader<int, 9, 9> reader("out.bin"); reader.read(b); dump(b); return 0; }
実行結果
1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81
double型の行列をバイナリ形式で保存するのとテキストファイルで保存するのと比較してみる
#include<iostream> #include"iomatrix.hpp" using namespace std; double urand(){ return random() / (double)RAND_MAX; } double xrand() { return 1e5 - 2e5*urand(); } const int m = 2048; const int n = 4096; double mat[m][n]; int main() { srandom(time(NULL)); for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) mat[i][j] = xrand(); printf("init complete\n"); // バイナリファイルで書き出し MatrixWriter<double, m,n> writer("mat.bin"); writer.write(mat); // テキストファイルで書き出し FILE *fp = fopen("mat.txt", "w"); for (int i = 0; i < m; i++){ for (int j = 0; j < n; j++){ fprintf(fp, "%lf ", mat[i][j]); } fprintf(fp, "\n"); } }
ファイルサイズ比較
バイナリファイルとテキストファイルをそれぞれtar.bz2とtar.gzとzipで圧縮してサイズを比較してみた
$ du -h mat.bin mat.txt 65M mat.bin 108M mat.txt $ tar cvjf mat.bin.tar.bz2 mat.bin $ tar cvjf mat.txt.tar.bz2 mat.txt $ tar cvzf mat.bin.tar.gz mat.bin $ tar cvzf mat.txt.tar.gz mat.txt $ zip mat.bin.zip mat.bin $ zip mat.txt.zip mat.txt $ du -h mat.bin mat.bin.tar.bz2 mat.bin.tar.gz mat.bin.zip mat.txt mat.txt.tar.bz2 mat.txt.tar.gz mat.txt.zip 65M mat.bin 63M mat.bin.tar.bz2 62M mat.bin.tar.gz 62M mat.bin.zip 108M mat.txt 47M mat.txt.tar.bz2 53M mat.txt.tar.gz 53M mat.txt.zip
バイナリファイルにしたら圧縮かけたときにほとんど小さくならなくなってしまった・・・
R/W速度
測るのめんどくさい。fscanfとfread、fprintf,fwrite位違う速度。行列が大きいと明らかにバイナリR/Wが早い。
シリアライズ
3次元ベクトル構造体の行列のシリアライズとかできるんでね?と思ったので試したみた
#include<stdio.h> #include<stdlib.h> #include<time.h> #include"binaryMatrixIO.hpp" float urand() { return rand() / (float)RAND_MAX; } typedef struct vector{ float x, y, z; } vec3; void dump(vec3 v[3][3]) { for (int i = 0; i < 3; i++){ for (int j = 0; j < 3; j++){ vec3 &p = v[i][j]; printf("%f %f %f\n", p.x, p.y, p.z); } } printf("\n"); } int main() { vec3 u[3][3]; vec3 v[3][3]; BinaryMatrixWriter<vec3,3,3> writer("out.bin"); BinaryMatrixReader<vec3,3,3> reader("out.bin"); // 乱数で初期化 srand(time(NULL)); for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++){ vec3 &p = u[i][j]; p.x = urand(); p.y = urand(); p.z = urand(); } dump(u); // 書き出し writer.write(u); // 読み込み reader.read(v); dump(v); return 0; }
出力
0.142629 0.618720 0.695503 0.284359 0.001272 0.300783 0.903097 0.981465 0.468198 0.532429 0.858546 0.180134 0.643004 0.195832 0.974500 0.247794 0.828028 0.904411 0.261974 0.088152 0.708580 0.295095 0.503187 0.353356 0.666642 0.023045 0.532530 0.142629 0.618720 0.695503 0.284359 0.001272 0.300783 0.903097 0.981465 0.468198 0.532429 0.858546 0.180134 0.643004 0.195832 0.974500 0.247794 0.828028 0.904411 0.261974 0.088152 0.708580 0.295095 0.503187 0.353356 0.666642 0.023045 0.532530
できたー。