大數據測試場景常需快速生成超大測試文件。常規逐字符寫入(fputc)極其緩慢,本文通過實測對比 fputc 與 fwrite,揭示緩衝機制對 I/O 性能的決定性影響。
| 函數 | 寫入 1GB 耗時 | 寫入 10GB 耗時 | 核心差異 |
|---|---|---|---|
fputc | ~30 秒 | ~300 秒 (5 分鐘) | 每次調用進入內核,無用戶態緩衝,系統調用開銷巨大 |
fwrite | < 0.1 秒 | ~1 秒 | 用戶態緩衝區(默認 4KB/8KB),批量刷新,系統調用次數減少 4 個數量級 |
圖1 fputc執行效果
圖2 fwrite執行效果
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#include <time.h>
#pragma comment(lib, "winmm.lib")
void main() {
FILE *fd;
char filename[64];
unsigned long filesize, block, i;
char type;
DWORD s_time, e_time;
fpos_t pos;
printf("Input your file name: ");
scanf("%s", filename);
fd = fopen(filename, "wb+");
if (!fd) { perror("Create file fail"); return; }
printf("Input file size (e.g. 10G): ");
scanf("%lu%c", &filesize, &type);
// 根據單位計算塊大小
switch (toupper(type)) {
case 'K': block = 0x400; break; // 1 KB
case 'M': block = 0x100000; break; // 1 MB
case 'G': block = 0x40000000; break; // 1 GB
default: return;
}
s_time = clock();
fgetpos(fd, &pos); // 獲取當前位置(文件開頭)
// 關鍵:使用 fwrite 批量寫入零塊
for (i = 0; i < filesize; i++) {
fsetpos(fd, &pos); // 重置位置(實際可直接連續寫入,此處為演示定位)
fwrite("\0", block, 1, fd); // 寫入一塊零數據
}
// 更高效寫法:預分配一大塊緩衝區循環寫入,或使用 _write/WriteFile 直接 I/O
e_time = clock();
printf("Time used: %.3f seconds\n", (double)(e_time - s_time) / CLOCKS_PER_SEC);
fclose(fd);
}
FILE 結構體內部 _buf,通常 4KB/8KB)。fflush/fclose 時一次性 write 進內核。文件大小 / 緩衝區大小(10GB / 4KB ≈ 260 萬次 vs 100 億次)。// Windows: SetFilePointerEx + SetEndOfFile (瞬間創建 10GB 稀疏文件,不佔實際磁盤空間)
HANDLE hFile = CreateFile("huge.dat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
LARGE_INTEGER li; li.QuadPart = 10LL * 1024 * 1024 * 1024;
SetFilePointerEx(hFile, li, NULL, FILE_BEGIN);
SetEndOfFile(hFile);
CloseHandle(hFile);
// 實際寫入數據時才分配物理頁,極適合測試場景
setvbuf(fp, NULL, _IOFBF, 1024*1024) 設置 1MB 緩衝。mmap (Linux) / CreateFileMapping (Windows) 零拷貝。fallocate (Linux) / SetEndOfFile (Windows) 秒級創建 TB 級文件。I/O 性能瓶頸 90% 來自系統調用開銷與緩衝策略不當。理解 FILE* 緩衝機制、頁緩存原理、稀疏文件特性,是寫出高性能存儲代碼的關鍵。