الاتصال من نقطة إلى نقطة عبر RDMA لأنظمة نماذج اللغات الكبيرة
أداة أنيقة لمعالجة أنماط اتصال نماذج اللغات الكبيرة الناشئة
اعتمدت أنظمة نماذج اللغات الكبيرة (LLM) تقليديًا على الاتصالات الجماعية لأنماط التوازي المهيكلة في التدريب والاستدلال. ومع ذلك، تتطلب أحمال العمل الناشئة، مثل الاستدلال المفكك، وتوجيه خبراء المزج (MoE)، والضبط الدقيق للتعلم التعزيزي غير المتزامن، مرونة الاتصال من نقطة إلى نقطة.
تقدم ورقتنا البحثية الأحدث TransferEngine، وهي مكتبة اتصالات محمولة تعتمد على تقنية RDMA وتعمل عبر وحدات تحكم واجهة شبكة مختلفة (NIC)، بما في ذلك NVIDIA ConnectX-7 و AWS Elastic Fabric Adapter (EFA). نحن نوضح فعاليتها في ثلاثة أنظمة إنتاجية في Perplexity AI:
التعبئة وفك التشفير المفككان: نقل ذاكرة KvCache طبقة تلو الأخرى باستخدام عمليات الكتابة المقسمة إلى صفحات ومراقبين UVM، مما يسمح لن instancias التعبئة وفك التشفير بالتوسع بشكل مستقل.
نقل الأوزان للتدريب اللاحق لـ RL في أقل من ثانيتين: مزامنة سريعة للغاية للأوزان بين عقد التدريب والاستدلال.
تمكين نماذج ذات تريليون معلمة على AWS EFA: عمليات إرسال ودمج MoE منخفضة زمن الوصول باستخدام أولويات النشر والحاجز، مما يحقق زمن وصول رائد في الصناعة على ConnectX-7 وأول تنفيذ قابل للتطبيق متوافق مع AWS EFA.
تركز هذه المقالة على TransferEngine نفسه، وهو مكتبة الاتصالات التي تشغل هذه الأنظمة. اقرأ الورقة البحثية كاملة على arXiv واستكشف الكود على GitHub.
الدافع
تتفوق الاتصالات الجماعية في الأنماط المتوازية المهيكلة مثل التوازي الموثق وتوازي البيانات، لكنها تفرض قيودًا تحد من إمكانية تطبيقها على أحمال العمل الناشئة:
العضوية الثابتة: تتطلب العمليات الجماعية إنشاء عالم ارتباط عالمي في وقت التهيئة، مع إعداد متزامن عبر جميع المشاركين. بالنسبة للاستدلال المفكك، يمثل هذا مشكلة لأن عدد حالات التعبئة وفك التشفير يجب أن يتوسع ديناميكيًا استجابة لأنماط حركة المرور.
أشكال الموتر الموحدة: تفترض العمليات الجماعية أن جميع المشاركين يتبادلون موترات ذات أشكال وأنواع بيانات متطابقة. في توجيه MoE، يعتمد عدد الرموز المرسلة إلى كل خبير على البيانات ويتم اكتشافه وقت التشغيل، مما يجبر العمليات الجماعية الشاملة الكثيفة على الحشو لأحجام المخزن المؤقت للأسوأ.
ترتيب العمليات: تفرض واجهات برمجة التطبيقات الجماعية دلالات متزامنة، حتى عندما تحتاج التطبيقات فقط إلى معرفة أن مجموعة من العمليات قد اكتملت. على سبيل المثال، في نقل KvCache، يمكن لجميع الصفحات من مثيل التعبئة إلى مثيل فك التشفير الوصول بأي ترتيب. وبالمثل، في تحديثات أوزان RL، يمكن نقل جميع موترات الأوزان بشكل مستقل دون قيود ترتيب.
يعالج الاتصال من نقطة إلى نقطة القائم على RDMA هذه القيود بشكل طبيعي، لكن ثلاثة حواجز منعت اعتماده:
قيود المكتبة: بينما توفر torch.distributed و NCCL واجهات برمجة تطبيقات من نقطة إلى نقطة مثل Send/Recv، فإن هذه العمليات لا تزال ترث الدلالات والقيود الجماعية.
تنوع الأجهزة: تُعد وحدات تحكم NVIDIA ConnectX NICs مع نقل الاتصال الموثوق (RC) إعداد RDMA الأكثر شيوعًا، مما يضمن التسليم بالترتيب. يستخدم موفر السحابة مثل AWS أجهزة مختلفة، حيث يقدم Elastic Fabric Adapter (EFA) مع نقل البيانات الموثوق القابل للتطوير (SRD) الرسائل خارج الترتيب. يتطلب EFA أيضًا تجميع أربعة محولات NIC بسرعة 100 جيجابت في الثانية (أو محولين بسرعة 200 جيجابت في الثانية) لتحقيق عرض نطاق ترددي 400 جيجابت في الثانية.
قابلية نقل البرامج: تعاني مكتبات RDMA الحالية من قفل البائعين. تتطلب DeepEP RDMA المُبادَرة بواسطة وحدة المعالجة الرسومية (IBGDA) المتوفرة فقط على ConnectX. تُظهر NVSHMEM تدهورًا حادًا في الأداء على EFA. تفتقر مكتبات مثل Mooncake و NIXL إلى دعم EFA أو تظل في مراحل التطوير المبكرة. ونتيجة لذلك، لا يوجد حل قابل للتطبيق عبر موفرين لاستدلال نماذج اللغات الكبيرة.
TransferEngine
يعالج TransferEngine هذه الحواجز من خلال أربع قدرات أساسية:
افتراضات النقل الشائعة: بدلاً من استهداف NIC معين، يفترض TransferEngine فقط أن النقل الأساسي موثوق به ولكنه غير مرتب، أي تقاطع الضمانات التي توفرها عمليات نقل RC و SRD.
مزامنة ImmCounter: أداة بدائية مبتكرة لإشعار الإكمال تعمل دون الاعتماد على ترتيب الرسائل، مما يتيح تشغيل نفس الكود على عمليات النقل المرتبة (RC) وغير المرتبة (SRD).
دعم بطاقات NIC متعددة: إدارة شفافة لبطاقات NIC متعددة لكل وحدة معالجة رسومية، مع تقسيم عمليات النقل تلقائيًا لتحقيق أقصى عرض نطاق ترددي.
واجهة برمجة تطبيقات مصغرة: مجموعة صغيرة من العمليات الكافية لتنفيذ أنماط اتصال معقدة.
يقوم TransferEngine بإنشاء سلسلة عاملة واحدة لكل وحدة معالجة رسومية. تدير كل سلسلة عاملة DomainGroup تنسق من 1 إلى 4 بطاقات RDMA NIC، اعتمادًا على منصة الأجهزة (يوفر ConnectX-7 الفردي 400 جيجابت في الثانية، بينما يتطلب EFA أربعة محولات بسرعة 100 جيجابت في الثانية أو محولين بسرعة 200 جيجابت في الثانية). داخل DomainGroup، تكون كل Domain خاصة بالأجهزة وتدير بطاقة NIC واحدة، وتتعامل مع إدارة أزواج قوائم الانتظار، وإرسال العمل، واستطلاع الإكمال.
تعرض كل مثيلة TransferEngine عنوانًا رئيسيًا main address لتحديد الأقران والاكتشاف. يتبادل الأقران البعيدون هياكل NetAddr التي تفرز عناوين الشبكة ويمكن تسلسلتها للاتصال خارج النطاق. يجب على جميع الأقران في النظام استخدام نفس عدد بطاقات NIC لكل وحدة معالجة رسومية. يتيح هذا التكوين الموحد لـ TransferEngine تقسيم عمليات النقل بذكاء: عند بدء النقل، يعرف كل من المصدر والوجهة بالضبط بطاقات NIC المتاحة، مما يتيح موازنة الحمل التلقائية عبر جميع المحولات.
تصميم واجهة برمجة التطبيقات
يوفر TransferEngine واجهة برمجة تطبيقات مصغرة تجرّد تعقيد واجهات RDMA الأساسية.
#[serde] struct NetAddr(Bytes);
#[serde] struct MrDesc{ ptr: u64, rkeys: Vec<(NetAddr, u64)> }
struct MrHandle(NonNull<c_void>); type Offset = u64;
struct Pages{ indices: Vec<u32>, stride: u64, offset: Offset }
struct PeerGroupHandle(u64);
struct ScatterDst{ len: u64, src: Offset, dst: (MrDesc,Offset)}
enum OnDone { Callback(fn () -> ()), Flag(Atomic<bool>) }
trait TransferEngine {
fn main_address() -> NetAddr;
// Memory Region Management
fn reg_mr(ptr, len, device) -> (MrHandle, MrDesc);
// Two-sided Send/Recv
fn submit_send(addr: NetAddr, msg: &[u8], cb: fn () -> ());
fn submit_recvs(len: u64, cnt: u64, cb: fn (&[u8]) -> ());
// One-sided Write
fn expect_imm_count(imm: u32, count: u32, cb: fn () -> ());
fn submit_single_write(len: u64, imm: Option<u32>
src: (MrHandle, Offset), dst: (MrDesc, Offset), OnDone);
fn submit_paged_writes(page_len: u64, imm: Option<u32>,
src: (MrHandle, Pages), dst: (MrDesc, Pages), OnDone);
// One-sided Write to a group of peers
fn add_peer_group(addrs: Vec<NetAddr>) -> PeerGroupHandle;
fn submit_scatter(h: Option<PeerGroupHandle>, OnDone,
imm: Option<u32>, src: MrHandle, dst: Vec<ScatterDst>);
fn submit_barrier(h: Option<PeerGroupHandle>, OnDone,
imm: u32, dst: Vec<MrDesc>);
// Watcher for CPU-GPU synchronization
fn alloc_uvm_watcher(cb: fn(u64,u64) -> ()) -> NonNull<u64>;
}تتكون واجهة برمجة التطبيقات من عدة مكونات رئيسية:
تسجيل الذاكرة
تتطلب عمليات RDMA تسجيل مناطق الذاكرة في NIC قبل نقل البيانات. تأخذ الدالة reg_mr مؤشرًا وطولًا وجهازًا، وتُرجع مقبضين:
MrHandle: مقبض محلي يُستخدم كمصدر لعمليات النقل
MrDesc: محدد قابل للتسلسل يمكن تبادله مع الأقران البعيدين لتمكينهم من الكتابة إلى الذاكرة المسجلة
يحتوي هيكل MrDesc على قائمة بعناوين شبكة البعيدة ومفتاح بعيد (واحد لكل NIC) يمنح وصولاً عن بُعد إلى منطقة الذاكرة. يتعامل هذا التصميم بشكل طبيعي مع إعدادات بطاقات NIC المتعددة.
إرسال/استقبال ثنائي الاتجاه
للرسائل الصغيرة ورسائل مستوى التحكم على غرار RPC، يوفر TransferEngine submit_send و submit_recvs:
submit_send: يرسل الرسالة إلى نظير بعيد مُحدَّد بواسطة NetAddr. يتم نسخ الرسالة داخليًا، بحيث يمكن للداعي إعادة استخدام المخزن المؤقت أو تحريره على الفور.
submit_recvs: ينشر تجمعًا من المخازن المؤقتة للاستقبال. عند وصول رسالة، يتم استدعاء رد اتصال بالبيانات المستلمة. يتم إرجاع المخزن المؤقت تلقائيًا إلى التجمع بعد اكتمال رد الاتصال.
كتابة أحادية الاتجاه
لنقل البيانات المجمعة، يوفر TransferEngine عمليات كتابة أحادية الاتجاه خالية من النسخ:
submit_single_write: ينقل منطقة متجاورة من المصدر إلى الوجهة
submit_paged_writes: ينقل صفحات غير متجاورة محددة بالفهارس والخطوات والإزاحات
تقبل كلتا العمليتين قيمة فورية اختيارية مدتها 32 بت يتم تسليمها إلى المتلقي بشكل ذري عند الانتهاء.
يقوم المحرك تلقائيًا بتقسيم عمليات النقل الكبيرة عبر بطاقات NIC متعددة لتعظيم عرض النطاق الترددي.
مجموعات الأقران والعمليات المجمّعة
بالنسبة للعمليات التي تستهدف عدة أقران (وهو أمر شائع في توجيه MoE)، يوفر TransferEngine أولويات محسنة:
add_peer_group: يسجل مسبقًا مجموعة من عناوين الأقران للبحث السريع
submit_scatter: يرسل شرائح بيانات مختلفة لكل نظير في المجموعة
submit_barrier: يرسل إشعارًا فوريًا فقط لجميع الأقران (نقل بقيمة صفر بايت)
تم تحسين هذه العمليات لتقليل زمن الوصول عن طريق التخزين المؤقت لهياكل البيانات الداخلية وطلبات عمل RDMA النموذجية، مما يتجنب النفقات العامة للإعداد المتكرر.
مراقب UVM: إرسال الإشارات من وحدة المعالجة الرسومية إلى وحدة المعالجة المركزية
تتمثل التحديات الرئيسية في أنظمة نماذج اللغات الكبيرة في تنسيق عمليات نقل RDMA مع تقدم وحدة المعالجة الرسومية. من الناحية المثالية، يجب أن يبدأ نقل البيانات بمجرد إنتاج وحدة المعالجة الرسومية للمخرجات، ولكن استطلاع وحدة المعالجة الرسومية من وحدة المعالجة المركزية غير فعال.
يعالج TransferEngine هذا الأمر باستخدام UVM Watchers: مواقع ذاكرة افتراضية موحدة يمكن تحديثها بواسطة نوى وحدة المعالجة الرسومية (حتى داخل رسوميات CUDA) ويمكن استطلاعها بكفاءة بواسطة وحدة المعالجة المركزية باستخدام GDRCopy. عند تخصيص مراقب UVM، يتم تسجيل رد اتصال يتم استدعاؤه كلما قامت وحدة المعالجة الرسومية بتحديث القيمة.
يتيح ذلك أنماطًا مثل نقل KvCache طبقة تلو الأخرى: بعد اكتمال كل طبقة محول، تقوم وحدة المعالجة الرسومية بزيادة المراقب، مما يؤدي إلى تشغيل نقل RDMA لـ KvCache لتلك الطبقة بينما تستمر الطبقات اللاحقة في الحساب.
إشعار الإكمال
يوفر TransferEngine آليات إشعار الإكمال لكل من جانب المُبادِر (داعي submit_write) وجانب المتلقي (الهدف من عمليات الكتابة).
إشعار جانب المُبادِر: عند تقديم عملية كتابة، يمكن للداعي اختيار تلقي إشعار عند الانتهاء إما من خلال دالة رد اتصال أو علامة ذرية. يؤكد هذا الإشعار أنه تم نقل البيانات بنجاح من مخزن المصدر المؤقت.
إشعار جانب المتلقي: الابتكار الرئيسي هو ImmCounter، وهو مكون يتتبع العدادات المفهرسة بأرقام فورية مدتها 32 بت. عندما تحمل عملية كتابة قيمة فورية، يتم زيادة ImmCounter للمتلقي بشكل ذري عند تسليم البيانات. تتيح دالة expect_imm_count للمتلقي تسجيل رد اتصال يتم تشغيله عندما تصل قيمة فورية معينة إلى العدد المستهدف. يمكن مزامنة العدادات بشفافية مع وحدة المعالجة الرسومية عبر GDRCopy، أو استطلاعها مباشرة، أو مراقبتها من خلال عمليات رد الاتصال.
توفر آلية جانب المتلقي هذه مزامنة موثوقة دون الحاجة إلى ترتيب الرسائل. وهي تعمل بشكل جيد بنفس القدر على بطاقات ConnectX NICs (التي تضمن التسليم بالترتيب عبر نقل RC) وبطاقات EFA NICs (التي تسلم الرسائل خارج الترتيب عبر نقل SRD).
التنفيذ
تم تنفيذ TransferEngine بلغة Rust، مع تحسين التخصيصات والخيوط والمزامنة بعناية لتقليل زمن الوصول وتحقيق إنتاجية عالية. يقوم المحرك بإنشاء سلسلة عاملة واحدة لكل DomainGroup، وكل منها مثبت على نواة وحدة المعالجة المركزية على عقدة NUMA التي تترابط بها الأجهزة، مما يقلل من زمن وصول الجدولة والوصول إلى الذاكرة. يتم تخصيص هياكل البيانات الخاصة بالنطاق بعد التثبيت لضمان حجز الذاكرة في عقدة NUMA الصحيحة. تتعامل سلسلة عاملة واحدة مع ما يصل إلى 4 Domains (تدير كل منها بطاقة NIC واحدة)، بينما تقوم سلسلة أخرى مخصصة باستطلاع وحدة المعالجة الرسومية لتحديث مراقبي UVM. يتم الاتصال عبر السلاسل من خلال طوابير خالية من الأقفال.
تقوم واجهة برمجة التطبيقات بتوجيه الطلبات إلى DomainGroup التي تخدم الجهاز المناسب، حيث يخدم الأول المضيف أيضًا. في حلقة ضيقة، يستطلع عامل النطاق عن عمل جديد، مع إعطاء الأولوية لتقديم طلبات جديدة. اعتمادًا على الأجهزة والتكوين، يتم تقسيم الطلبات وموازنة حملها عبر Domains المتاحة. تتم كتابة أول عملية كتابة لطلب مركب على الفور في قائمة انتظار الإرسال الخاصة ببطاقة NIC. بمجرد نفاد الطلبات الجديدة، ينتقل العامل لمعالجة العمليات المعلقة، ونشر عمليات الكتابة لملء خط الأنابيب للأجهزة. بعد ذلك، يتم استطلاع طوابير الإكمال للاستعلام عن عمليات النقل المنتهية وزيادات العداد الفورية. يتم تجميع الأحداث لتسليم إشعارات لكل نقل، وتسليم النقل إلى سلسلة رد اتصال مخصصة مشتركة بين جميع المجموعات.
التقسيم داخل DomainGroup مرن. يمكن أن تستهدف عمليات النقل بطاقات NIC معينة حسب الفهرس. يمكن تقسيم عملية كتابة واحدة. يمكن لعمليات النقل المقسمة إلى صفحات، والانتشار، والحاجز، والتي تترجم جميعها إلى عمليات كتابة متعددة، التقسيم عبر جميع بطاقات NIC.
تحسينات خاصة بالأجهزة
تعتبر النطاقات Domains داخل TransferEngine متخصصة ومتحسنة للأجهزة الخاضعة لسيطرتها:
AWS EFA: يغتنم تنفيذ EFA استخدام libfabric، لإدارة نطاق نسيج لكل NIC داخل DomainGroup. تتضمن قرارات التنفيذ الرئيسية والتحسينات ما يلي:
عمليات كتابة بحجم صفر: يبتعد EFA عن مواصفات RDMA من خلال طلب محددات هدف صالحة حتى لعمليات الكتابة بحجم صفر. ومن ثم، يفرض TransferEngine محددات صالحة لجميع عمليات النقل.
صياغة طلب العمل: يقوم مسبقًا بتعبئة الحقول المشتركة لهياكل libfabric والاحتفاظ بها قبل النشر، مما يقلل من النفقات العامة لكل عملية لعمليات النقل المجمعة وعمليات مجموعة الأقران
NVIDIA ConnectX-7: يستخدم تنفيذ ConnectX-7 libibverbs مع نقل RC. تتضمن قرارات التنفيذ الرئيسية والتحسينات ما يلي:
أزواج قوائم انتظار UD للمصافحة: تستخدم كل Domain زوج قائمة انتظار بيانات غير موثوقة (UD) لتبادل مصافحات اتصال RC.
أزواج قوائم انتظار RC منفصلة: ينشئ زوجين من قوائم انتظار RC لكل نظير، أحدهما لعمليات الإرسال/الاستقبال ثنائية الاتجاه والآخر لعمليات الكتابة أحادية الاتجاه Write و WriteImm. هذا الفصل ضروري لأن كل من عمليات اكتمال Recv و WriteImm تستهلك طلبات العمل بترتيب النشر، مما يسمح بدلالات Recv عالية المستوى مع دعم WriteImm دون تداخل.
صياغة طلب العمل: يقوم مسبقًا بتعبئة الحقول المشتركة لهياكل طلبات العمل لعمليات النقل المجمعة ومجموعات الأقران.
سلاسل طلبات العمل: يربط ما يصل إلى 4 طلبات عمل من خلال مؤشر next لـ ibv_send_wr، مما يقلل من عدد رنات جرس الباب إلى بطاقة NIC.
ترتيب استرخائي: يمكن تمكين IBV_ACCESS_RELAXED_ORDERING للسماح بمعاملات PCIe خارج الترتيب بين بطاقة NIC وذاكرة وحدة المعالجة الرسومية.
الخاتمة
من خلال استهداف تقاطع الضمانات عبر عمليات نقل RDMA المختلفة وإدخال مزامنة مستقلة عن الترتيب عبر ImmContainer، يوفر TransferEngine أساسًا محمولاً للاتصال من نقطة إلى نقطة في أنظمة نماذج اللغات الكبيرة. المكتبة مفتوحة المصدر ومتوفرة على GitHub، ونحن نشجع المجتمع على استكشافها والبناء عليها لأنماط الاتصال الناشئة في أنظمة التعلم الآلي واسعة النطاق.
الاستشهاد
@online{perplexity2025RdmaPointToPointCommunicationForLlmSystems,
author = {{Perplexity} and Nandor Licker and Kevin Hu and Vladimir Zaytsev and Lequn Chen},
title = {{RDMA point-to-point communication for LLM systems}},
date = {2025-11-05},
url = {https://www.perplexity.ai/hub/blog/rdma-point-to-point-communication-for-llm-systems}
}