简体   繁体   English

c 中的读/写程序复制文件,我认为它复制的时间太长

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

First code uses preset buffer and when i set buffer to 512byte and i need to copy 100MB file it takes about 1 second, but when I use 1byte buffer it takes over 3 minutes to copy 100MB file, on the other hand i have other code that uses fread and fwrite functions and it is about 0.5sec faster on 512byte buffer but it only takes him about 13 seconds to copy 100 mb file with 1byte buffer can someone see any error in code that uses system calls(read, write, open)第一个代码使用预设缓冲区,当我将缓冲区设置为 512 字节并且我需要复制 100MB 文件时大约需要 1 秒,但是当我使用 1 字节缓冲区时复制 100MB 文件需要超过 3 分钟,另一方面我还有其他代码使用 fread 和 fwrite 函数,它在 512 字节缓冲区上快了大约 0.5 秒,但是用 1 字节缓冲区复制 100 MB 文件只需要他大约 13 秒,有人可以看到使用系统调用(读、写、打开)的代码中的任何错误吗


1. Code that uses(read, write...) 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. Code that uses(fread, fwrite...) 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;
}

EDIT: These are my measurements for buffers I took 20 measurements for each buffer and each file malaDat(1byte), srednjaDar(100MB), velikaDat(1GB)编辑:这些是我对缓冲区的测量我对每个缓冲区和每个文件 malaDat(1byte)、srednjaDar(100MB)、velikaDat(1GB) 进行了 20 次测量我对每个缓冲区和每个文件 malaDat(1byte)、srednjaDar(100MB)、velikaDat(1GB) 进行了 20 次测量

Side note: sizeof(char) is always 1 by definition .旁注:根据定义sizeof(char)始终1 So, just don't use sizeof(char) --it's frowned upon.所以,不要使用sizeof(char) ——它不受欢迎。 And, I think it's adding to your confusion.而且,我认为这增加了你的困惑。

Because your example using read/write is using sizeof(char) as the count (the 3rd argument), it is only transferring one byte on each loop iteration (ie very slow).因为您使用read/write示例使用sizeof(char)作为count (第三个参数),所以它在每个循环迭代中只传输一个字节(即非常慢)。

At a guess, I think you're confusing the count for read/write with the size argument to fread/fwrite .猜测,我认为您将read/write countfread/fwritesize参数混淆了。

What you want is:你想要的是:

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

Also, fread/fwrite will [probably] pick an optimal buffer size [possibly 4096].此外, fread/fwrite将 [可能] 选择最佳缓冲区大小 [可能是 4096]。 You can use setbuf to change that.您可以使用setbuf来更改它。

I've found [from some documentation somewhere] that for most filesystems [under linux, at least] the optimal transfer size is 64KB (ie 64 * 1024 ).我发现 [从某处的一些文档] 对于大多数文件系统 [至少在 linux 以下],最佳传输大小是 64KB(即64 * 1024 )。

So, try setting bufferSize = 64 * 1024 .因此,尝试设置bufferSize = 64 * 1024

IIRC, there is an ioctl/syscall that can return the value of the optimal size, but I forget what it is. IIRC,有一个 ioctl/syscall 可以返回最佳大小的值,但我忘了它是什么。


UPDATE:更新:

Ok but when I choose 1byte buffer it is still too slow it takes more than 40mins to copy.好的,但是当我选择 1 字节缓冲区时,它仍然太慢,需要超过 40 分钟才能复制。

Of course, a 1 byte transfer [buffer] size will produce horrible results.当然,1 字节的传输 [缓冲区] 大小会产生可怕的结果。

and when i use fread and fwrite with same buffer it takes way less time it takes about 3mins why is that?当我将 fread 和 fwrite 与相同的缓冲区一起使用时,它花费的时间更少,大约需要 3 分钟,这是为什么?

How big is the file (ie what are the respective transfer rates [in MB/sec])?文件有多大(即各自的传输速率 [以 MB/秒为单位] 是多少)? I'll assume your system can transfer at 10 MB/sec [conservative--30 MB/sec [minimum] for recently new system].我假设您的系统可以 10 MB/秒的速度传输 [对于最近的新系统,保守的 --30 MB/秒 [最低]]。 So, this is 600 MB/min and the file is approx 1.8 GB?那么,这是 600 MB/分钟,文件大约是 1.8 GB?

When you specify a 1 byte transfer/buffer size to read/write they do exactly what you tell them to do.当您指定 1 字节的传输/缓冲区大小时进行read/write时,它们会完全按照您告诉它们的方式进行。 Transfer 1 byte.传输 1 个字节。 So, you'll do ~2 billion read syscalls and 2 billion write syscalls!!!因此,您将进行约 20 亿次read系统调用和 20 亿次write系统调用!!!

syscalls are generally slow.系统调用通常很慢。

stdio streams have an internal buffer. stdio流有一个内部缓冲区。 This is set to an optimal size, let's say: 4096.这被设置为最佳大小,比方说:4096。

fread [and fwrite ] will fill/drain that buffer by calling [internally] read/write with a count of 4096. fread [和fwrite ] 将通过调用 [内部] read/write计数 4096 来填充/耗尽缓冲区。

So, fread/fwrite are doing 4096 times fewer syscalls.因此, fread/fwrite执行的系统调用减少了 4096 倍。 So, only about 470,000 syscalls.因此,只有大约 470,000 个系统调用。 Quite a reduction.相当大的减少。

The transfer to your buffer from the internal buffer is done a byte at a time, but that is only a short/fast memcpy operation done totally within the userspace application.内部缓冲区到缓冲区传输一次完成一个字节,但这只是一个完全在用户空间应用程序中完成的短/快速memcpy操作。 This is much faster than issuing a syscall on a per byte basis.这比按字节发出系统调用快得多。

So, the transfer size you pass to fread/fwrite does not affect the size of its internal buffer.因此,您传递给fread/fwrite的传输大小不会影响其内部缓冲区的大小。

fread/fwrite only issue a read/write syscall to replenish/drain the stream's internal buffer when it is empty/full [respectively], regardless of what length you give in your fread/fwrite call. fread/fwrite仅在流的内部缓冲区为空/满时 [分别] 发出read/write系统调用以补充/耗尽流的内部缓冲区,而不管您在fread/fwrite调用中给出的长度如何。

If you want to slow down fread/fwrite , look at the man page for setbuf et.如果您想减慢fread/fwrite的速度,请查看setbuf等的手册页。 al.阿尔。 and do:并做:

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

UPDATE #2:更新#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我问是因为这是我的大学任务来进行这项测量,我的同事们得到的系统调用结果比用户调用慢了大约 2 分钟,而我得到的结果要慢得多

If you check with them, I'll bet they're using a larger buffer size.如果您与他们核实,我敢打赌他们正在使用更大的缓冲区大小。

Remember that your original read/write code had a bug that would only transfer a byte at a time, regardless of what you set bufferSize to [from the command line].请记住,您的原始read/write代码有一个错误无论您将bufferSize设置为 [从命令行] 是什么,一次只能传输一个字节。

That's why I changed the loop in my original post.这就是为什么我更改了原始帖子中的循环。

To overachieve...超额完成...

Look at O_DIRECT for open .查看O_DIRECTopen If you use posix_memalign instead of malloc , you can force buffer alignment to be a multiple of the page size (4096) that allows O_DIRECT to work.如果使用posix_memalign而不是malloc ,则可以强制缓冲区 alignment 为允许O_DIRECT工作的页面大小 (4096) 的倍数。 And, setting a buffer size that is a multiple of the page size.并且,将缓冲区大小设置为页面大小的倍数。

This option bypasses the read/write syscall's copy/transfer operation from your userspace from the kernel's internal page/filesystem cache and has the DMA H/W transfer directly to/from your buffer.此选项从内核的内部页面/文件系统缓存绕过用户空间的read/write系统调用的复制/传输操作,并让 DMA H/W直接传输到/从您的缓冲区。

Also, consider adding O_NOATIME另外,考虑添加O_NOATIME

Also, there is a linux syscall that is specifically designed to bypass all userspace memory/buffering to have the kernel do a file-to-file copy.此外,还有一个 linux 系统调用,专门设计用于绕过所有用户空间内存/缓冲,让 kernel 执行文件到文件的复制。 It is sendfile , but it is similar to memcpy but uses file descriptors, an offset and a length.它是sendfile ,但它类似于memcpy但使用文件描述符、偏移量和长度。

And, the fastest way to access file data is to use mmap .而且,访问文件数据的最快方法是使用mmap See my answers:看我的回答:

  1. How does mmap improve file reading speed? mmap如何提高文件读取速度?
  2. read line by line in the most efficient way *platform specific* 以最有效的方式逐行阅读*平台特定*

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM