ข้อมูลเบื้องต้น
เราถือว่าผู้อ่านคุ้นเคยกับข้อมูลเบื้องต้นเกี่ยวกับการนำเสนอการแยกข้อมูลเป็นอย่างน้อย ซึ่งอธิบายวิธีแสดงการแยกข้อมูลของเทมพอร์ใน Shardy เอกสารนี้แสดงวิธีใช้การนำเสนอการแยกส่วนในโปรแกรม เช่น เพื่อแนบการแยกส่วนกับเทนเซอร์ที่เฉพาะเจาะจงของโปรแกรม
การนำไปใช้งานการแยกกลุ่มเป็นกระบวนการตัดสินใจเกี่ยวกับการแยกกลุ่มสำหรับเทนเซอร์ทุกรายการในโปรแกรมโดยพิจารณาจากข้อจำกัดการแยกกลุ่มสำหรับเทนเซอร์ชุดย่อย คอมไพเลอร์ API ของ Shardy มีวิธีต่างๆ ในการส่งผลต่อ/ควบคุมการนำไปใช้งานการแยกข้อมูล นอกจากนี้ ยังช่วยให้ผู้ใช้แทรกการคํานวณที่แบ่งกลุ่มด้วยตนเองลงในโปรแกรมได้
วัตถุประสงค์
เอกสารนี้อธิบายการออกแบบคอมโพเนนต์ API ดังกล่าวใน Shardy และอธิบายลักษณะการทำงานและค่าคงที่ โปรดทราบว่าแม้ว่า API นี้จะใช้ในการควบคุมการนำไปใช้งานการแยกข้อมูล แต่เอกสารนี้จะไม่กล่าวถึงลักษณะการนำไปใช้งานหรือการทํางาน
ภาพรวม
การแยกกลุ่มอินพุต/เอาต์พุต - แนบการแยกกลุ่มกับอินพุตหรือเอาต์พุตของฟังก์ชันหลักเพื่อระบุว่านี่คือวิธีที่ควรแยกกลุ่มเทนเซอร์อินพุต/เอาต์พุตเมื่อส่งไปยัง/แสดงผลจากฟังก์ชัน
ข้อจำกัดการแยกกลุ่ม - แนบการแยกกลุ่มไปยังเทนเซอร์ระดับกลาง (เช่น ผลลัพธ์ของ matmul) เพื่อระบุว่านี่คือวิธีที่ควรแยกกลุ่มเทนเซอร์นั้นหรือกลุ่มย่อยของการใช้งาน
กลุ่มการแยกกลุ่ม - จัดกลุ่มเทนเซอร์หลายรายการตามรหัสเพื่อบ่งบอกว่าควรแยกกลุ่มด้วยวิธีเดียวกัน
การคํานวณด้วยตนเอง - ล้อมรอบการคํานวณย่อยที่มีการแบ่งพาร์ติชันด้วยตนเองโดยใช้ชุดย่อยของแกนเมช โดยมีการระบุการแยกกลุ่มตามแกนด้วยตนเองเหล่านั้นสําหรับอินพุตและเอาต์พุตทั้งหมด และภายในการคํานวณย่อย ประเภทเทนเซอร์จะเป็นแบบภายในเมื่อเทียบกับการแยกกลุ่มเหล่านั้น
การออกแบบในรายละเอียด
การแยกส่วนอินพุต/เอาต์พุต
อนุญาตให้ผู้ใช้ระบุการแยกข้อมูลสําหรับอินพุตและเอาต์พุตของฟังก์ชันหลัก
ใน MLIR คุณสามารถแนบแอตทริบิวต์กับอาร์กิวเมนต์และผลลัพธ์ของฟังก์ชันได้ ดังนั้นผู้ใช้จึงแนบแอตทริบิวต์การแยกข้อมูลไปยังฟังก์ชันด้วยวิธีนี้ได้
เช่น
@mesh_xy = <["x"=2, "y"=2]>
// The 1st input has a sharding specified, but the 2nd input doesn't.
// The output has a sharding specified.
func @main(%arg0: tensor<8x8xf32>
{sdy.sharding = #sdy.sharding<@mesh_xy, [{"x"}, {}]>},
%arg1: tensor<8x16xf32>)
-> (tensor<8x16xf32> {sdy.sharding = #sdy.sharding<@mesh_xy, [{}, {"y"}]>}) {
...
}
ข้อจำกัดการแยกกลุ่ม
อนุญาตให้ผู้ใช้แนบการแยกส่วนกับเทนเซอร์กลางในโปรแกรม ซึ่งจะบอกเครื่องแบ่งพาร์ติชันว่าควรแยกเทนเซอร์หรือชุดย่อยของการใช้งานเทนเซอร์นั้นอย่างไร
การดำเนินการนี้เป็นการดำเนินการ MLIR ที่ใช้เทนเซอร์เป็นอินพุตและมีแอตทริบิวต์การแยกข้อมูลอยู่ การดำเนินการอาจเป็นดังนี้
- ไม่มีการใช้งาน (ไม่ได้ใช้) ซึ่งหมายความว่าการแยกกลุ่มที่แนบมาคือวิธีที่ควรแยกกลุ่มเทนเซอร์
- มีการใช้งาน - ซึ่งหมายความว่าการแยกส่วนงานที่แนบมาคือวิธีที่ควรแยกส่วนของการใช้งานการดําเนินการข้อจํากัดการแยกส่วน ขณะที่การใช้งานอื่นๆ ของเทมพอร์ลอินพุตอาจมีการแยกส่วนที่แตกต่างกัน (หากเทมพอร์ลอินพุตไม่มีการใช้งานอื่นๆ ลักษณะการทํางานจะเหมือนกับกรณีไม่มีการใช้งาน) การนำไปใช้งานจะกำหนดการแยกกลุ่มของเทมพอร์และแยกกลุ่มใหม่หากจำเป็น
อาจมีการจัดสรรมิติข้อมูลแบบเปิด ซึ่งหมายความว่าตัวดำเนินการสามารถแบ่งตามแกนที่มีอยู่ได้
@mesh_xy = <["x"=2, "y"=2]>
%0 = ... : tensor<8x8xf32>
%1 = sdy.sharding_constraint %0 <@mesh_xy, [{"x"}, {?}]> : tensor<8x8xf32>
กลุ่มการแยกกลุ่ม
ในกรณีที่ไม่มีข้อมูลพึ่งพาหรือข้อมูลพึ่งพาที่แน่นหนาระหว่างเทนเซอร์ 2 รายการขึ้นไป ในขณะที่ผู้ใช้ทราบว่าควรแบ่งเทนเซอร์เหล่านั้นในลักษณะเดียวกันหรือคล้ายกัน Shardy API มีวิธีระบุความสัมพันธ์นี้ ซึ่งช่วยให้ผู้ใช้มีอิสระในการระบุอย่างชัดเจนว่าควรแบ่งกลุ่มเทนเซอร์แยกกัน
เราจึงขอแนะนําแนวคิดกลุ่มกลุ่มย่อย ซึ่งแต่ละกลุ่มจะมีคําสั่งจํานวนเท่าใดก็ได้ที่เชื่อมโยงกับรหัสกลุ่มกลุ่มย่อยเดียวกัน กลุ่มการแยกข้อมูลจะบังคับให้ใช้การแยกข้อมูลภายในกลุ่มเดียวกัน
ตัวอย่างเช่น ในโปรแกรมผู้ใช้สมมติดังที่แสดงด้านล่าง เราต้องการแบ่งกลุ่มผลลัพธ์ของโปรแกรมให้เหมือนกับอินพุตของโปรแกรมทุกประการ โดยที่ข้อมูลทั้งสองไม่มีความเกี่ยวข้องกัน
หากเราเรียกใช้โปรแกรมนี้ การนำไปใช้งานการแยกข้อมูลจะไม่สามารถทำนายการแยกข้อมูลของเทมพอร์ล %1
และ %2
และระบบจะทําซ้ำข้อมูลดังกล่าว
อย่างไรก็ตาม เราได้แนบแอตทริบิวต์ shard_group
ที่ระบุว่าอินพุต %0
และเอาต์พุต %2
อยู่ใน shard_group
เดียวกัน ซึ่งจะช่วยให้เราอนุญาตให้มีการนำไปใช้การแยกส่วน @mesh_xy,
[{"x"},{"y"}]>
จากอินพุต %0
ไปยังเอาต์พุต %2
และไปยังส่วนที่เหลือของกราฟ ซึ่งมีการออกอากาศ %1
คงที่ที่นี่ เรากําหนดค่าให้กับกลุ่มได้ด้วยการดำเนินการ sdy.sharding_group
@mesh_xy = <["x"=2, "y"=2]>
module @"jit_zeros_like" {
func.func @main(%arg0: tensor<8x2xi64> {sdy.sharding = #sdy.sharding<@mesh_xy, [{"x"},{"y"}]>} }) -> (tensor<8x2xi64>) {
%0 = sdy.sharding_group %arg0, id=0 : tensor<8x2xi64>
%1 = stablehlo.constant dense<0> : tensor<8x2xi64>
%2 = sdy.sharding_group %1, id=0 : tensor<8x2xi64>
return %2 : tensor<8x2xi64>
}
}
ในตัวอย่างง่ายๆ ข้างต้น เราอาจระบุการแยกกลุ่มเดียวกันในเอาต์พุตเป็นอินพุตได้อย่างชัดเจน ซึ่งจะให้ผลลัพธ์เดียวกัน เนื่องจากเราทราบอยู่แล้วว่าต้องการกำหนดกลุ่มใดให้กับอินพุตล่วงหน้า แต่ในกรณีจริง เราใช้กลุ่มเพื่อทำให้กลุ่มของเทนเซอร์หลายรายการซิงค์กันโดยไม่ต้องทราบการแยกกลุ่มของเทนเซอร์ใดๆ ขณะ Shardy จะจัดการส่วนที่เหลือและค้นหาการแยกกลุ่มที่ดีที่สุดที่จะกำหนดให้กับเทนเซอร์เหล่านั้น
การคํานวณด้วยตนเอง
ผู้ใช้อาจต้องการควบคุมอย่างชัดแจ้งว่าระบบจะแบ่งพาร์ติชันการประมวลผลบางส่วนอย่างไร และจะใช้กลุ่มใด ตัวอย่างเช่น ผู้ใช้บางรายต้องการใช้ matmul แบบรวมด้วยตนเอง (จาก API ของส่วนหน้า) แทนที่จะเลื่อนไปให้คอมไพเลอร์ เรามี API การประมวลผลด้วยตนเองที่ช่วยให้ดำเนินการดังกล่าวได้
นี่คือการดำเนินการ MLIR ที่มีภูมิภาคเดียวสําหรับการคํานวณย่อยด้วยตนเอง ผู้ใช้จะระบุการแยกข้อมูลอินพุต/เอาต์พุตไปยังการคํานวณย่อยนี้โดยใช้ชุดย่อย (รวมถึงชุดย่อยทั้งหมด) ของแกนมัด การประมวลผลย่อยจะเป็นแบบเฉพาะที่/ด้วยตนเองสำหรับแกนเมชที่ระบุ (หรือที่เรียกว่าแกนด้วยตนเอง) และแบบทั้งระบบ/ไม่ได้แบ่งพาร์ติชันสำหรับแกนที่ไม่ระบุ (หรือที่เรียกว่าแกนอิสระ) การคํานวณย่อยสามารถแบ่งออกเป็นส่วนๆ ตามแกนอิสระได้ในระหว่างการนำไปใช้งาน เช่นเดียวกับการคํานวณนอกการดำเนินการนี้
เช่น
@mesh_name = <["data"=2, "model"=2]>
%0 = ... : tensor<16x32xf32>
%1 = sdy.manual_computation(%0)
in_shardings=[<@mesh_name, [{"data"}, {"model",?}]>]
out_shardings=[<@mesh_name, [{"data"}, {?}]>]
manual_axes={"data"}
(%arg1: tensor<8x32xf32>) {
// body
return %42 : tensor<8x32xf32>
} : (tensor<16x32xf32>) -> tensor<16x32xf32>
อินตัวแปร
in_shardings
,out_shardings
และmanual_axes
ทั้งหมดต้องอ้างอิงถึงเมชเดียวกันmanual_axes
จัดเรียงตามเมชmanual_axes
ต้องใช้อย่างชัดเจนในการแยกส่วนข้อมูลเข้า/ออกทั้งหมด กล่าวคือ สำหรับการแยกแต่ละส่วน แกนที่กำหนดเองทั้งหมดต้องแยกมิติข้อมูลหรือทำซ้ำอย่างชัดเจนหากแกนอิสระ (แกนเมชที่ไม่ได้อยู่ใน
manual_axes
) อยู่ในการจัดกลุ่มอิน/เอาต์รายการใดรายการหนึ่ง แกนนั้นต้องเป็นแกนรองของแกนที่กำหนดเองในการจัดกลุ่มมิติข้อมูลเดียวกัน (ในตัวอย่างนี้ การจัดกลุ่มมิติข้อมูล{"model", "data"}
จะใช้งานไม่ได้)ภูมิภาค/เนื้อหาของการคํานวณคือการคํานวณในเครื่อง (เช่น รวมกลุ่มที่ผู้ใช้ระบุ) ต้องเป็นข้อมูลในเครื่องสำหรับการจัดสรรข้อมูลขาเข้า/ขาออก вдоль แกนที่กำหนดเอง (ดูหมายเหตุด้านบน)
การฝังการคํานวณด้วยตนเอง
คุณสามารถฝังการคํานวณด้วยตนเองหลายรายการไว้ด้วยกันได้ ตราบใดที่แต่ละรายการทํางานบนแกนด้วยตนเองชุดที่ไม่ซ้ำกัน