Read/write 复制文件的 c 程序,我认为它复制的时间太长

Read/write program in c that copies file and i think its taking too long to copy

第一个代码使用预设缓冲区,当我将缓冲区设置为 512 字节时,我需要复制 100MB 文件大约需要 1 秒,但是当我使用 1 字节缓冲区时,复制 100MB 文件需要 3 分钟以上,另一方面我还有其他使用 fread 和 fwrite 函数的代码,它在 512 字节缓冲区上快了大约 0.5 秒,但是他只需要大约 13 秒就可以用 1 字节缓冲区复制 100 MB 文件有人可以看到使用系统调用的代码中的任何错误吗(读取,写,打开)


1.使用的代码(读取,写入...)

int main(int argc, char* argv[])
{
    char sourceName[20], destName[20], bufferStr[20];
    int f1, f2, fRead;
    int bufferSize = 0;
    char* buffer;

    bufferSize = atoi(argv[3]);

    buffer = (char*)calloc(bufferSize, sizeof(char));
    strcpy(sourceName, argv[1]);
   
    f1 = open(sourceName, O_RDONLY);
    if (f1 == -1)
        printf("something's wrong with oppening source file!\n");
    else
        printf("file opened!\n");

    strcpy(destName, argv[2]);
    f2 = open(destName, O_CREAT | O_WRONLY | O_TRUNC | O_APPEND);
    if (f2 == -1)
        printf("something's wrong with oppening destination file!\n");
    else
        printf("file2 opened!");

    fRead = read(f1, buffer, sizeof(char));
    while (fRead != 0)
    {
        write(f2, buffer, sizeof(char));
        fRead = read(f1, buffer, sizeof(char);
    }
    close(f1);
    close(f2);


    return 0;
}

2。使用的代码(fread, fwrite...)

int main(int argc, char* argv[]) {
    FILE* fsource, * fdestination;

    char sourceName[20], destinationName[20], bufferSize[20];
    //scanf("%s %s %s", sourceName, destinationName, bufferSize);
    strcpy(sourceName, argv[1]);
    strcpy(destinationName, argv[2]);
    strcpy(bufferSize, argv[3]);
    int bSize = atoi(bufferSize);
    printf("bSize = %d\n", bSize);
    fsource = fopen(sourceName, "r");
    if (fsource == NULL)
        printf("read file did not open\n");
    else
        printf("read file opened sucessfully!\n");
    
    fdestination = fopen(destinationName, "w");
    if (fdestination == NULL)
        printf("write file did not open\n");
    else
        printf("write file opened sucessfully!\n");

    char *buffer = (char*)calloc(bSize, sizeof(char));
    int flag;

    printf("size of buffer: %d", bSize);

    while (0 < (flag = fread(buffer, sizeof(char), bSize, fsource)))
        fwrite(buffer, sizeof(char), bSize, fdestination);
   

    fclose(fsource);
    fclose(fdestination);
    return 0;
}

编辑: 这些是我对缓冲区的测量 我对每个缓冲区和每个文件 malaDat(1byte)、srednjaDar(100MB)、velikaDat(1GB) 进行了 20 次测量

旁注:sizeof(char)根据定义总是1.所以,不要使用 sizeof(char)——它不受欢迎。而且,我认为这增加了你的困惑。

因为您使用 read/write 的示例是 使用 sizeof(char) 作为 count(第三个参数),它仅传输 一个字节在每个循环迭代中(即非常慢)。

我猜测,您将 read/writecountfread/fwritesize 参数混淆了。

你想要的是:

while (1) {
    fRead = read(f1, buffer, bufferSize);
    if (fRead <= 0)
        break;
    write(f2, buffer, fRead);
}

此外,fread/fwrite 将 [可能] 选择最佳缓冲区大小 [可能是 4096]。您可以使用 setbuf 来更改它。

我[从某处的一些文档]发现对于大多数文件系统[在linux下,至少]最佳传输大小是64KB(即64 * 1024)。

所以,尝试设置 bufferSize = 64 * 1024

IIRC,有一个ioctl/syscall可以return最优尺寸的值,但是忘记是什么了


更新:

Ok but when I choose 1byte buffer it is still too slow it takes more than 40mins to copy.

当然,1 字节的传输 [缓冲区] 大小会产生可怕的结果。

and when i use fread and fwrite with same buffer it takes way less time it takes about 3mins why is that?

文件有多大(即各自的传输速率是多少 [in MB/sec])?我假设您的系统可以在 10 MB/sec [保守--30 MB/sec [最近的新系统的最小值] 传输。所以,这是 600 MB/min 文件大约 1.8 GB?

当您将 1 字节 transfer/buffer 大小指定为 read/write 时,它们会完全 执行您告诉它们的操作。传输 1 个字节。因此,您将执行约 20 亿 read 系统调用和 20 亿 write 系统调用!!!

系统调用通常很慢。

stdio 流有一个 内部 缓冲区。这被设置为最佳大小,比方说:4096.

fread [and fwrite] 将 fill/drain that buffer by calling [internally] read/write with a count of 4096.

因此,fread/fwrite 进行了 4096 次 更少的 系统调用。因此,只有大约 470,000 个系统调用。减少不少。

内部缓冲区您的缓冲区的传输完成一个字节时间,但这只是完全在用户空间应用程序中完成的 short/fast memcpy 操作。这比按字节发出系统调用快

因此,您传递给 fread/fwrite 的传输大小 不会 影响其 内部 缓冲区的大小。

fread/fwrite 仅在 empty/full [分别] 时向 replenish/drain 流的 内部 缓冲区发出 read/write 系统调用,无论您在 fread/fwrite 通话中给予多长时间。

如果您想放慢 fread/fwrite,请查看 setbuf 等的手册页。阿尔。并做:

setbuffer(fsource,NULL,1);
setbuffer(fdestination,NULL,1);

更新#2:

So this is totally normal? I am asking because this is my university task to do this measurements and my colleagues are getting results for system calls about 2 mins slower then user calls and i get much slower results

如果你向他们核实,我敢打赌他们使用了更大的缓冲区大小。

请记住,您的原始 read/write 代码有一个 bug 一次只能传输一个字节,不管您将 bufferSize 设置为 [从命令行]。

这就是为什么我改变了我原来的循环 post。

超额完成...

查看 O_DIRECT 以获得 open。如果您使用 posix_memalign 而不是 malloc,您可以强制缓冲区对齐为允许 O_DIRECT 工作的页面大小 (4096) 的倍数。并且,将缓冲区大小设置为页面大小的倍数。

此选项绕过来自内核内部read/write系统调用copy/transfer用户空间的操作page/filesystem 缓存并具有 DMA H/W 传输 直接 to/from 您的 缓冲区。

此外,考虑添加 O_NOATIME

此外,还有一个 linux 系统调用专门用于绕过 all 用户空间 memory/buffering 让内核执行 file-to-file复制。它是 sendfile,但它类似于 memcpy,但使用文件描述符、偏移量和长度。

并且,最快 访问文件数据的方法是使用 mmap。查看我的回答:

  1. How does mmap improve file reading speed?