首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言 1 秒創建 10GB 大文件:fwrite vs fputc 性能實測與原理解析

     編輯:關於C語言

需求背景

大數據測試場景常需快速生成超大測試文件。常規逐字符寫入(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);
}

深度原理解析

1. fputc 為何慢?

  • 每次調用觸發 用戶態 → 內核態切換(System Call)。
  • 內核需檢查文件描述符、權限、更新文件指針、分配頁緩存、標記髒頁、處理磁盘調度。
  • 10GB 文件約 100 億次調用,CPU 絕大部分時間耗在上下文切換而非數據拷貝。

2. fwrite 為何快?

  • 用戶態緩衝區(FILE 結構體內部 _buf,通常 4KB/8KB)。
  • 數據先拷貝到緩衝區,滿了或 fflush/fclose 時一次性 write 進內核。
  • 系統調用次數減少為 文件大小 / 緩衝區大小(10GB / 4KB ≈ 260 萬次 vs 100 億次)。
  • 現代 OS 頁緩存 + 延遲寫回 + 磁盤控制器寫入合併,實現極高吞吐。

3. 更快方案:稀疏文件 / 直接 I/O / 内存映射

// 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);
// 實際寫入數據時才分配物理頁,極適合測試場景

最佳實踐建議

  1. 大塊數據 I/O 必須用 fread/fwrite,禁用 fgetc/fputc 循環。
  2. 調大緩衝區:setvbuf(fp, NULL, _IOFBF, 1024*1024) 設置 1MB 緩衝。
  3. 關鍵路徑考慮 mmap (Linux) / CreateFileMapping (Windows) 零拷貝。
  4. 僅需佔用空間不寫數據:用 fallocate (Linux) / SetEndOfFile (Windows) 秒級創建 TB 級文件。

結語

I/O 性能瓶頸 90% 來自系統調用開銷與緩衝策略不當。理解 FILE* 緩衝機制、頁緩存原理、稀疏文件特性,是寫出高性能存儲代碼的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved