البرمجة وتطوير البرمجيات

كيف تستخدم Numba لتسريع نماذج Python الحسابية في الخدمات المالية

يشرح Chad Schuster من Milliman كيف يمكن لـ Numba تحويل أجزاء من برامج Python إلى شيفرة محسّنة عبر LLVM، مع الاستفادة من المعالجة المتوازية ووحدات GPU. وتعرض التجربة مكاسب كبيرة في الأداء، لكنها تكشف أيضاً قيوداً في البرمجة الكائنية، واستدلال الأنواع، وزمن الترجمة، ما يجعل اختيار الأجزاء المناسبة للتسريع عاملاً حاسماً.

27 أغسطس 2026
5 دقائق قراءة
3 قراءة
فريق تحرير certi.news
كيف تستخدم Numba لتسريع نماذج Python الحسابية في الخدمات المالية

عرض Chad Schuster، وهو Principal في ممارسة إدارة المخاطر المالية لدى Milliman، تجربة عملية لبناء نماذج حسابية عالية الأداء باستخدام Python وNumba، بدلاً من الاعتماد الكامل على تطبيقات مكتوبة بلغة C++. وتنطلق التجربة من مشكلة شائعة في الخدمات المالية: النماذج التي تحاكي التدفقات النقدية عبر عدد كبير من السيناريوهات تحتاج إلى قدرة حسابية كبيرة، بينما ترغب فرق العمل في الاحتفاظ بسرعة تطوير Python وسهولة صيانتها.

تكتسب المشكلة أهمية خاصة في نماذج التأمين على الحياة والمنتجات التقاعدية مثل annuities، حيث تُستخدم الحسابات لتقييم الالتزامات المستقبلية وتشغيل سيناريوهات متعددة. ووفق العرض، اعتمدت شركات تأمين تاريخياً على شبكات محلية تضم بين 5,000 و10,000 عقدة، لكن الانتقال إلى السحابة جعل زمن التشغيل وتكلفة كل عملية أكثر وضوحاً في قرارات البنية التحتية.

ما الذي تضيفه Numba إلى Python؟

تعمل Python التقليدية، أو CPython، عبر تفسير الشيفرة أثناء التشغيل، في حين تُحوّل اللغات المترجمة مثل C++ الشيفرة إلى تعليمات آلة قبل التنفيذ. تحاول Numba تقليص هذه الفجوة من خلال الترجمة الفورية Just-in-Time، إذ تُترجم الدوال المؤهلة أثناء تشغيل البرنامج باستخدام LLVM، ثم تُستبدل الدالة الأصلية بالتنفيذ المترجم.

يُفعّل المطور هذه العملية عادةً عبر مزينات Python مثل JIT أو njit. وتعمل Numba على مستوى الدالة، فتفحص الشيفرة وتحوّلها إلى تمثيل وسيط خاص بها، ثم تستنتج أنواع المتغيرات والمعاملات وقيم الإرجاع، قبل خفض التمثيل إلى LLVM IR وإجراء التحسينات اللازمة. وإذا استُدعيت الدالة بأنواع مختلفة، يمكن لـ Numba إنشاء تطبيقات مترجمة متخصصة لكل مجموعة أنواع عبر ما تسميه polymorphic dispatch.

مكاسب الأداء ليست رقماً ثابتاً

في إثبات المفهوم الذي عرضه Schuster، جعلت Numba البرنامج أسرع بنحو 75 مرة مقارنةً بـ Python المفسرة. وفي نموذج آخر، كانت النتيجة مختلفة: أدى نقل الحسابات العددية المكثفة إلى Numba على المعالج المركزي إلى تسريع يقارب الضعف، ثم أدى الانتقال إلى GPU إلى تسريع إضافي بلغ 750 مرة في ذلك التشغيل المحدد. وذكر العرض أن هذا المستوى من التحسن جعل وحدة GPU تعادل تقريباً 750 نواة مستخدمة في ذلك التشغيل، مع خفض تقديري للتكلفة إلى نحو عُشرها.

لكن هذه النتائج لا تمثل وعداً عاماً لكل تطبيق. يعتمد التحسن على مقدار الشيفرة القابلة للترجمة، وحجم العمليات الحسابية مقارنةً بعمليات الإدخال والإخراج، وكفاءة Python الأصلية، وقدرة LLVM على تحسين الشيفرة الناتجة. كما أن المعالجة على GPU لا تناسب كل خوارزمية، لذلك يجب قياس عنق الزجاجة الفعلي قبل إعادة تصميم النموذج.

ما الذي يتغير عملياً للفرق الهندسية؟

توصي التجربة بإبقاء طبقات تجهيز البيانات والمدخلات والمخرجات في Python، ونقل الجزء العددي المكثف فقط إلى Numba كلما أمكن. هذا النهج يقلل نطاق إعادة الكتابة ويحافظ على جزء كبير من بيئة Python، بينما يركز جهد التحسين على الدوال التي تستهلك معظم وقت التنفيذ. وعندما تكون الدوال العددية محدودة وواضحة، يرى Schuster أن تجربة Numba قد تكون خياراً مباشراً لمن يريد البقاء ضمن Python.

أما عند إدخال منطق واسع ومعقد إلى Numba، فتزداد كلفة التصميم والصيانة. فقد لجأت التجربة إلى مصفوفات NumPy، والـtuples، وهياكل بيانات بسيطة، لأن دعم Numba لا يغطي جميع خصائص Python. وتشمل القيود المذكورة القواميس ذات الأنواع المرنة، والاستثناءات، ومديري السياق، والإغلاقات، والقوائم المنشأة عبر comprehensions، إضافة إلى محدودية بعض الدوال الشائعة مثل print وsorted وgetattr.

القيود التي يجب احتسابها قبل الاعتماد

تظل البرمجة الكائنية نقطة ضعف مهمة في هذه التجربة. توفر Numba ميزات تجريبية مثل jitclasses وstructrefs لإضفاء سلوك شبيه بالكائنات، لكنها قد تتغير بين الإصدارات ولا تدعم GPU، وهو ما جعلها غير مناسبة للنهج الذي اعتمد عليه الفريق. لذلك اتجه التطبيق إلى أسلوب أقرب إلى البرمجة الوظيفية وهياكل البيانات البسيطة، رغم أن التصميم الكائني كان مرغوباً لأسباب تتعلق بقابلية الصيانة وتسليم النماذج للعملاء.

كما أن أخطاء استدلال الأنواع وأخطاء lowering قد تكون صعبة التتبع، خصوصاً في البرامج ذات طبقات الاستدعاء الكثيرة. وقد يشير الخطأ إلى دالة بعيدة عن الموضع الذي نشأت فيه المشكلة فعلياً. ومن الأساليب العملية المقترحة استخدام JIT بدلاً من njit عند الحاجة، بحيث يمكن إيقاف Numba مؤقتاً والعودة إلى Python المفسرة لتسهيل التصحيح، ثم إعادة تفعيل التسريع في التشغيل الإنتاجي.

هناك أيضاً زمن ترجمة أولي يحدث عند الاستدعاء الأول لكل دالة، وقد يتضخم عندما تُستخدم تقنية inlining على نطاق واسع. يمكن اللجوء إلى الترجمة المسبقة Ahead-of-Time لتجنب هذا التأخير، لكن ذلك قد يقلل قدرة الشيفرة على التكيف مع الجهاز الفعلي مقارنةً بالترجمة الفورية.

القراءة التحريرية: القيمة الأساسية في هذه التجربة ليست رقم 750 مرة بحد ذاته، بل المنهج الذي يربط قياس الأداء بتقسيم النموذج. Numba مناسبة عندما يكون عنق الزجاجة حسابياً ويمكن عزله في دوال ذات أنواع وهياكل واضحة؛ أما تحويل نظام Python بأكمله إلى شيفرة قابلة للترجمة فقد ينقل المشكلة من بطء التنفيذ إلى تعقيد التطوير والتصحيح. لذلك ينبغي للفرق المالية والهندسية أن توازن بين سرعة التشغيل، وقابلية الصيانة، وزمن الترجمة، وتوافق GPU، قبل اعتبارها بديلاً شاملاً عن C++ أو عن إعادة تصميم أوسع للنظام.

مصدر الخبر
InfoQ - Architecture Articles
فتح المصدر الأصلي
كيف أعددنا هذا الخبر؟

اعتمد الخبر على المصدر الأصلي الموضح أعلاه. قد نستخدم أدوات آلية للمساعدة في الاستخراج والتصنيف والصياغة، لكن النشر يخضع لقواعد تمنع المحتوى المكرر والقصير أو الروتيني منخفض القيمة. اقرأ سياستنا التحريرية.

ف
كاتب المقال

فريق تحرير certi.news

فريق التحرير

فريق تحرير certi.news يتابع المصادر التقنية ويعيد بناء الأخبار بالعربية مع مراجعة الحقائق والسياق قبل النشر.

من نفس التصنيف

مقالات قد تهمك

عرض جميع المقالات