อ่านตอนก่อนๆ ได้ที่ #Parallel
ใน ตอนที่ 1 เราพูดถึงการใช้ MPI และโครงสร้างแบบ Master-Slave ไปแล้ว โดยคอนเซ็ปของการเขียนโปรแกรมแบบพาราเรล Message Passing นั่นจะต้องมีการคุยกันระหว่างโปรเซส
วิธีง่ายที่สุดคือใช้คำสั่ง MPI_Send (สำหรับคนส่งข้อมูล) และ MPI_Recv (สำหรับคนรับข้อมูล)
แต่! ถึงจะบอกว่าส่งข้อมูลไปให้อีกโปรเซสหนึ่ง มันก็ไม่ได้ส่งเป็นข้อความอะไรหรอกนะ แต่ส่งเป็น "value" ไปตั้งหากล่ะ
อย่าง เช่น Process 0 กำลังถือค่า 100 อยู่ซึ่งเก็บอยู่ในตัวแปร X ละกัน ถ้ามันอยากส่งค่า 100 นี่ไปให้ Process 1 ... เจ้า Process 1 นี่เป็นคนรับข้อมูลก็ต้องบอกด้วยว่าค่า 100 ที่รับมาจาก Process 0 เมื่อกี้น่ะ จะให้วางไว้ในตัวแปรอะไร
MPI_Send( ตัวที่จะส่ง, ขนาด, ชนิดข้อมูล, ส่งให้ใคร, tag, MPI_COMM_WORLD )
ใช้ สำหรับคนที่ต้องการจะส่งข้อมูล โดยต้องบอกว่าจะส่งตัวแปรอะไรไป และตัวแปรตัวนั้นน่ะมันเป็นอะไร (เช่น int double char) ขนาดกี่ตัวและส่งไปให้ใคร
MPI_Recv( ตัวที่จะรับ, ขนาด, ชนิดข้อมูล, รับจากใคร, tag, MPI_COMM_WORLD, status)
เมื่อมีคนส่งก็ต้องมีคนรับ ส่งใหญ่โค้ดชุดนี้ก็จะวางไว้ในส่วนของโปรเซสที่คนเรียก MPI_Send ส่งไปหา
ทั้ง 2 คำสั่งจำเป็นต้องบอกว่าหมายเลขโปรเซสที่ส่งไปให้น่ะ มันอยู่ในกลุ่มคอมพิวเตอร์ชุดไหน ซึ่งเราใช้ MPI_COMM_WORLD เป็นตัวบอก
ส่วน tag เป็นเลขที่ต้องใส่ให้ตรงกันทั้งคนส่งและคนรับ ถ้าไม่ตรงกันจะถือว่าการส่งนี้ส่งไม่ได้นะ
สำหรับ MPI_Recv จะมี parameter มากกว่า MPI_Send 1 ตัวคือต้องเพิ่ม status เข้ามาเพื่อบอกว่าการส่งข้อมูลสำเร็จป่ะ?
มาดูตัวอย่างการเขียนโค้ดกันเลยดีกว่า
int rank; MPI_Status status; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &rank ); int x = 10, y = 20; if( rank == 0 ){ x = 100; MPI_Send( &x, 1, MPI_INT, 1, 123, MPI_COMM_WORLD ); } else if( rank == 1 ){ MPI_Send( &y, 1, MPI_INT, 0, 123, MPI_COMM_WORLD, &status ); }
อธิบายโค้ดกันหน่อย
เราเริ่มกันด้วยการประกาศตัวแปร int x = 10, y = 20; ขึ้นมา แต่อย่างที่บอกไปในตอนที่แล้ว การเรียก MPI_Init() นั้นเป็นการสั่งว่าโค้ดตั้งแต่ตรงนี้เป็นต้นไปเราจะคิดแบบพาราเรล
ดังนั้น ค่า x และ y จะถือว่าโปรเซสแต่ละตัวถือแยกกัน ไม่เกี่ยวกันเลย
จากรูป เราจะเห็นว่าทั้ง Process 0 และ Process 1 มีค่า x กับ y เป็นของตัวเองเลย ไม่แชร์กันเลย
หลัง จากนั้นเราก็ใช้รูปแบบของการสั่งงานแบบ Master-Slave คือใช้ if-else เช็กกันเลยว่าถ้า rank เป็น 0 ให้ทำงานแบบนี้ ถ้าเป็น 1 ให้ทำงานแบบนี้ๆ
Process 0 เลยทำการสั่งให้เปลี่ยนค่า x = 100 ... ในจังหวะนี้ ค่า x ที่เก็บอยู่ใน Process 1 จะยังเป็นค่าเดิม ไม่เปลี่ยนแปลง
ส่วนคำสั่งต่อไป (ทุกโปรเซสจะทำงานไปพร้อมๆ กัน) คือ Process 0 จะทำการส่งค่า x ไปให้ Process 1 ซึ่งเจ้า Process 1 ก็จะรับค่ามาจาก Process 0 มาแล้วเก็บไว้ในค่า y (ดูโค้ดตัวอย่างประกอบด้วยนะ)
ตัว แปรที่ส่งจะต้องส่งในรูปของ "pointer" (คือส่งแบบ Parse by Reference ... มีสัญลักษณ์ [&] นำหน้าตัวแปร) และต้องบอกด้วยว่าตัวแปรที่เราส่งไปน่ะ มันเป็น int ขนาด 1 ตัว (จะมีขนาดมากกว่า 1 ตัวในกรณีที่เราส่งค่าแบบเป็น array ไงล่ะ)
สรุป ... ผลสุดท้ายค่าที่โปรเซสทั้งสองตัวเก็บก็จะเป็น
- Process 0 เก็บค่าเป็น x = 100, y = 20
- Process 1 เก็บค่าเป็น x = 10, y = 100
คำถามคือถ้าเราต้องการจะส่งข้อมูลจากโปรเซสหนึ่งไปให้โปรเซสอื่นอีกหลายๆ ตัวจะต้องทำยังไง
ก็ใช้ "Loop" ยังไงล่ะ!
for( i = 1; i<size; i++){ MPI_Send( &x, 1, MPI_INT, i, 123, MPI_COMM_WORLD ); }
ตัวอย่าง เช่น ถ้าคราวนี้ Process 0 ต้องการจะส่งค่า x ไปให้โปรเซสอื่นทั้งหมดเลย แทนที่จะมาเรียก MPI_Send เองก็จัดการเขียนเป็นลูปซะเลย
แต่ เมื่อต้องเขียนแบบนี้ไปเรื่อยๆ เขาก็เริ่มเห็นว่ามันน่าจะมีคำสั่งอะไรที่มันดีกว่า Send-Receive สิน่า แบบสั่งครั้งเดียวได้เลย ไม่ต้องมานั่งลูปเอง ... ใช่แล้ว มันเลยเกิดชุดคำสั่งที่เรียกว่า "Collective Communication" เกิดขึ้นมา (ซึ่งเอาไว้คราวหน้าค่อยมาพูดต่อละกัน ฮา)