การใช้ MPI_Irecv และ MPI_Isend ใน for loop

ฉันมีปัญหากับ MPI_Isend และ MPI_Irecv ฉันกำลังทำงานกับเมทริกซ์ adjacency ของกราฟ ซึ่งมีการกระจายตามแถว เราสามารถถือว่าโปรเซสเซอร์แต่ละตัวมีหนึ่งแถว สำหรับดัชนีแต่ละคู่ (i,j) ฉันต้องส่งและรับจำนวนเต็ม 2 ตัว โดยพื้นฐานแล้ว ฉันจำเป็นต้องได้รับข้อมูลอื่นจากแถวอื่นเพื่อที่จะทำการคำนวณ ฉันเป็นคนใหม่ใน MPI และที่นี่มันจะเข้าสู่วงวนไม่สิ้นสุด ฉันไม่แน่ใจว่ามันเป็นวิธีที่ถูกต้องในการใช้ MPI_Isend หรือ MPI_Irecvin a for loop รวมถึงเป็นสถานที่ในการรอด้วย

ตามตัวอย่าง สมมติว่าเรามีกราฟที่มีจุดยอด 6 จุด ดังนั้นเมทริกซ์ adjacency (adjMatrix) จะเป็นเมทริกซ์ขนาด 6*6 เราก็มีเมทริกซ์ขนาด 6*2 สำหรับข้อมูลอื่นๆ ด้วย และสุดท้าย เราก็กระจาย ข้อมูลระหว่างโปรเซสเซอร์ 6 ตัว ดังนั้น:

          |0  20 16 0  6  0 |      |0  1|
          |20 0  0  19 0  6 |      |1  1|
addMatrix=|16 0  0  0  12 0 |    M=|2  1|
          |0  19 0  0  0  12|      |3  1|
          |6  0  12 0  0  9 |      |0  0|
          |0  6  0  12 9  0 |      |1  0|

เรากระจายเมทริกซ์ดังนี้:

P0:       |0  20 16 0  6  0 |      |0  1|

P1:       |20 0  0  19 0  6 |      |1  1|

P2:       |16 0  0  0  12 0 |      |2  1|

P3:       |0  19 0  0  0  12|      |3  1|

P4:       |6  0  12 0  0  9 |      |0  0|

P5:       |0  6  0  12 9  0 |      |1  0|

ขณะนี้ โปรเซสเซอร์แต่ละตัวจำเป็นต้องอัปเดตส่วนของ adjMatrix ในการทำเช่นนั้น พวกเขาต้องการข้อมูลจากบางส่วนของเมทริกซ์ M ซึ่งอยู่ในโปรเซสเซอร์อื่นๆ ตัวอย่างเช่น ในการ P0 อัปเดตดัชนี (0,1) ซึ่งก็คือ 20 จะต้องมีสิทธิ์เข้าถึงแถว 1 ของเมทริกซ์ M ซึ่งก็คือ {1,1} ดังนั้น:

P1 ควรส่ง MLocal[0][0]=1 และ MLocal[0][1]=1 ไปที่ P0 โดยที่ P0 ได้รับเป็น M_j0 และ M_j1 ตามลำดับ

และ

P0 ควรส่ง MLocal[0][0]=0 และ MLocal[0][1]=1 ไปที่ P1 โดยที่ P1 ได้รับเป็น M_j0 และ M_j1 ตามลำดับ

    for(int i=0;i<rows;i++){
            for (int j=0; j<n; j++)
            {
                int M_j0,M_j1;
                MPI_Isend(&MLocal[i][0], 1, MPI_INT, j, my_rank+i*n+j+0, MPI_COMM_WORLD, &send_request0);
                MPI_Isend(&MLocal[i][1], 1, MPI_INT, j, my_rank+i*n+j+1, MPI_COMM_WORLD, &send_request1);
                MPI_Irecv(&M_j0, 1, MPI_INT, j, my_rank+i*n+j+0, MPI_COMM_WORLD, &recv_request0);
                MPI_Irecv(&M_j1, 1, MPI_INT, j, my_rank+i*n+j+1, MPI_COMM_WORLD, &recv_request1);
                //MPI_Wait(&send_request0, &status);
                //MPI_Wait(&send_request1, &status);
                MPI_Wait(&recv_request0, &status);
                MPI_Wait(&recv_request1, &status);

                 // Do something ...
            }
        }

จากนั้นด้วยคำแนะนำของ GillesGouaillardet ฉันเปลี่ยน 4 MPI_Isend และ MPI_Irecv เป็น:

    MPI_Sendrecv(&MoatsLocal[i][0], 1, MPI_INT, j, my_rank+i*n+j+0, &M_j0,1, MPI_INT, my_rank, my_rank+i*n+j+0, MPI_COMM_WORLD, &status);
    MPI_Sendrecv(&MoatsLocal[i][1], 1, MPI_INT, j, my_rank+i*n+j+1, &M_j1,1, MPI_INT, my_rank, my_rank+i*n+j+1, MPI_COMM_WORLD, &status);

แต่กระนั้น มันก็เข้าสู่วงวนไม่สิ้นสุด

อัปเดต:

ฉันอัปเดตโค้ดแล้ว ส่วนหนึ่งของปัญหาเกิดจากการจัดอันดับโปรเซสเซอร์และการจับคู่แท็ก ฉันแก้ไขส่วนนั้นแล้ว แต่ถึงกระนั้น มันก็มีแนวโน้มที่จะเกิดการหยุดชะงัก ซึ่งฉันคิดว่าฉันรู้ว่าปัญหาอยู่ที่ไหน และอาจแก้ไม่ได้ หากฉันมีตัวประมวลผลเพียงพอ การกระจายแต่ละบรรทัดไปยังตัวประมวลผล เช่น n=p ก็คงไม่เป็นปัญหาใดๆ แต่ปัญหาคือจำนวนโปรเซสเซอร์น้อยกว่า n ดังนั้นโฟลว์จะไม่ผ่านเส้นทแยงมุมหลักอย่างสวยงาม ผมอธิบายผ่านตัวอย่าง สมมติว่าเรามีโปรเซสเซอร์ 4 ตัวและ n=6 สมมติว่านี่คือการกระจาย:

P0:       |0  20 16 0  6  0 |      |0  1|

P1:       |20 0  0  19 0  6 |      |1  1|
          |16 0  0  0  12 0 |      |2  1|

P2:       |0  19 0  0  0  12|      |3  1|

P3:       |6  0  12 0  0  9 |      |0  0|
          |0  6  0  12 9  0 |      |1  0|

นี่คือสิ่งที่เกิดขึ้นในวง

การทำซ้ำครั้งแรก:

P0 ส่งและรับข้อมูล P1 สำหรับ (0,1): "20" และรอ (เสร็จสิ้น)

P1 ส่งและรับข้อมูล P0 สำหรับ (1,0): "20" และรอ (เสร็จสิ้น)

P2 ส่งและรับข้อมูล P1 สำหรับ (3,1): "19" แล้วรอ

P3 ส่งและรับข้อมูล P0 สำหรับ (4,1):"6" และรอ

การทำซ้ำครั้งที่สอง:

P0 ส่งและรับข้อมูล P1 สำหรับ (0,2): "16" แล้วรอ

P1 ส่งและรับข้อมูล P2 สำหรับ (1,3): "19" และรอ (เสร็จสิ้น)

P2 กำลังรอ P1 (3,1):"19" จากนั้นก็รับและทำเสร็จแล้ว!

P3 กำลังรอ P0 สำหรับ (4,1):"6" และรอ

การทำซ้ำครั้งที่สาม:

P0 กำลังรอ P1 สำหรับ (0,2):"16"

P1 ส่งและรับข้อมูล P3 ไปยัง (1,5): "19" แล้วรอ

P2 ส่งและรับข้อมูล P3 ไปยัง (3,5): "12" แล้วรอ

P3 กำลังรอ P0 สำหรับ (4,1):"6"

การทำซ้ำครั้งที่สี่:

P0 กำลังรอ P1 สำหรับ (0,2):"16"

P1 กำลังรอ P3 สำหรับ (1,5): "19"

P2 กำลังรอ P3 สำหรับ (3,5):"12"

P3 กำลังรอ P0 สำหรับ (4,1):"6"

ตอนนี้ทุกคนรอกันอยู่ไม่คิดว่าจะมีวิธีแก้ไขอะไร วิธีแก้ปัญหาที่ ptb แนะนำอาจใช้ได้ ฉันจะลองใช้วิธีนั้น

อย่างไรก็ตาม เรายังชื่นชมแนวคิดอื่นๆ อีกด้วย!


person Sarah    schedule 04.04.2018    source แหล่งที่มา
comment
คุณสามารถใช้อาร์เรย์ 4 คำขอ จากนั้น MPI_Waitall(4, ...);   -  person Gilles Gouaillardet    schedule 04.04.2018
comment
คุณอาจใช้ MPI_Sendrecv() สองอันก็ได้   -  person Gilles Gouaillardet    schedule 04.04.2018
comment
@GillesGouaillardet ฉันทำตามที่คุณพูดถึง ยังคงมีปัญหาเดียวกัน   -  person Sarah    schedule 04.04.2018
comment
โปรดอัปโหลดตัวอย่างที่ทำซ้ำได้น้อยที่สุด   -  person Gilles Gouaillardet    schedule 04.04.2018
comment
@GillesGouaillardet ฉันอัปเดตแล้ว ความช่วยเหลือใด ๆ ที่ชื่นชม   -  person Sarah    schedule 04.04.2018
comment
นั่นไม่ใช่ ไม่ใช่ ตัวอย่างที่ทำซ้ำได้น้อยที่สุด   -  person Gilles Gouaillardet    schedule 05.04.2018
comment
คุณได้ลองด้วย MPI_Waitall(4, ...) หรือยัง? นั่นน่าจะมีโอกาสเกิดการหยุดชะงักน้อยลง   -  person Gilles Gouaillardet    schedule 05.04.2018
comment
@GillesGouaillardet ใช่นี่คือวิธีที่ฉันกำลังทำอยู่ตอนนี้ ฉันพบว่าปัญหาที่ทำให้เกิดการหยุดชะงักเกิดขึ้นที่ใดซึ่งฉันคิดว่าไม่มีวิธีแก้ไข ฉันอัปเดตคำถามของฉันแล้ว   -  person Sarah    schedule 05.04.2018


คำตอบ (2)


โค้ดที่คุณโพสต์มีปัญหาบางประการ

  1. โปรเซสเซอร์แต่ละตัวจะวนซ้ำผ่าน rows อย่างไรก็ตาม ในคำอธิบายของคุณ แถวต่างๆ จะกระจายไปตามโปรเซสเซอร์ ดังนั้นนี่อาจเป็นข้อผิดพลาด
  2. ปลายทางการส่งและรับและแหล่งที่มาเหมือนกัน ดังนั้นหากคุณพิจารณากรณีที่ j=0, MPI_Isend(...,j,...) หมายความว่าทุกอันดับจะส่งบางสิ่งไปยังกระบวนการรูท ตามด้วยการเรียก MPI_IRecv(...,j,...), MPI_Wait ซึ่งหมายความว่าทุกกระบวนการจะรอการส่งจากกระบวนการรูทที่ไม่เคยมา
  3. การเรียก MPI_SendRecv มีปัญหาพื้นฐานเดียวกัน

ความท้าทายคือคุณต้องส่งและรับสายเพื่อจับคู่กัน วิธีหนึ่งในการทำเช่นนั้น (ไม่จำเป็นต้องมีประสิทธิภาพมากที่สุด) คือการโพสต์การส่งทั้งหมดของคุณผ่าน MPI_Isend ในลูปแล้วใช้ MPI_Probe, MPI_Recv เพื่อประมวลผลแต่ละอันดับ recvs (เนื่องจากจำนวน recvs คือจำนวนการส่ง คุณรู้แน่ชัดว่าอย่างไร มากมาย). ตัวอย่างรหัสเทียม:

int send_count = 0;
for (int j=0; j<n; j++) {
  if (matrix_entry[j] != 0) {
    call MPI_Isend(M_local, 2, MPI_INT, j, 0, ...)
    send_count++;
  }
}
while (send_count) {
  MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, comm, status)
  /* get source from status and then call recv */
  MPI_Recv(M_j01, 2, MPI_INTEGER, status(MPI_SOURCE), ...)
 /* Do something with M_j01 */
 send_count--;
} 
person ptb    schedule 05.04.2018
comment
ขอบคุณ คุณพูดถูก อันดับและแท็กผิดพลาด ฉันแค่แก้ไขมัน ฉันมีปัญหาอื่นที่ทำให้เกิดการหยุดชะงักได้ง่าย ฉันอัปเดตคำถามของฉันและให้รายละเอียดเพิ่มเติม มีความคิดอะไรบ้าง? - person Sarah; 05.04.2018
comment
คุณยังคงรวมการดำเนินการส่ง/รับของคุณเข้าด้วยกัน แทนที่จะโพสต์การส่งทั้งหมดของคุณ จากนั้นจึงเข้าสู่การประมวลผลการรับสินค้า วิธีที่คุณเขียนนั้นเป็นแบบซิงโครนัส / บล็อกเป็นหลักเนื่องจากคุณเรียกการรอทันทีหลังจากการส่ง คุณสามารถทำเช่นนั้นได้ แต่คุณจะต้องใช้ความพยายามมากขึ้นในการจัดการส่ง/รับของคุณเพื่อให้เข้ากันได้ - person ptb; 06.04.2018

คำแนะนำเล็กๆ น้อยๆ:

คุณต้องจำไว้เสมอว่าแต่ละกระบวนการมีความเป็นอิสระ... ไม่มีการซิงโครไนซ์ระหว่างกระบวนการ (คาดว่าถ้าคุณใส่ MPI_Barrier)

ฉันไม่เข้าใจการวนซ้ำของคุณเหนือแถว (แถว = 6 หรือไม่)

จากนั้นกระบวนการทั้งหมดจะรันโค้ด.... ซึ่งหมายความว่า: P0,1,2,3,4,5,6 โทรหาคุณ sendrecv ทั้งหมดทำ 6 ครั้ง เนื่องจากการเรียกเหล่านั้นอยู่ในลูป...

สุดท้าย: เมทริกซ์ปกติจะมีขนาดเท่าใด เป็นความคิดที่ไม่ดีจริงๆ ที่จะส่งข้อความเล็กๆ จำนวนมาก

คุณควรออกแบบอัลกอริธึมของคุณดังนี้: 1) ค้นหาว่าข้อมูลใดที่กระบวนการ PX จำเป็นต้องอัปเดตคอลัมน์ทั้งหมด 2) ทำการสื่อสารที่รวบรวมข้อมูลนี้สำหรับกระบวนการทั้งหมด 3) ดำเนินการอัปเดต

person David Daverio    schedule 09.04.2018