C++ で大きなバッファをバイナリ ファイルに高速に書き込むにはどうすればよいでしょうか? 質問する

C++ で大きなバッファをバイナリ ファイルに高速に書き込むにはどうすればよいでしょうか? 質問する

大量のデータを SSD (ソリッド ステート ドライブ) に書き込もうとしています。大量というのは、80 GB のことです。

解決策を求めてウェブを閲覧しましたが、最も良い解決策は次のとおりでした。

#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 でコンパイルされ、完全に最適化され、Windows 7 で実行されるこのプログラムは、最大で約 20MB/秒になります。本当に気になるのは、Windows が別の SSD からこの SSD にファイルをコピーする場合、150MB/秒から 200MB/秒の速度でコピーできるということです。つまり、少なくとも 7 倍の速度です。だから、もっと速くできるはずだと私は思います。

文章を書くスピードを上げる方法はありますか?

ベストアンサー1

これはうまくいきました(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 を 36 秒で実行したところ、約 220MB/秒となり、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++ バージョン 7.2.0、-std=c++11 -march=native -O3
  • デスクトップ、Core i7、SSD、Windows 10、Visual Studio 2017 バージョン 15.3.1 (/Ox /Ob2 /Oi /Ot /GT /GL /Gy 付き)

これにより、次の測定値が得られました (1MB の値は明らかに外れ値であるため除外しました)。ここに画像の説明を入力してください ここに画像の説明を入力してくださいオプション 1 とオプション 3 の両方で SSD が最大限に活用されました。オプション 2 は当時、私の古いマシンで最速のコードだったので、このような結果になるとは思っていませんでした。

TL;DR : 私の測定では、std::fstreamを超える量を使用する必要があることを示していますFILE

おすすめ記事