簡體   English   中英

打開MPI在PGM文件中分發和處理2D數組

[英]Open MPI to distributed and manipulate 2d array in PGM files

我需要使用Open MPI在10台工作的計算機之間的PGM文件中分配2d數組。 然后,我需要操縱數組的每個值以獲得負圖像(255-i),然后將輸出打印回去。 我正在考慮使用mpi_scattermpi_gather分發數據。 現在的問題是如何將二維數組讀入子數組並將該子數組發送到每個工作計算機以進行操作。 我正在用C編寫此程序。

誰能幫助我解決這個問題或提出想法? 謝謝。

以下是PGM文件中數組的示例:

P2
# created by 'xv balloons_bw.tif'
640 480
255
232 227 220 216 212 209 207 206 205 205 205 207 208 209 210 211 212 
211 211 213 212 211 210 209 210 210 211 212 211 210 210 210 210 211 
210 210 210 210 209 210 209 208 209 208 209 210 209 208 210 209 209 
208 208 208 209 208 208 208 207 207 207 206 207 207 207 207 207 207 
207 207 207 207 205 204 206 205 205 204 204 204 203 202 203 202 201 
201 201 200 199 199 200 199 198 198 198 197 197 198 197 196 195 195 
194 193 192 192 191 191 190 190 190 190 189 189 190 188 188 188 187 
187 187 186 186 186 186 187 186 186 187 188 188 187 186 186 186 185 
186 186 186 187 186 186 186 185 185 187 186 185 186 185 185 186 185 
184 185 186 185 186 186 186 185 186 185 185 185 184 183 184 184 183

讀取PGM文件的最簡單方法是使用netpbm軟件包中的libpgm

您使用以下方法在pgm文件中讀取:

gray **image;
FILE *fp;
int cols; # num columns
int rows; # num rows
int maxval; # max grayscale value

fp = fopen("input.pgm","r");
image = pgm_readpgm( fp, &cols, &rows, &maxval); 

現在,您可以通過遍歷行/列來獲得負像:

for (i = 0; i < rows; i++)
    for (j = 0; j < cols; j++)
        image[i][j] = maxval - image[i][j];

棘手的一點是,由於image可能在內存中不是連續的(我沒有檢查),因此需要在MPI節點之間分配任務。 可以深入研究代碼以確定存儲模式並相應地分散/聚集陣列,但是並不能保證它將來不會更改(不太可能,但是可能)並破壞您的代碼。

一種可能但非最佳的方法是創建一個臨時緩沖區,該緩沖區在內存中是連續的,進行分配,然后在以后重建圖像。 例如

gray *buffer = malloc(sizeof(gray) * rows * cols);
for (i = 0; i < rows; i++)
    for (j = 0; j < cols; j++)
        buffer[(i*cols)+j] = image[i][j];

現在,我們准備

  1. 跨節點的分散緩沖區
  2. 您可能需要向每個節點廣播maxval
  3. 每個節點都執行buffer[n] = maxval - buffer[n];
  4. 將緩沖區收集回主服務器
  5. 重建輸出圖像

您可以通過將其寫回到image數據來重建圖像,或者如果您熟悉格式 ,則可以簡單地手動打印出pgm文件

至於用於MPI操作的數據類型, MPI_UNSIGNED將起作用,因為grayunsigned int的typedef 然而,要嚴格向前兼容,您可以使用MPI_BYTE和乘以你的send_count通過sizeof(gray)

不使用libpgm

如果您想手動讀取文件,這並不難,因為您的PGM文件為純格式( P2而不是P5 )。

假設格式有效,則需要執行以下操作:

  1. 開啟檔案
  2. 跳過前2行
  3. 讀入列和行: fscanf(fp,"%d %d", &cols, &rows);
  4. 讀入maxval: fscanf(fp,"%d", &maxval);
  5. 分配您可以根據緩存colsrows
  6. 通過在col /行之間循環並重復fscanf(fp,"%d", &buffer[r][c]);讀取其余圖像fscanf(fp,"%d", &buffer[r][c]);

我通常會同意Shawn Chin關於使用現有庫進行文件讀取的觀點。 在這種情況下,我可能會不同意,因為文件格式是如此簡單,並且MPI知道數據如何在內存中布局是如此重要。 分配為連續的一維nxm數組的2d nxm數組與遍布內存的行有很大不同! 與往常一樣,這是C的錯,因為它沒有真正的多維數組。 另一方面,您可以檢出libnetpbm庫並查看其分配方式,或者如Shawn所建議的那樣,在讀入內容后將整個內容復制到連續的內存中。

還要注意,使用(二進制)P5格式實際上會更容易,因為可以使用MPI-IO從一開始就並行讀取數據,而不是讓一個處理器完成所有讀取並使用分散/聚集來做數據分配。 使用ascii文件,您永遠不會真正知道一條記錄將要存儲多長時間,這使得協調I / O變得非常困難。

還要注意,這確實不是2d問題-您只是在數組的每個部分上執行元素操作。 因此,您只需將數據視為一維數組並忽略幾何即可大大簡化事情。 如果(例如)對圖像應用2D濾鏡,情況就不會如此,因為在那里幾何很重要,因此您必須相應地對數據進行分區; 但是在這里我們不在乎。

最后,即使在這種簡單情況下,您也必須使用scatterv和collectv,因為映像中的單元格數量可能無法平均除以MPI任務數量。 您可以通過填充數組以使其均勻划分來簡化邏輯。 那么您可以避免此處的一些額外步驟。

所以,如果你有一個read_pgm()write_pgm()你知道指針返回到一個單獨的內存連續塊,你可以這樣做:

int main(int argc, char **argv) {
    int ierr;
    int rank, size;
    int **greys;
    int rows, cols, maxval;
    int ncells;
    int mystart, myend, myncells;
    const int IONODE=0;
    int *disps, *counts, *mydata;
    int *data;

    ierr = MPI_Init(&argc, &argv);
    if (argc != 3) {
        fprintf(stderr,"Usage: %s infile outfile\n",argv[0]);
        fprintf(stderr,"       outputs the negative of the input file.\n");
        return -1;
    }            

    ierr  = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    ierr |= MPI_Comm_size(MPI_COMM_WORLD, &size);
    if (ierr) {
        fprintf(stderr,"Catastrophic MPI problem; exiting\n");
        MPI_Abort(MPI_COMM_WORLD,1);
    }

    if (rank == IONODE) {
        if (read_pgm(argv[1], &greys, &rows, &cols, &maxval)) {
            fprintf(stderr,"Could not read file; exiting\n");
            MPI_Abort(MPI_COMM_WORLD,2);
        }
        ncells = rows*cols;
        disps = (int *)malloc(size * sizeof(int));
        counts= (int *)malloc(size * sizeof(int));
        data = &(greys[0][0]); /* we know all the data is contiguous */
    }

    /* everyone calculate their number of cells */
    ierr = MPI_Bcast(&ncells, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
    myncells = ncells/size;
    mystart = rank*myncells;
    myend   = mystart + myncells - 1;
    if (rank == size-1) myend = ncells-1;
    myncells = (myend-mystart)+1;
    mydata = (int *)malloc(myncells * sizeof(int));

    /* assemble the list of counts.  Might not be equal if don't divide evenly. */
    ierr = MPI_Gather(&myncells, 1, MPI_INT, counts, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
    if (rank == IONODE) {
        disps[0] = 0;
        for (int i=1; i<size; i++) {
            disps[i] = disps[i-1] + counts[i-1];
        }
    }

    /* scatter the data */
    ierr = MPI_Scatterv(data, counts, disps, MPI_INT, mydata, myncells, 
                        MPI_INT, IONODE, MPI_COMM_WORLD);

    /* everyone has to know maxval */
    ierr = MPI_Bcast(&maxval, 1, MPI_INT, IONODE, MPI_COMM_WORLD);

    for (int i=0; i<myncells; i++)
        mydata[i] = maxval-mydata[i];

    /* Gather the data */
    ierr = MPI_Gatherv(mydata, myncells, MPI_INT, data, counts, disps, 
                        MPI_INT, IONODE, MPI_COMM_WORLD);

    if (rank == IONODE) {
        write_pgm(argv[2], greys, rows, cols, maxval);
    }

    free(mydata);
    if (rank == IONODE) {
        free(counts);
        free(disps);
        free(&(greys[0][0]));
        free(greys);
    }
    MPI_Finalize();
    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM