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/write
的 count
与 fread/fwrite
的 size
参数混淆了。
你想要的是:
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
。查看我的回答:
- How does mmap improve file reading speed?
第一个代码使用预设缓冲区,当我将缓冲区设置为 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/write
的 count
与 fread/fwrite
的 size
参数混淆了。
你想要的是:
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
。查看我的回答:
- How does mmap improve file reading speed?