用现代C++最快的方法生成一个包含任意二进制数据的1GB文件

wqsoz72f  于 2023-02-17  发布在  其他
关注(0)|答案(2)|浏览(185)

所以,我试过了:

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();

这是可行的,但需要超过15分钟的释放。我运行的1TB NVME固态硬盘和64GB的RAM,我觉得我应该能够在几秒钟内完成,或不到一秒。
我确实可以使用C++20。我更喜欢一些现代的可读性,只要它的性能合理。我觉得任何超过10秒的东西都太慢了。
需要说明的是,将所有数据推回数组几乎是瞬间完成的,但写入数据却要花费几分钟。

ia2d9nvy

ia2d9nvy1#

一次写入一个字节会导致很高的开销,* 特别是 * 如果文件是无缓冲的,并且您对每个字节都要进行系统调用。同样,operator<<写入 * formated * 数据,这对于字符来说是正确的,但更容易出错,因为许多输入在写入过程中会被格式化。
相反,您已经可以访问一个充满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();
}

在strace中,上面的示例仅使用一个系统调用来写入:

writev(3, [{iov_base=NULL, iov_len=0}, {iov_base="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., iov_len=2000000000}], 2) = 2000000000

更好的做法是,将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();
}

最后,注意你放入缓冲区的内容。\ba是一个多字节字符-\b0x08的转义符,aa。在我的机器上,它被截断为a。如果你想在你的文件中得到更多有趣的数据,改为改变填充缓冲区的循环。

zlhcx6iw

zlhcx6iw2#

不要在内存中创建2GB的数组。
以块大小为例,大约65536字节,确切的最佳大小取决于操作系统和文件系统。
填充该块大小的缓冲区并使用iostream::write写入。
大概是这样的

const size_t sz = 65536;
uint8_t data[sz];
for ( /* blah */) {
    fill_random_data(data, sz);
    outfile.write(data, sz);
}

相关问题