MPI를 사용하여 C에서 2D 배열 블록 전송
2D 어레이 블록을 다른 프로세서로 전송하려면 어떻게 해야 합니까?2D 어레이 크기가 400x400이라고 가정하면 크기가 100x100인 블록을 다른 프로세서로 보내고 싶습니다.은 각 프로세서가 의 개별 그 를 첫 번째 입니다.
MPI를 사용하다
우선, 일반적으로는, 「마스터」프로세스로부터 대량의 데이터를 수집해 버리는 것은 그다지 하고 싶지 않습니다.통상, 각 태스크는 그 퍼즐의 일부에 집중해, 데이터 전체를 「글로벌 뷰」로 표시할 필요가 없는 것을 목표로 합니다.필요에 따라서, scalability와 문제의 사이즈를 제한할 수 있습니다.I/O를 위해 이 작업을 수행할 경우, 한 프로세스가 데이터를 읽고, 산란시킨 다음, 쓰기 위해 데이터를 다시 수집하면 최종적으로 MPI-IO를 조사하게 됩니다.
단, MPI는 메모리에서 임의의 데이터를 꺼내 프로세서 세트로 분산/수집하는 매우 좋은 방법이 있습니다.유감스럽게도 MPI의 개념은 MPI 유형, 익스텐트 및 집합 운영 등 상당히 많이 필요합니다.MPI_Type_create_subarray와 MPI_Gather라는 질문에 대한 답변에는 많은 기본 아이디어가 논의되어 있습니다.
업데이트 - 냉정하게 볼 때, 이것은 많은 코드이며 설명이 많지 않습니다.조금 더 확대해 보겠습니다.
태스크 0에 포함된 1d 정수 글로벌 어레이를 여러 MPI 태스크에 배포하여 각 태스크가 로컬 어레이에 포함되도록 합니다.작업이 은 4개입니다.[01234567]태스크 0은 4개의 메시지(하나 포함)를 전송하여 이 메시지를 배포할 수 있습니다.재어셈블리가 필요한 경우 4개의 메시지를 수신하여 번들링할 수 있습니다.다만, 대량의 프로세스에서는, 분명히 시간이 걸립니다.이러한 종류의 작업(산포/수집 작업)에 최적화된 루틴이 있습니다. 이 할 수 . 1D는 1D로 하다.
int global[8]; /* only task 0 has this */
int local[2]; /* everyone has this */
const int root = 0; /* the processor with the initial global data */
if (rank == root) {
for (int i=0; i<7; i++) global[i] = i;
}
MPI_Scatter(global, 2, MPI_INT, /* send everyone 2 ints from global */
local, 2, MPI_INT, /* each proc receives 2 ints into local */
root, MPI_COMM_WORLD); /* sending process is root, all procs in */
/* MPI_COMM_WORLD participate */
이 후 프로세서의 데이터는 다음과 같습니다.
task 0: local:[01] global: [01234567]
task 1: local:[23] global: [garbage-]
task 2: local:[45] global: [garbage-]
task 3: local:[67] global: [garbage-]
즉, scatter 조작은 글로벌어레이를 취득하여 연속된2-int 청크를 모든 프로세서에 송신합니다.
어레이를 재구성하려면MPI_Gather()동작은 똑같지만 역방향으로 동작합니다.
for (int i=0; i<2; i++)
local[i] = local[i] + rank;
MPI_Gather(local, 2, MPI_INT, /* everyone sends 2 ints from local */
global, 2, MPI_INT, /* root receives 2 ints each proc into global */
root, MPI_COMM_WORLD); /* recv'ing process is root, all procs in */
/* MPI_COMM_WORLD participate */
그리고 이제 데이터는
task 0: local:[01] global: [0134679a]
task 1: local:[34] global: [garbage-]
task 2: local:[67] global: [garbage-]
task 3: local:[9a] global: [garbage-]
Gather는 모든 데이터를 되돌립니다.여기서 a는 10입니다.이 예를 시작할 때 포맷을 충분히 주의 깊게 하지 않았기 때문입니다.
데이터 포인트의 수가 프로세스 수를 균등하게 나누지 않고 각 프로세스에 다른 수의 항목을 보내야 하는 경우 어떻게 됩니까?그럼 일반화된 버전의 산란이 필요하겠군MPI_Scatterv()각 프로세서의 카운트와 변위량을 지정할 수 있습니다.글로벌 어레이에서 데이터 조각이 시작되는 위치입니다.예를 들어, 여러 가지 캐릭터가 있다고 칩시다.[abcdefghi]모든 프로세스에 2글자를 할당하려고 했는데 마지막 3글자를 제외한 나머지 2글자를 할당하려고 했습니다.그럼 당신은
char global[9]; /* only task 0 has this */
char local[3]={'-','-','-'}; /* everyone has this */
int mynum; /* how many items */
const int root = 0; /* the processor with the initial global data */
if (rank == 0) {
for (int i=0; i<8; i++) global[i] = 'a'+i;
}
int counts[4] = {2,2,2,3}; /* how many pieces of data everyone has */
mynum = counts[rank];
int displs[4] = {0,2,4,6}; /* the starting point of everyone's data */
/* in the global array */
MPI_Scatterv(global, counts, displs, /* proc i gets counts[i] pts from displs[i] */
MPI_INT,
local, mynum, MPI_INT; /* I'm receiving mynum MPI_INTs into local */
root, MPI_COMM_WORLD);
이제 데이터는
task 0: local:[ab-] global: [abcdefghi]
task 1: local:[cd-] global: [garbage--]
task 2: local:[ef-] global: [garbage--]
task 3: local:[ghi] global: [garbage--]
이제 scatv를 사용하여 불규칙한 양의 데이터를 배포했습니다.각 경우의 변위는 배열 시작부터 2*랭크(문자 단위로 측정됩니다. 변위는 산란 또는 수집용으로 수신된 유형의 단위입니다. 일반적으로 바이트 단위 등은 아닙니다.)이며 카운트는 {2,2,3,}입니다.3글자를 원하는 첫 번째 프로세서라면 counts={3,2,2}로 설정했을 것이고, 변위는 {0,3,5,7}이 되었을 것입니다.Gatherv는 다시 동일하게 동작하지만, 반대로 동작합니다.수 및 디스플레이 어레이는 동일하게 유지됩니다.
2D에서는 좀 더 까다롭습니다.2D 어레이의 2D 서브록을 송신하는 경우, 송신하는 데이터는 더 이상 연속되지 않습니다.6x6 어레이의 3x3 서브블록을 4개의 프로세서에 송신(예를 들어)하는 경우, 송신하는 데이터에 구멍이 있습니다.
2D Array
---------
|000|111|
|000|111|
|000|111|
|---+---|
|222|333|
|222|333|
|222|333|
---------
Actual layout in memory
[000111000111000111222333222333222333]
(모든 고성능 컴퓨팅은 메모리의 데이터 레이아웃을 이해하는 것으로 귀결됩니다.)
작업 1에 "1"로 표시된 데이터를 전송하려면 세 개의 값을 건너뛰고, 세 개의 값을 보내고, 세 개의 값을 건너뛰고, 세 개의 값을 보내고, 세 개의 값을 건너뛰고, 세 개의 값을 보내야 합니다.두 번째 문제는 서브영역이 정지하고 시작되는 곳입니다.영역 "1"은 영역 "0"이 정지하는 곳에서 시작되지 않습니다.영역 "0"의 마지막 요소 뒤에 메모리 내의 다음 위치는 영역 "1"을 통과하는 중간 지점입니다.
우선 첫 번째 레이아웃 문제인 송신하는 데이터만을 추출하는 방법에 대해 설명하겠습니다.에 "수 하면 "0" 지역 데이터를 "0"으로 수 .또한 신중하게 계획하면 콜을 할 수 있는 방법으로도 할 수 있습니다.MPI_Scatter결과를 보고합니다.그러나 전체 주요 데이터 구조를 그런 식으로 바꿀 필요는 없습니다.
지금까지 사용한 MPI 데이터 타입은 모두 단순한 것입니다.MPI_INT는 4바이트를 연속해서 지정합니다.그러나 MPI를 사용하면 메모리에서 임의로 복잡한 데이터 레이아웃을 설명하는 고유한 데이터 유형을 생성할 수 있습니다.이 경우 - 어레이의 직사각형 하위 영역 - 특정 요구가 있을 정도로 일반적입니다.위에서 설명한 2차원 케이스는
MPI_Datatype newtype;
int sizes[2] = {6,6}; /* size of global array */
int subsizes[2] = {3,3}; /* size of sub-region */
int starts[2] = {0,0}; /* let's say we're looking at region "0",
which begins at index [0,0] */
MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &newtype);
MPI_Type_commit(&newtype);
이것에 의해, 글로벌 어레이로부터 영역 「0」만을 선택하는 타입이 작성됩니다.그 데이터만을 다른 프로세서로 송신할 수 있습니다.
MPI_Send(&(global[0][0]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "0" */
수신 프로세스가 로컬 어레이로 수신할 수 있습니다.수신 프로세스가 3x3 어레이로만 수신되는 경우 수신 프로세스가 수신되는 대상을newtype; 더 이상 메모리 레이아웃을 설명하지 않습니다.=의 정수 아, 3*3 = 9개의 을 받는 입니다.
MPI_Recv(&(local[0][0]), 3*3, MPI_INT, 0, tag, MPI_COMM_WORLD);
수 .start다른 블록에 대해서는, 또는 특정 블록의 시작점에 송신하는 것만으로, 다음의 처리를 실시합니다.
MPI_Send(&(global[0][3]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "1" */
MPI_Send(&(global[3][0]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "2" */
MPI_Send(&(global[3][3]), 1, newtype, dest, tag, MPI_COMM_WORLD); /* region "3" */
및 되어 「」는 「」입니다.&(global[0][0]) ★★★★★★★★★★★★★★★★★」&(local[0][0]) *global ★★★★★★★★★★★★★★★★★」*local6*6 및 3*3 。이는 다이내믹 멀티D 어레이를 할당하는 일반적인 방법으로는 보증되지 않습니다.이 방법은 다음과 같습니다.
이제 하위 영역을 지정하는 방법을 이해했습니다. 산포/수집 작업을 사용하기 전에 검토해야 할 사항은 하나뿐입니다. 을 사용하다'냥쓸 use use use use use use를 사용할 수 .MPI_Scatter()(또는 scatv)는 아직 이러한 유형의 데이터 전송은 16개의 정수의 범위를 가지기 때문에 시작 후 16개의 정수로 끝납니다. 즉, 다음 블록이 시작되는 부분과 제대로 정렬되지 않기 때문에 산포만 사용할 수 없습니다. 데이터 전송을 다음 프로세서로 시작하기 위해 잘못된 위치를 선택할 수 있습니다.
론, 리, 우, 우, 우, 있, 있, 있, 다, of, 다, of, 다, of, of, of, of, ,MPI_Scatterv()직접 변위를 지정합니다.변위가 송신형 크기 단위라는 점만 제외하면 이 작업에는 도움이 되지 않습니다.multi록 、 [ 0 , 3, 18 , 21 ]정수 배수로 시멘트를 합니다.
이 문제를 해결하기 위해 MPI를 사용하면 이러한 계산을 위해 유형의 범위를 설정할 수 있습니다.유형을 잘라내는 것이 아니라 마지막 요소가 주어진 다음 요소의 시작 위치를 파악하는 데 사용됩니다.이러한 구멍이 있는 타입의 경우는, 메모리내의 거리보다 작은 범위로 설정하는 것이 편리합니다.
우리에게 편리한 범위라면 무엇이든 설정할 수 있습니다.익스텐트 1을 정수로 한 다음 정수 단위로 변위를 설정할 수 있습니다.그러나 이 경우 범위를 3개의 정수(하위 행 크기)로 설정하고 블록 "1"은 블록 "0" 바로 뒤에 시작하고 블록 "3"은 블록 "2" 바로 뒤에 시작합니다.안타깝게도 블록 2에서 블록 3으로 점프할 때처럼 잘 작동하지 않지만 어쩔 수 없습니다.
이 경우 서브블록을 분산시키려면 다음 작업을 수행합니다.
MPI_Datatype type, resizedtype;
int sizes[2] = {6,6}; /* size of global array */
int subsizes[2] = {3,3}; /* size of sub-region */
int starts[2] = {0,0}; /* let's say we're looking at region "0",
which begins at index [0,0] */
/* as before */
MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &type);
/* change the extent of the type */
MPI_Type_create_resized(type, 0, 3*sizeof(int), &resizedtype);
MPI_Type_commit(&resizedtype);
여기서는 이전과 동일한 블록 유형을 생성했지만 크기를 조정했습니다. 유형 "시작"(0)은 변경하지 않았지만 "끝"(3ints)은 변경했습니다.얘기 안 , ''가 '우리'가 '우리 '우리'가 아니라 '우리'가 '우리'가 ''가MPI_Type_commit는 유형을 사용할 수 있어야 합니다.단, 실제로 사용하는 최종 타입만 커밋하면 됩니다.중간 단계는 커밋하지 않습니다.은 you 다 you you youMPI_Type_free쓰
마지막으로 블록을 분산할 수 있습니다.상기의 데이터 조작은 조금 복잡하지만 처리가 완료되면 이전과 같은 상태가 됩니다.
int counts[4] = {1,1,1,1}; /* how many pieces of data everyone has, in units of blocks */
int displs[4] = {0,1,6,7}; /* the starting point of everyone's data */
/* in the global array, in block extents */
MPI_Scatterv(global, counts, displs, /* proc i gets counts[i] types from displs[i] */
resizedtype,
local, 3*3, MPI_INT; /* I'm receiving 3*3 MPI_INTs into local */
root, MPI_COMM_WORLD);
이것으로 산란형, 개더형, MPI유래형 등을 조금 둘러보고 완료했습니다.
문자 배열이 있는 수집 및 분산 작업을 모두 보여주는 코드 예를 다음에 나타냅니다.프로그램 실행:
$ mpirun -n 4 ./gathervarray
Global array is:
0123456789
3456789012
6789012345
9012345678
2345678901
5678901234
8901234567
1234567890
4567890123
7890123456
Local process on rank 0 is:
|01234|
|34567|
|67890|
|90123|
|23456|
Local process on rank 1 is:
|56789|
|89012|
|12345|
|45678|
|78901|
Local process on rank 2 is:
|56789|
|89012|
|12345|
|45678|
|78901|
Local process on rank 3 is:
|01234|
|34567|
|67890|
|90123|
|23456|
Processed grid:
AAAAABBBBB
AAAAABBBBB
AAAAABBBBB
AAAAABBBBB
AAAAABBBBB
CCCCCDDDDD
CCCCCDDDDD
CCCCCDDDDD
CCCCCDDDDD
CCCCCDDDDD
코드가 따라옵니다.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "mpi.h"
int malloc2dchar(char ***array, int n, int m) {
/* allocate the n*m contiguous items */
char *p = (char *)malloc(n*m*sizeof(char));
if (!p) return -1;
/* allocate the row pointers into the memory */
(*array) = (char **)malloc(n*sizeof(char*));
if (!(*array)) {
free(p);
return -1;
}
/* set up the pointers into the contiguous memory */
for (int i=0; i<n; i++)
(*array)[i] = &(p[i*m]);
return 0;
}
int free2dchar(char ***array) {
/* free the memory - the first element of the array is at the start */
free(&((*array)[0][0]));
/* free the pointers into the memory */
free(*array);
return 0;
}
int main(int argc, char **argv) {
char **global, **local;
const int gridsize=10; // size of grid
const int procgridsize=2; // size of process grid
int rank, size; // rank of current process and no. of processes
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (size != procgridsize*procgridsize) {
fprintf(stderr,"%s: Only works with np=%d for now\n", argv[0], procgridsize);
MPI_Abort(MPI_COMM_WORLD,1);
}
if (rank == 0) {
/* fill in the array, and print it */
malloc2dchar(&global, gridsize, gridsize);
for (int i=0; i<gridsize; i++) {
for (int j=0; j<gridsize; j++)
global[i][j] = '0'+(3*i+j)%10;
}
printf("Global array is:\n");
for (int i=0; i<gridsize; i++) {
for (int j=0; j<gridsize; j++)
putchar(global[i][j]);
printf("\n");
}
}
/* create the local array which we'll process */
malloc2dchar(&local, gridsize/procgridsize, gridsize/procgridsize);
/* create a datatype to describe the subarrays of the global array */
int sizes[2] = {gridsize, gridsize}; /* global size */
int subsizes[2] = {gridsize/procgridsize, gridsize/procgridsize}; /* local size */
int starts[2] = {0,0}; /* where this one starts */
MPI_Datatype type, subarrtype;
MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_CHAR, &type);
MPI_Type_create_resized(type, 0, gridsize/procgridsize*sizeof(char), &subarrtype);
MPI_Type_commit(&subarrtype);
char *globalptr=NULL;
if (rank == 0) globalptr = &(global[0][0]);
/* scatter the array to all processors */
int sendcounts[procgridsize*procgridsize];
int displs[procgridsize*procgridsize];
if (rank == 0) {
for (int i=0; i<procgridsize*procgridsize; i++) sendcounts[i] = 1;
int disp = 0;
for (int i=0; i<procgridsize; i++) {
for (int j=0; j<procgridsize; j++) {
displs[i*procgridsize+j] = disp;
disp += 1;
}
disp += ((gridsize/procgridsize)-1)*procgridsize;
}
}
MPI_Scatterv(globalptr, sendcounts, displs, subarrtype, &(local[0][0]),
gridsize*gridsize/(procgridsize*procgridsize), MPI_CHAR,
0, MPI_COMM_WORLD);
/* now all processors print their local data: */
for (int p=0; p<size; p++) {
if (rank == p) {
printf("Local process on rank %d is:\n", rank);
for (int i=0; i<gridsize/procgridsize; i++) {
putchar('|');
for (int j=0; j<gridsize/procgridsize; j++) {
putchar(local[i][j]);
}
printf("|\n");
}
}
MPI_Barrier(MPI_COMM_WORLD);
}
/* now each processor has its local array, and can process it */
for (int i=0; i<gridsize/procgridsize; i++) {
for (int j=0; j<gridsize/procgridsize; j++) {
local[i][j] = 'A' + rank;
}
}
/* it all goes back to process 0 */
MPI_Gatherv(&(local[0][0]), gridsize*gridsize/(procgridsize*procgridsize), MPI_CHAR,
globalptr, sendcounts, displs, subarrtype,
0, MPI_COMM_WORLD);
/* don't need the local data anymore */
free2dchar(&local);
/* or the MPI data type */
MPI_Type_free(&subarrtype);
if (rank == 0) {
printf("Processed grid:\n");
for (int i=0; i<gridsize; i++) {
for (int j=0; j<gridsize; j++) {
putchar(global[i][j]);
}
printf("\n");
}
free2dchar(&global);
}
MPI_Finalize();
return 0;
}
그런 식으로 확인하는 게 더 쉬웠을 뿐이에요.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "mpi.h"
/*
This is a version with integers, rather than char arrays, presented in this
very good answer: http://stackoverflow.com/a/9271753/2411320
It will initialize the 2D array, scatter it, increase every value by 1 and then gather it back.
*/
int malloc2D(int ***array, int n, int m) {
int i;
/* allocate the n*m contiguous items */
int *p = malloc(n*m*sizeof(int));
if (!p) return -1;
/* allocate the row pointers into the memory */
(*array) = malloc(n*sizeof(int*));
if (!(*array)) {
free(p);
return -1;
}
/* set up the pointers into the contiguous memory */
for (i=0; i<n; i++)
(*array)[i] = &(p[i*m]);
return 0;
}
int free2D(int ***array) {
/* free the memory - the first element of the array is at the start */
free(&((*array)[0][0]));
/* free the pointers into the memory */
free(*array);
return 0;
}
int main(int argc, char **argv) {
int **global, **local;
const int gridsize=4; // size of grid
const int procgridsize=2; // size of process grid
int rank, size; // rank of current process and no. of processes
int i, j, p;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (size != procgridsize*procgridsize) {
fprintf(stderr,"%s: Only works with np=%d for now\n", argv[0], procgridsize);
MPI_Abort(MPI_COMM_WORLD,1);
}
if (rank == 0) {
/* fill in the array, and print it */
malloc2D(&global, gridsize, gridsize);
int counter = 0;
for (i=0; i<gridsize; i++) {
for (j=0; j<gridsize; j++)
global[i][j] = ++counter;
}
printf("Global array is:\n");
for (i=0; i<gridsize; i++) {
for (j=0; j<gridsize; j++) {
printf("%2d ", global[i][j]);
}
printf("\n");
}
}
//return;
/* create the local array which we'll process */
malloc2D(&local, gridsize/procgridsize, gridsize/procgridsize);
/* create a datatype to describe the subarrays of the global array */
int sizes[2] = {gridsize, gridsize}; /* global size */
int subsizes[2] = {gridsize/procgridsize, gridsize/procgridsize}; /* local size */
int starts[2] = {0,0}; /* where this one starts */
MPI_Datatype type, subarrtype;
MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &type);
MPI_Type_create_resized(type, 0, gridsize/procgridsize*sizeof(int), &subarrtype);
MPI_Type_commit(&subarrtype);
int *globalptr=NULL;
if (rank == 0)
globalptr = &(global[0][0]);
/* scatter the array to all processors */
int sendcounts[procgridsize*procgridsize];
int displs[procgridsize*procgridsize];
if (rank == 0) {
for (i=0; i<procgridsize*procgridsize; i++)
sendcounts[i] = 1;
int disp = 0;
for (i=0; i<procgridsize; i++) {
for (j=0; j<procgridsize; j++) {
displs[i*procgridsize+j] = disp;
disp += 1;
}
disp += ((gridsize/procgridsize)-1)*procgridsize;
}
}
MPI_Scatterv(globalptr, sendcounts, displs, subarrtype, &(local[0][0]),
gridsize*gridsize/(procgridsize*procgridsize), MPI_INT,
0, MPI_COMM_WORLD);
/* now all processors print their local data: */
for (p=0; p<size; p++) {
if (rank == p) {
printf("Local process on rank %d is:\n", rank);
for (i=0; i<gridsize/procgridsize; i++) {
putchar('|');
for (j=0; j<gridsize/procgridsize; j++) {
printf("%2d ", local[i][j]);
}
printf("|\n");
}
}
MPI_Barrier(MPI_COMM_WORLD);
}
/* now each processor has its local array, and can process it */
for (i=0; i<gridsize/procgridsize; i++) {
for (j=0; j<gridsize/procgridsize; j++) {
local[i][j] += 1; // increase by one the value
}
}
/* it all goes back to process 0 */
MPI_Gatherv(&(local[0][0]), gridsize*gridsize/(procgridsize*procgridsize), MPI_INT,
globalptr, sendcounts, displs, subarrtype,
0, MPI_COMM_WORLD);
/* don't need the local data anymore */
free2D(&local);
/* or the MPI data type */
MPI_Type_free(&subarrtype);
if (rank == 0) {
printf("Processed grid:\n");
for (i=0; i<gridsize; i++) {
for (j=0; j<gridsize; j++) {
printf("%2d ", global[i][j]);
}
printf("\n");
}
free2D(&global);
}
MPI_Finalize();
return 0;
}
출력:
linux16:>mpicc -o main main.c
linux16:>mpiexec -n 4 main Global array is:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
Local process on rank 0 is:
| 1 2 |
| 5 6 |
Local process on rank 1 is:
| 3 4 |
| 7 8 |
Local process on rank 2 is:
| 9 10 |
|13 14 |
Local process on rank 3 is:
|11 12 |
|15 16 |
Processed grid:
2 3 4 5
6 7 8 9
10 11 12 13
14 15 16 17
언급URL : https://stackoverflow.com/questions/9269399/sending-blocks-of-2d-array-in-c-using-mpi
'source' 카테고리의 다른 글
| VeeValidate에서 오류 메시지(필드 이름)를 커스터마이즈하는 방법 (0) | 2022.08.15 |
|---|---|
| VueJs 동적 v-on 이벤트가 가능합니까? (0) | 2022.08.15 |
| JSF, Servlet 및 JSP의 차이점은 무엇입니까? (0) | 2022.08.15 |
| 단일 파일이 작동하지 않는 경우의 EsLint 비활성화 (0) | 2022.08.15 |
| Vue를 'vue'에서 Import하면 '다른' Vue를 다른 파일로 Import합니다. (0) | 2022.08.15 |