والهدف على المدى الطويل هو جعل Shardy مكوّنًا مستقلاً تمامًا، وقابلاً للعمل مع أي لهجة من لهجات MLIR. يعتمد Shardy حاليًا مباشرةً على StableHLO، ولكن نحرز تقدّمًا نحو التخلص من ذلك من خلال الاعتماد على واجهات وعمليات تجريدية مختلفة لجعل Shardy أكثر مرونة.
قواعد التجزئة
تُشفِّر قاعدة التجزئة كيفية نشر البيانات من خلال عملية. بما أنّ Shardy
يعتمد الآن على StableHLO، فهو يحدّد قواعد التجزئة لكلّ عملية stablehlo. بالإضافة إلى ذلك، يوفّر Shardy العنصر
ShardingRuleOpInterface
الذي يمكن لمالكي اللهجات استخدامه في عملياتهم لتحديد قواعد التجزئة
لعملياتهم الخاصة. طالما أنّ العملية تنفِّذ هذه الواجهة،
سيكون بإمكان Shardy الانتشار من خلالها.
def ShardingRuleOpInterface : OpInterface<"ShardingRuleOpInterface"> {
let methods = [
InterfaceMethod<
/*desc=*/[{
Returns the sharding rule of the op.
}],
/*retType=*/"mlir::sdy::OpShardingRuleAttr",
/*methodName=*/"getShardingRule"
>,
];
}
عمليات تدفق البيانات
تتطلّب بعض العمليات، مثل العمليات المستندة إلى المنطقة، نهجًا مختلفًا حيث لا تكون قواعد التحليل المتعلّقة بالشرائح، والتي تصف فقط المطابقة بين السمات في جميع المَعلمات والنتائج، كافية. في هذه الحالات، يحدِّد Shardy ملفًا شخصيًا بعنوان
ShardableDataFlowOpInterface
لكي يتمكّن مالكو اللهجات من وصف عملية نشر التجزئة من خلال
ops. توفّر هذه الواجهة طُرقًا للحصول على مصادر واستهدافات كلّ مسار
تدفق بيانات من خلال مالكها، بالإضافة إلى الحصول على تقسيمات مالكين
المسارات وضبطها.
def ShardableDataFlowOpInterface :
OpInterface<"ShardableDataFlowOpInterface"> {
(get|set)BlockArgumentEdgeOwnerShardings;
(get|set)OpResultEdgeOwnerShardings;
getBlockArgumentEdgeOwners;
getOpResultEdgeOwners;
getEdgeSources;
// ...
}
اطّلِع أيضًا على عمليات تدفق البيانات للحصول على نظرة عامة رفيعة المستوى حول كيفية تعاملنا مع عمليات تدفق البيانات.
الواجهات التي لم يتم تنفيذها بعد
وفي المستقبل، ستتم إضافة المزيد من الواجهات والسمات لجعل Shardy أكثر مرونة واستقلالية عن اللهجة. وسنعرضها أدناه.
التقسيم المستمر
تحتوي معظم برامج مصفوفات النطاق في MLIR على مثيل واحد لقيَم ثابتة تتم إعادة استخدامها من قِبل أيّ عامل تشغيل يحتاج إلى تلك القيمة. يكون هذا منطقيًا عندما يكون الثابت المطلوب هو نفسه. ومع ذلك، لتحقيق التجزئة المثلى للبرنامج، نريد السماح بأن يكون لكل استخدام لعدد ثابت عملية تقسيم خاصة به، وألا يتأثّر بكيفية استخدام عمليات أخرى لهذا العدد الثابت.
على سبيل المثال، في الشكل أدناه، إذا تم تقسيم add
، من المفترض ألا يؤثر ذلك في كيفية تقسيمdivide
وsubtract
(في أجزاء مختلفة من العملية الحسابية).
نُطلق على ذلك اسم تبعية زائفة: لأنّ الثوابت غير مكلفة، لا تتوفر تبعية حقيقية بين العمليات التي تستخدِم الثابت نفسه. وبالتالي، يمكن للمستخدمين تحديد تقسيم عملياتهم الثابتة (والعمليات المشابهة للعمليات الثابتة). يمكن أن يتضمّن كل استخدام لهذه القيمة الثابتة تقسيمًا مختلفًا يمكن أن ينتشر بشكل منفصل إلى نسخته الخاصة من العملية الفرعية للقيمة الثابتة.
لتحقيق ذلك، على مستخدمي Shardy تحديد ما يلي: - عملية your_dialect.constant
->
sdy.constant
- سمة sdy::ConstantLike
، مثل
iota - سمة mlir::Elementwise
لعمليات العناصر، مثل add
و
multiply
-
sdy::ConstantFoldable
لعمليات مثل
slice/broadcast.
يمكن احتساب هذه العمليات من الناحية الفنية في وقت الترجمة، إذا كانت كل
مُشغّلاتها/نتائجها ثابتة.
أولويات العمليات
في GSPMD، يتمّ نشر العمليات على مستوى العنصر أولاً، ثمّ العمليات مثل matmul
.
في Shardy، نريد السماح للمستخدمين بتحديد أولويات العمليات الخاصة بهم لأنّنا لا نعرف لهجاتهم مسبقًا. وبناءً على ذلك، سنطلب منهم إرسال قائمة
بالعمليات بالترتيب الذي يريدون أن تنشره Shardy.
يوضّح الشكل أدناه كيفية استخدام الأولويات في GSPMD لنشر العمليات في الترتيب الصحيح.
اطّلِع على مستند GSPMD لمناقشة أهمية الأولويات القصوى.
أن تكون غير مرتبطة باللهجة
طالما أنّك تطبّق الواجهات والسمات والبطاقات السابقة، سيتمكّن Shardy من العمل مع لهجتك. نحن نعمل على تحسين مرونة Shardy وجعلها مناسبة لجميع اللهجات، لذا يُرجى متابعتنا باستمرار للاطّلاع على آخر الأخبار.