Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/125.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我的C++;磁盘写测试比使用bash简单地复制文件慢得多?_C++_Performance_Fstream - Fatal编程技术网

C++ 为什么我的C++;磁盘写测试比使用bash简单地复制文件慢得多?

C++ 为什么我的C++;磁盘写测试比使用bash简单地复制文件慢得多?,c++,performance,fstream,C++,Performance,Fstream,使用下面的程序,我试图测试使用std::ofstream写入磁盘的速度 在编写一个1GB文件时,我实现了大约300 MiB/s 但是,使用cp命令进行简单的文件复制至少要快一倍 我的程序是否达到了硬件限制,或者是否可以加快速度 #include <chrono> #include <iostream> #include <fstream> char payload[1000 * 1000]; // 1 MB void test(int MB) {

使用下面的程序,我试图测试使用
std::ofstream
写入磁盘的速度

在编写一个1GB文件时,我实现了大约300 MiB/s

但是,使用
cp
命令进行简单的文件复制至少要快一倍

我的程序是否达到了硬件限制,或者是否可以加快速度

#include <chrono>
#include <iostream>
#include <fstream>

char payload[1000 * 1000]; // 1 MB

void test(int MB)
{
    // Configure buffer
    char buffer[32 * 1000];
    std::ofstream of("test.file");
    of.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

    auto start_time = std::chrono::steady_clock::now();

    // Write a total of 1 GB
    for (auto i = 0; i != MB; ++i)
    {
        of.write(payload, sizeof(payload));
    }

    double elapsed_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start_time).count();
    double megabytes_per_ns = 1e3 / elapsed_ns;
    double megabytes_per_s = 1e9 * megabytes_per_ns;
    std::cout << "Payload=" << MB << "MB Speed=" << megabytes_per_s << "MB/s" << std::endl;
}

int main()
{
    for (auto i = 1; i <= 10; ++i)
    {
        test(i * 100);
    }
}
更新 我从
std::of stream
更改为
fwrite

#include <chrono>
#include <cstdio>
#include <iostream>

char payload[1024 * 1024]; // 1 MiB

void test(int number_of_megabytes)
{
    FILE* file = fopen("test.file", "w");

    auto start_time = std::chrono::steady_clock::now();

    // Write a total of 1 GB
    for (auto i = 0; i != number_of_megabytes; ++i)
    {
       fwrite(payload, 1, sizeof(payload), file );
    }
    fclose(file); // TODO: RAII

    double elapsed_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start_time).count();
    double megabytes_per_ns = 1e3 / elapsed_ns;
    double megabytes_per_s = 1e9 * megabytes_per_ns;
    std::cout << "Size=" << number_of_megabytes << "MiB Duration=" << long(0.5 + 100 * elapsed_ns/1e9)/100.0 << "s Speed=" << megabytes_per_s << "MiB/s" << std::endl;
}

int main()
{
    test(256);
    test(512);
    test(1024);
    test(1024);
}
while fread() from source to buffer
  fwrite() buffer to destination file
它的速度与dd的速度一样快:

time dd if=/dev/zero of=test.file bs=1024 count=0 seek=1048576

real    0m1.539s
user    0m0.001s
sys 0m0.344s
  • cp
    直接使用系统调用
    read(2)
    mmap(2)

  • 我敢打赌,无论是在CP还是在文件系统中,它都是聪明的。如果它在CP中,那么可能是您正在复制的文件中有很多0,而CP正在检测到这一点并编写文件的版本。cp的手册页上说:“默认情况下,稀疏源文件通过粗略的启发式检测,相应的DEST文件也变为稀疏。”这可能意味着一些事情,但其中之一是cp可以生成文件的稀疏版本,这将需要更少的磁盘写时间

    如果它在您的文件系统中,那么它可能是


    作为第三种情况,也可能是您的操作系统或磁盘固件中的某些东西正在将读写转换为一些特殊指令,而这些指令不需要像程序所需的那样多的同步(较低的总线使用意味着较少的延迟)。

    您使用的缓冲区大小相对较小。较小的缓冲区意味着每秒有更多的操作,这会增加开销。磁盘系统在接收到读/写请求并开始处理它之前有少量的延迟;缓冲区越大,成本就越高。较小的缓冲区也可能意味着磁盘将花费更多的时间进行查找

    您没有同时发出多个请求-您需要在下一次开始之前完成一次读取。这意味着磁盘可能有死区时间,而它什么也不做。由于所有的写操作都依赖于所有的读操作,并且读操作是串行的,因此磁盘系统中的读请求将被耗尽(由于写操作会带走读操作,所以这种情况会加倍)

    所有读取请求中请求的读取字节总数应大于磁盘系统的带宽延迟乘积。如果磁盘有0.5毫秒的延迟和4 GB/秒的性能,那么您希望在任何时候都有4 GB*0.5毫秒=2 MB的未完成读取

    您没有使用任何操作系统提示进行顺序读取

    要解决此问题,请执行以下操作:

    • 将代码更改为始终有多个未完成的读取请求
    • 有足够的未完成读取请求,以便等待至少2MB的数据
    • 使用posix_fadvise()标志帮助操作系统磁盘调度和页面缓存优化
    • 考虑使用mmap来减少开销
    • 每个读取请求使用较大的缓冲区大小以减少开销
    此答案提供了更多信息:

    首先,您并不是真正测量磁盘写入速度,而是(部分)将数据写入操作系统磁盘缓存的速度。要真正测量磁盘写入速度,应在计算时间之前将数据刷新到磁盘。如果不刷新,则根据文件大小和可用内存可能会有所不同

    计算中似乎也有问题。您没有使用
    MB
    的值

    还要确保缓冲区大小是2的幂,或者至少是磁盘页面大小(4096字节)的倍数:
    charbuffer[32*1024]。对于
    有效负载
    也可以这样做。(看起来您在添加计算的编辑中将其从1024更改为1000)

    不要使用流将(二进制)数据缓冲区写入磁盘,而是使用
    file*、fopen()、fwrite()、fclose()直接写入文件。有关示例和一些计时,请参见


    要复制文件:请以只读模式打开源文件,如果可能的话,使用
    fread(),fwrite()

    这将为您提供与OS文件拷贝速度相当的速度(您可能需要测试一些不同的缓冲区大小)

    使用内存映射可能会稍微快一些:

    open src, create memory mapping over the file
    open/create dest, set file size to size of src, create memory mapping over the file
    memcpy() src to dest
    

    对于大文件,应使用较小的映射视图。

    问题是您为fstream指定的缓冲区太小

    char buffer[32 * 1000];
    std::ofstream of("test.file");
    of.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
    
    您的应用程序以用户模式运行。为了写入磁盘,ofstream调用在内核模式下执行的系统函数。然后将数据传输到系统缓存,然后传输到HDD缓存,然后将数据写入磁盘

    此缓冲区大小影响系统调用的数量(每32*1000字节调用一次)。在系统调用期间,操作系统必须将执行上下文从用户模式切换到内核模式,然后再切换回来。切换上下文是开销。在Linux中,它相当于大约2500-3500个简单的CPU命令。因此,您的应用程序在上下文切换上花费的CPU时间最多

    在您使用的第二个应用程序中

    FILE* file = fopen("test.file", "w");
    
    默认情况下,文件使用更大的缓冲区,这就是它生成更高效代码的原因。您可以尝试使用setvbuf指定小缓冲区。在这种情况下,您应该会看到相同的性能下降

    请注意,在您的情况下,瓶颈不是硬盘性能。它是上下文转换


    您是否正在测试程序的版本优化?您是否尝试过增加缓冲区大小?它不应该是
    double-megabytes\u per\u ns=MB/已用\u ns?您还应该以二进制模式打开流,以便与其他写入方法进行公平比较。使用流的
    std::of(“test.file”,std::ios::binary)
    。我在stream
    fwrite
    之间获得了非常接近的性能(差异在于测量误差的范围)。编译器VC++2017。在我的机器上,有效负载的测试结果=1024 MiB,平均运行50次以上。<
    char buffer[32 * 1000];
    std::ofstream of("test.file");
    of.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
    
    FILE* file = fopen("test.file", "w");