生成具有任意二进制数据的 1GB 文件的最快现代 C++ 方法

Jos*_*ers -1 c++ performance file-io

所以,我尝试过这个:

uint64_t size = 1 * 1000 * 1000 * 1000;
std::vector<char> data(size, 'a');

std::ofstream myfile;
myfile.open("test.dat");
for (auto it = data.begin(); it != data.end(); ++it)
{
    myfile << *it;
}

myfile.close();
Run Code Online (Sandbox Code Playgroud)

它可以工作,但需要 15 分钟以上的时间才能发布。我运行的是 1TB NVME SSD 和 64GB RAM,我觉得我应该能够在几秒钟或不到一秒内完成此操作。

我确实可以访问 C++20。我更喜欢现代的东西,因为它具有可读性,只要它具有合理的性能。我觉得超过10秒就太慢了。

需要明确的是,将所有数据推回数组几乎是即时的。但写下来只需要几分钟的时间。

ζ--*_*ζ-- 5

一次写入一个字节会导致很高的开销,特别是当文件未缓冲并且您​​要为每个字节进行系统调用时。同样,operator<<写入格式化数据,这对于 char 来说是正确的,但更容易出错,因为许多输入在写入过程中会被格式化。

相反,您已经可以访问充满的缓冲区char- 最简单的解决方案是使用以下命令写入整个缓冲区ofstream::write

#include <vector>
#include <iostream>
#include <fstream>

int main()
{
    uint64_t size = 2 * 1000 *1000 * 1000;
    std::vector<char> data;
    data.reserve(size);

    for (int i = 0; i < size; ++i)
    {
        data.push_back('a');
    }

    std::ofstream myfile;
    myfile.open("test.dat");
    myfile.write(data.data(), size);

    myfile.close();
}
Run Code Online (Sandbox Code Playgroud)

当straced时,上面的例子只使用了一个系统调用来写:

writev(3, [{iov_base=NULL, iov_len=0}, {iov_base="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., iov_len=2000000000}], 2) = 2000000000
Run Code Online (Sandbox Code Playgroud)

更好的是,写入(例如)1 MB 块 1000 次,以避免分配一整 GB 的 RAM:

#include <vector>
#include <iostream>
#include <fstream>

int main()
{
    uint64_t size = 1000 * 1000;
    std::vector<char> data;
    data.reserve(size);

    for (int i = 0; i < size; ++i)
    {
        data.push_back('a');
    }

    std::ofstream myfile;
    myfile.open("test.dat");
    for (int i = 0; i < 1000; i++) {
        myfile.write(data.data(), size);
    }
    myfile.close();
}
Run Code Online (Sandbox Code Playgroud)

最后,请注意您要放入缓冲区的内容。\bais 是一个多字节字符 -是and is\b的转义。在我的机器上,这被截断为. 如果您希望文件中包含更多有趣的数据,请更改填充缓冲区的循环。0x08aaa