MPI-1 僅支持串行IO(0號進程讀寫再廣播),數據通信量大、擴展性差。MPI-2 引入並行IO,允許所有進程直接並發訪問文件,消除數據再分發開銷。本文系統講解三大訪問模式(顯式偏移/獨立文件指針/共享文件指針)、阻塞與非阻塞 API、二進制數組文件讀寫完整案例、文本↔二進制轉換工具,適合高性能計算開發者快速上手。
| 模式 | 核心API | 指針管理 | 適用場景 | 同步特性 |
|---|---|---|---|---|
| 顯式偏移 | MPI_File_read_at_all MPI_File_write_at_all | 每次調用顯式指定偏移量 | 固定佈局、隨機訪問、校驗點寫入 | 集體操作,隱式屏障同步 |
| 獨立文件指針 | MPI_File_set_view + MPI_File_read_all MPI_File_write_all | 每進程維護私有文件指針 | 流式讀寫、日誌、順序數據 | 集體操作,指針獨立推進 |
| 共享文件指針 | MPI_File_seek_shared + MPI_File_read_ordered MPI_File_write_ordered | 所有進程共享單一指針 | 順序寫入單一文件、協調輸出 | 集體操作,按秩有序訪問 |
#define BLOCK_LOW(rank, size, n) ((rank)*(n)/(size)) #define BLOCK_HIGH(rank, size, n) (BLOCK_LOW((rank)+1,size,n)-1) #define BLOCK_SIZE(rank, size, n) (BLOCK_HIGH(rank,size,n)-BLOCK_LOW(rank,size,n)+1) // 進程 rank 負責第 [BLOCK_LOW, BLOCK_HIGH] 行,共 BLOCK_SIZE 行
[行數:int][列數:int][數據:float[row][col] 行主序]
int main(int argc, char** argv) {
MPI_Init(&argc, &argv);
int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_File fh; MPI_Status st;
int n, m; float* local_data;
// 1. 打開文件
MPI_File_open(MPI_COMM_WORLD, "data", MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);
// 2. 所有進程讀取行列數(偏移0和sizeof(int))
MPI_File_read_at_all(fh, 0, &n, 1, MPI_INT, &st);
MPI_File_read_at_all(fh, sizeof(int), &m, 1, MPI_INT, &st);
// 3. 分配局部緩衝並讀取數據
int local_rows = BLOCK_SIZE(rank, size, n);
local_data = malloc(local_rows * m * sizeof(float));
MPI_Offset offset = 2*sizeof(int) + BLOCK_LOW(rank,size,n)*m*sizeof(float);
MPI_File_read_at_all(fh, offset, local_data, local_rows*m, MPI_FLOAT, &st);
MPI_File_close(&fh);
// 4. 驗證輸出(進程0)
if (rank == 0) { /* 打印局部數據 */ }
// 5. 寫入新文件(同理用 MPI_File_write_at_all)
MPI_File_open(MPI_COMM_WORLD, "data2", MPI_MODE_CREATE|MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
MPI_File_write_at_all(fh, 0, &n, 1, MPI_INT, &st);
MPI_File_write_at_all(fh, sizeof(int), &m, 1, MPI_INT, &st);
MPI_File_write_at_all(fh, offset, local_data, local_rows*m, MPI_FLOAT, &st);
MPI_File_close(&fh);
MPI_Finalize(); return 0;
}
// 關鍵差異:用 set_view 設置起始視圖,read_all/write_all 自動推進指針 MPI_File_set_view(fh, 0, MPI_INT, MPI_INT, "native", MPI_INFO_NULL); MPI_File_read_all(fh, &n, 1, MPI_INT, &st); MPI_File_read_all(fh, &m, 1, MPI_INT, &st); // 重置視圖到數據區 MPI_Offset data_offset = 2*sizeof(int) + BLOCK_LOW(rank,size,n)*m*sizeof(float); MPI_File_set_view(fh, data_offset, MPI_FLOAT, MPI_FLOAT, "native", MPI_INFO_NULL); MPI_File_read_all(fh, local_data, local_rows*m, MPI_FLOAT, &st);
// 關鍵差異:共享指針 + ordered 操作(按 rank 順序訪問) MPI_File_read_at_all(fh, 0, &n, 1, MPI_INT, &st); // 讀頭部仍用顯式偏移 MPI_File_read_at_all(fh, sizeof(int), &m, 1, MPI_INT, &st); MPI_File_seek_shared(fh, 2*sizeof(int), MPI_SEEK_SET); // 共享指針定位 MPI_File_read_ordered(fh, local_data, local_rows*m, MPI_FLOAT, &st); // 按秩順序讀 // 寫入同理 MPI_File_write_at_all(fh, 0, &n, 1, MPI_INT, &st); MPI_File_write_at_all(fh, sizeof(int), &m, 1, MPI_INT, &st); MPI_File_seek_shared(fh, 2*sizeof(int), MPI_SEEK_SET); MPI_File_write_ordered(fh, local_data, local_rows*m, MPI_FLOAT, &st);
MPI_Request req; MPI_File_iread_at(fh, offset, buf, count, datatype, &req); // 發起異步讀 // ... 進行計算 ... MPI_Wait(&req, &st); // 等待完成 // 同理:MPI_File_iwrite_at, MPI_File_iread_all, MPI_File_iwrite_all
FILE* fp = fopen("data", "rb");
fread(&n, sizeof(int), 1, fp);
fread(&m, sizeof(int), 1, fp);
float** a = malloc(n*sizeof(float*));
a[0] = malloc(n*m*sizeof(float));
for (int i=1; i
文本 → 二進制
FILE* fp = fopen("data.txt", "r");
fscanf(fp, "%d%d", &n, &m);
// 分配同上
for (int i=0; i
樣例文本文件 data.txt
8 8
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0
4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0
5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0
6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0
7.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0
8.0 8.0 8.0 8.0 8.0 8.0 8.0 8.0
最佳實踐總結
- 優先選擇顯式偏移:語義清晰、無隱式狀態、易於調試、性能最優
- 集體操作(_all)必須全進程參與:否則死鎖
- 文件視圖設置一次:避免重複
set_view 開銷
- 非連續內存用派生數據類型:
MPI_Type_vector / MPI_Type_hvector 描述子數組佈局
- 錯誤檢查:每個 MPI_File_* 調用檢查返回碼,
MPI_Error_string 獲取信息
- 原子性寫入:關鍵數據用
MPI_File_sync 強制刷盤
總結
MPI-2 並行IO 通過 顯式偏移、獨立指針、共享指針 三種模式覆蓋所有訪問模式。二維數組塊分佈配合 MPI_File_read_at_all/write_at_all 是最高效、最常用的模式。掌握本文案例代碼、數據佈局宏、二進制驗證工具,即可在 HPC 應用中實現高性能、可擴展的並行文件讀寫,徹底擺脫 MPI-1 串行IO 瓶頸。