[解決済み】C++で大きなバッファをバイナリファイルに高速に書き込むには?
質問
SSD(solid state drive)に大容量のデータを書き込もうとしています。大量というのは80GBのことです。
解決策をウェブで見て回りましたが、一番良かったのはこれでした。
#include <fstream>
const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
std::fstream myfile;
myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
//Here would be some error handling
for(int i = 0; i < 32; ++i){
//Some calculations to fill a[]
myfile.write((char*)&a,size*sizeof(unsigned long long));
}
myfile.close();
}
Visual Studio 2010と完全な最適化でコンパイルし、Windows7で実行すると、このプログラムは最大で20MB/s程度になります。本当に気になるのは、Windows は他の SSD からこの SSD に 150MB/s から 200MB/s の間のどこかでファイルをコピーできることです。つまり、少なくとも7倍は速いということです。だから私は、もっと速くできるはずだと思うのです。
どうすれば執筆のスピードを上げられるか、何かアイデアはありますか?
どのように解決するのですか?
これでうまくいきました(2012年当時)。
#include <stdio.h>
const unsigned long long size = 8ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
FILE* pFile;
pFile = fopen("file.binary", "wb");
for (unsigned long long j = 0; j < 1024; ++j){
//Some calculations to fill a[]
fwrite(a, 1, size*sizeof(unsigned long long), pFile);
}
fclose(pFile);
return 0;
}
今、8GBを36secで計時しましたが、これは約220MB/sで、SSDの最大値だと思います。また、質問のコードが1つのコアを100%使用するのに対し、このコードは2-5%しか使用していないことにも注目すべきです。
皆さん、どうもありがとうございました。
更新情報 : 5年が経過し、今は2017年です。コンパイラ、ハードウェア、ライブラリ、そして私の要求も変化しています。そのため、コードにいくつか変更を加え、新しい測定を行いました。
まずはコードから。
#include <fstream>
#include <chrono>
#include <vector>
#include <cstdint>
#include <numeric>
#include <random>
#include <algorithm>
#include <iostream>
#include <cassert>
std::vector<uint64_t> GenerateData(std::size_t bytes)
{
assert(bytes % sizeof(uint64_t) == 0);
std::vector<uint64_t> data(bytes / sizeof(uint64_t));
std::iota(data.begin(), data.end(), 0);
std::shuffle(data.begin(), data.end(), std::mt19937{ std::random_device{}() });
return data;
}
long long option_1(std::size_t bytes)
{
std::vector<uint64_t> data = GenerateData(bytes);
auto startTime = std::chrono::high_resolution_clock::now();
auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
myfile.write((char*)&data[0], bytes);
myfile.close();
auto endTime = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}
long long option_2(std::size_t bytes)
{
std::vector<uint64_t> data = GenerateData(bytes);
auto startTime = std::chrono::high_resolution_clock::now();
FILE* file = fopen("file.binary", "wb");
fwrite(&data[0], 1, bytes, file);
fclose(file);
auto endTime = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}
long long option_3(std::size_t bytes)
{
std::vector<uint64_t> data = GenerateData(bytes);
std::ios_base::sync_with_stdio(false);
auto startTime = std::chrono::high_resolution_clock::now();
auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
myfile.write((char*)&data[0], bytes);
myfile.close();
auto endTime = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}
int main()
{
const std::size_t kB = 1024;
const std::size_t MB = 1024 * kB;
const std::size_t GB = 1024 * MB;
for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option1, " << size / MB << "MB: " << option_1(size) << "ms" << std::endl;
for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option2, " << size / MB << "MB: " << option_2(size) << "ms" << std::endl;
for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option3, " << size / MB << "MB: " << option_3(size) << "ms" << std::endl;
return 0;
}
このコードは Visual Studio 2017 と g++ 7.2.0 (新しい要件) でコンパイルします。 2つのセットアップでコードを実行しました。
- ノートパソコン、Core i7、SSD、Ubuntu 16.04、g++ Version 7.2.0 with -std=c++11 -march=native -O3
- デスクトップ、Core i7、SSD、Windows 10、Visual Studio 2017 バージョン 15.3.1 with /Ox /Ob2 /Oi /Ot /GT /GL /Gy
ということで、以下のような測定値が得られました(1MBの値は明らかな異常値なので捨てた後)。 option1とoption3はどちらもSSDが最大になる時間です。当時はoption2が私の古いマシンで最速のコードだったので、これを見るのは予想外でした。
TL;DR
: 私の測定では、次のようになります。
std::fstream
オーバー
FILE
.
関連
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] Bashで通常のファイルが存在しないかどうかを判断する方法を教えてください。
-
[解決済み] Git リポジトリで削除されたファイルを検索して復元する方法
-
[解決済み] Pythonでファイルやフォルダを削除する方法は?
-
[解決済み] Microsoft Officeをインストールせずに、C#でExcel(.XLSおよび.XLSX)ファイルを作成するにはどうすればよいですか?
-
[解決済み] ファイルの内容からJavaの文字列を作成するにはどうすればよいですか?
-
[解決済み] ファイルを作成し、書き込むにはどうすればよいですか?
-
[解決済み] 出力をファイルや標準出力にリダイレクトする方法
-
[解決済み] ファイルに行を書き込む正しい方法?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み】正気で、安全で、効率的な方法でファイルをコピーする。
-
[解決済み】ファイルアクセスにmmapを使用するのはどんな場合ですか?