[英]FILE* and file descriptor read/write performance
使用FILE*
或file descriptor
API處理本地磁盤文件二進制數據讀寫有什么性能影響? 哪一種方式比另一種方式具有任何優勢?
在性能方面, fread()
或read()
要比另一個更好嗎? 他們的行為,緩存或系統資源使用情況如何不同?
在性能方面, fwrite()
或write()
要比另一個好嗎? 他們的行為,緩存或系統資源使用情況如何不同?
read
和write
的系統調用:因此它們在用戶空間緩沖。 您在那里提交的所有內容都將直接進入內核。 底層文件系統可能具有內部緩沖,但這里最大的性能影響將來自每次調用時更改為內核空間。
fread
和fwrite
是用戶空間庫調用,默認情況下是緩沖的。 因此,這些將把您的訪問組合在一起,以使它們更快(理論上)。
親自試一試: read
一次從文件中一個字節,然后fread
同時從它的一個字節。 后者應該快4000倍。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
int main() {
struct rusage usage_start, usage_end;
getrusage(RUSAGE_SELF, &usage_start);
int fd = open("/dev/zero", O_RDONLY);
int i = 0x400 * 0x400; // 1 MB
char c;
while (i--)
read(fd, &c, 1);
close(fd);
getrusage(RUSAGE_SELF, &usage_end);
printf("Time used by reading 1MiB: %zu user, %zu system.\n", ((usage_end.ru_utime.tv_sec - usage_start.ru_utime.tv_sec)* 1000000) + usage_end.ru_utime.tv_usec - usage_start.ru_utime.tv_usec, ((usage_end.ru_stime.tv_sec - usage_start.ru_stime.tv_sec)* 1000000) + usage_end.ru_stime.tv_usec - usage_start.ru_stime.tv_usec);
getrusage(RUSAGE_SELF, &usage_start);
FILE * fp = fopen("/dev/zero", "r");
i = 0x400 * 0x400; // 1 MB
while (i--)
fread(&c, 1, 1, fp);
fclose(fp);
getrusage(RUSAGE_SELF, &usage_end);
printf("Time used by freading 1MiB: %zu user, %zu system.\n", ((usage_end.ru_utime.tv_sec - usage_start.ru_utime.tv_sec)* 1000000) + usage_end.ru_utime.tv_usec - usage_start.ru_utime.tv_usec, ((usage_end.ru_stime.tv_sec - usage_start.ru_stime.tv_sec)* 1000000) + usage_end.ru_stime.tv_usec - usage_start.ru_stime.tv_usec);
return 0;
}
在我的OS X上返回:
Time used by reading 1MiB: 103855 user, 442698 system.
Time used by freading 1MiB: 20146 user, 256 system.
stdio
函數只是圍繞適當的系統調用包裝優化代碼。
這是程序的一個strace
:
getrusage(RUSAGE_SELF, {ru_utime={0, 0}, ru_stime={0, 0}, ...}) = 0
open("/dev/zero", O_RDONLY) = 3
然后跟隨1048576次
read(3, "\0", 1) = 1
其余的:
close(3) = 0
getrusage(RUSAGE_SELF, {ru_utime={0, 200000}, ru_stime={5, 460000}, ...}) = 0
這是fopen
一部分:
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2aaaaaaae000
getrusage(RUSAGE_SELF, {ru_utime={0, 200000}, ru_stime={5, 460000}, ...}) = 0
// ...
open("/dev/zero", O_RDONLY) = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 5), ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffffffb050) = -1 ENOTTY (Inappropriate ioctl for device)
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2aaaaaaaf000
現在256次:
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096
請注意,雖然我逐字節讀取,但stdio
庫一次stdio
獲取一頁文件內容。
其余大部分是釋放:
close(3) = 0
munmap(0x2aaaaaaaf000, 4096) = 0
getrusage(RUSAGE_SELF, {ru_utime={0, 230000}, ru_stime={5, 460000}, ...}) = 0
write(1, "Time used by reading 1MiB: 20000"..., 106Time used by reading 1MiB: 200000 user, 5460000 system.
Time used by freading 1MiB: 30000 user, 0 system.
) = 106
exit_group(0) = ?
關於訪問磁盤上的文件,答案是:這取決於。 較高級別的函數可以啟用緩沖,這可以減少物理I / O的數量,這意味着可以減少進行的read()
/ write()
調用的實際數量(fread()調用read()來訪問磁盤)等)。
因此,在啟用緩沖的情況下,高級功能的優勢在於,您通常會看到更好的性能,而無需考慮您正在做什么。 低級功能的優勢在於,如果您知道應用程序將如何執行操作,則可以通過直接管理自己的緩沖來提高性能。
我同意,fread / fwrite比讀/寫快,但是:
1)如果文件將被隨機訪問,則fwrite / fread無法如此有效地使用,並且大多數時候它們可能導致性能損失。
2)如果文件正在由另一個進程或線程共享,那么它將不會那么快並且無法使用,除非您每次寫入文件時都使用flush()命令,並且在這種情況下還提高了速度與寫命令至少相等地減小。 同樣,由於使用了fread命令,因為它使用其緩沖區讀取可能不會更新的數據,或者如果關心更新,則必須丟棄已讀取的內容以讀取新數據。
因此,這取決於。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.