当前位置: 首页 > 知识库问答 >
问题:

如何在C中快速将大缓冲区写入二进制文件?

贲宏硕
2023-03-14

我正试图将大量数据写入我的SSD(固态驱动器)。我指的是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可以以150MB/s到200MB/s之间的速度将文件从其他SSD复制到此SSD。所以至少快了7倍。这就是为什么我认为我应该能够走得更快。

有没有办法加快我的写作速度?

共有3个答案

袁宜
2023-03-14

我看不出std::stream/FILE/device之间有什么区别。在缓冲和非缓冲之间。

另请注意:

  • SSD驱动器在填满时“倾向于”降低速度(较低的传输速率)

我看到代码以63秒的速度运行<因此,传输速率为:260M/s(我的SSD看起来比你的略快)。

64 * 1024 * 1024 * 8 /*sizeof(unsigned long long) */ * 32 /*Chunks*/

= 16G
= 16G/63 = 260M/s

我从std::f流移动到FILE*没有增加。

#include <stdio.h>

using namespace std;

int main()
{
    
    FILE* stream = fopen("binary", "w");

    for(int loop=0;loop < 32;++loop)
    {
         fwrite(a, sizeof(unsigned long long), size, stream);
    }
    fclose(stream);

}

因此,C流的工作速度与底层库允许的一样快。

但我认为将操作系统与构建在操作系统之上的应用程序进行比较是不公平的。应用程序不能做出任何假设(它不知道驱动器是SSD),因此使用操作系统的文件机制进行传输。

虽然操作系统不需要做任何假设。它可以告诉所涉及的驱动器类型并使用最佳技术来传输数据。在这种情况下,直接内存到内存传输。尝试编写一个程序,将80G从内存中的一个位置复制到另一个位置,看看速度有多快。

我更改了代码以使用较低级别的调用:
即无缓冲。

#include <fcntl.h>
#include <unistd.h>


const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    int data = open("test", O_WRONLY | O_CREAT, 0777);
    for(int loop = 0; loop < 32; ++loop)
    {   
        write(data, a, size * sizeof(unsigned long long));
    }   
    close(data);
}

这没什么区别。

注意:我的驱动器是SSD驱动器。如果您使用的是普通驱动器,您可能会看到上述两种技术之间的差异。但正如我所料,非缓冲和缓冲(当写入大于缓冲区大小的大数据块时)没有区别。

你试过用C语言复制文件的最快方法吗

int main()
{
    std::ifstream  input("input");
    std::ofstream  output("ouptut");

    output << input.rdbuf();
}
姜弘化
2023-03-14

按顺序尝试以下操作:

>

注意:我注意到非常大的缓冲区往往会降低性能。我注意到以前使用16-MiB缓冲区而不是512-KiB缓冲区会导致速度下降。

使用_open(或_topen如果您想正确使用Windows)打开文件,然后使用_write。这可能会避免大量缓冲,但并不一定。

使用Windows特有的功能,如CreateFileWriteFile。这将避免标准库中的任何缓冲。

方昊
2023-03-14

这就成功了(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;
}

我刚刚在36秒内计时了8GB,大约是220MB/s,我认为这使我的SSD达到最大值。同样值得注意的是,问题中的代码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(新要求)编译。我用两种设置运行代码

  • 笔记本电脑,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

太长别读:我的测量表明使用std::fstreamoverFILE

 类似资料:
  • 我无法重写从二进制缓冲区获取的文件,我已与原始文件进行了检查,所有字节都相同。 这是从NodeJS创建的文件: 这是最初的一个: 你可以比较这两个文件,每个字节都是一样的,我猜NodeJS的编码不是正确的。 这是一段代码 你可以试着在你的节点上运行它,看看结果是错误的。 我能做些什么来修复它?

  • 下面的代码尝试使用缓冲区将WAV文件的头写入流,然后将其写入可写流。 出于某种原因,文件的前8个字节是错误的: 第一行应该是: 这条线路有故障: 它需要:

  • 问题内容: 在Python中,您可以将StringIO用作字符数据的文件状缓冲区。内存映射文件基本上对二进制数据执行类似的操作,但是它需要一个用作基础的文件。Python是否有一个用于二进制数据且仅是内存的文件对象,相当于Java的ByteArrayOutputStream? 我的用例是我想在内存中创建一个ZIP文件,而ZipFile需要一个类似文件的对象。 问题答案: 您可能正在寻找io.Byt

  • 本文向大家介绍如何将二进制NodeJS缓冲区转换为JavaScript ArrayBuffer?,包括了如何将二进制NodeJS缓冲区转换为JavaScript ArrayBuffer?的使用技巧和注意事项,需要的朋友参考一下 直接访问 buf.buffer属性,将二进制NodeJS Buffer转换为JavaScript ArrayBuffer。通过原始Buffer实例进行的写操作将写入Arra

  • 我有一个二进制数据缓冲区,我想存储在协议缓冲区中。 在留档(https://developers.google.com/protocol-buffers/docs/proto#scalar)中,它说类型等价于C中的。我无法相信这一点,所以我不得不尝试它,是的,这似乎是这样... 本协议: 给出一个包含以下内容的消息定义: 公共setter/getter API如下所示: 当然,这不是在消息中存储二

  • 问题内容: 我有一些二进制数据的缓冲区: 我想追加。 如何追加更多二进制数据?我正在搜索文档,但是要附加数据,它必须是字符串,否则,将发生错误( TypeError:Argument必须是string ): 然后,我在这里看到的唯一解决方案是为每个附加的二进制数据创建一个新缓冲区,并将其复制到具有正确偏移量的主缓冲区中: 但这似乎效率不高,因为我必须为每个追加实例化一个新的缓冲区。 您知道附加二进