Python Programming

تحسين أداء بايثون: الاستفادة من Numba و Cython لتسريع العمليات الحسابية

لطالما كانت بايثون اللغة الافتراضية في علوم البيانات، والتعلم الآلي، والحوسبة العلمية. ومع ذلك، فإن طبيعتها كـ لغة مفسرة وقفل المفسر العالمي (GIL) يمكن أن يؤدي إلى اختناقات أداء كبيرة عند التعامل مع حلقات حسابية ثقيلة أو مجموعات بيانات ضخمة. بينما تُكتب مكتبات مثل NumPy و Pandas بلغة C من الداخل، فإن منطق بايثون المخصص غالبًا ما يعمل بسرعة أقل بكثير مما هو مطلوب للمعالجة في الوقت الفعلي أو المحاكاة الضخمة.

بالنسبة للمطورين من المستوى المتوسط إلى المتقدم، تكمن الحل في سد الفجوة بين سهولة استخدام بايثون وسرعة لغة C الخام. وأقوى أدوات هذه المجموعة هما Numba و Cython. كلاهما يسمح لك بتسريع كود بايثون دون التخلي عن اللغة، لكنهما يتعاملان مع المشكلة بطريقتين مختلفتين. يستكشف هذا المقال كيفية الاستفادة من هذه التقنيات لتحقيق تسريعات حسابية كبيرة.

فهم الفجوة في الأداء

قبل الغوص في الأدوات، من الضروري فهم سبب بطء بايثون. ينفذ CPython الكود سطرًا تلو الآخر، ويقوم بفحص الأنواع الديناميكية وتخصيص الذاكرة لكل عملية. في حلقة بسيطة تحسب متتالية رياضية، يمكن أن يتفوق عبء تفسير كود البايت على وقت الحساب الفعلي. على سبيل المثال، قد تستغرق حلقة تتكرر مليون مرة لجمع الأرقام المربعة عدة ثوانٍ في بايثون النقية، بينما يعمل المكافئ المجمع بلغة C في أجزاء من الثانية.

لتحسين الأداء، لدينا عادةً ثلاثة مسارات: إعادة الكتابة بلغة C/C++، أو استخدام عمليات NumPy المتجهة (Vectorized)، أو الاستفادة من مكتبات التسريع. وعندما يكون التوجيه (Vectorization) مستحيلاً بسبب منطق معقد، تصبح Numba و Cython الخيارات الأساسية.

Numba: تجميع الوقت الفعلي (JIT) ببساطة

Numba هو مترجم مفتوح المصدر للوقت الفعلي (JIT) يقوم بترجمة مجموعة فرعية من كود بايثون و NumPy إلى كود آلة سريع. ميزته البارزة هي الحد الأدنى من التغييرات المطلوبة في الكود: غالبًا ما تحتاج فقط إلى إضافة ديكور (decorator).

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

انظر إلى دالة بايثون النقية التالية:

import numpy as np
import time

def pure_python_sum(x):
    total = 0.0
    for i in range(len(x)):
        total += x[i] * x[i]
    return total

لتسريع هذا الكود باستخدام Numba، نطبق ببساطة ديكور @jit:

from numba import jit

@jit(nopython=True)
def numba_sum(x):
    total = 0.0
    for i in range(len(x)):
        total += x[i] * x[i]
    return total

مع علم nopython=True، تقوم Numba بترجمة الدالة إلى كود آلة لا يعتمد على مفسر بايثون، مما يزيل العبء الديناميكي فعليًا. في الاختبارات المعيارية (Benchmarks)، يمكن أن يحقق هذا التحول البسيط تسريعات تتراوح بين 10 إلى 100 ضعف اعتمادًا على عبء العمل.

Cython: التجميع الثابت مع تركيب بايثون

إذا كانت Numba تتعلق بالتحسين الديناميكي، فإن Cython تتعلق بالتجميع الثابت. Cython هي مجموعة فرعية من بايثون تسمح لك بكتابة كود متوافق مع C مع الحفاظ على تركيب يشبه بايثون. تقوم بترجمة ملفات .pyx الخاصة بك إلى امتدادات C (.so أو .pyd) قبل تشغيلها.

يقدم Cython تحكمًا أدق من Numba. يمكنك تعريف أنواع ثابتة للمتغيرات، والتكامل مع مكتبات C الموجودة، وتحسين استخدام الذاكرة بشكل أكثر عدوانية. هذا يجعل Cython الخيار المفضل لبناء وحدات الامتداد (Extension Modules)، أو تضمين مكتبات C، أو عندما تحتاج إلى توزيع ثنائي (Binary) محسن للغاية ومستقل.

إليك كيف يبدو المثال السابق في Cython مع الأنواع الثابتة:

def cython_sum(double[:] x):
    cdef int i
    cdef double total = 0.0
    
    for i in range(x.shape[0]):
        total += x[i] * x[i]
        
    return total

تخبر ترميز double[:] Cython بمعاملة الإدخال كعرض للذاكرة (Memory View) للأرقام العشرية، ويعلن cdef المتغيرات المحلية كنوع C. عند تجميعها باستخدام سكريبت setup.py أو cythonize، يتم توليد كود C عالي الكفاءة يعمل بسرعة قريبة من السرعة الأصلية.

متى تستخدم أيًا منهما؟

يعتمد اختيار الأداة المناسبة على قيود مشروعك المحددة. اختر Numba إذا كنت بحاجة إلى تكرار سريع، أو تريد تسريع دوال محددة دون خطوة بناء (Build Step)، أو كنت تعمل في بيئة دفتر الملاحظات Jupyter. إنها أسرع طريق لتحقيق تسريع مع أقل احتكاك.

اختر Cython إذا كنت تبني مكتبة للتوزيع تحتاج إلى أن تكون مستقلة، أو تتطلب تكاملاً عميقًا مع واجهات برمجة التطبيقات C/C++، أو تحتاج إلى إدارة الذاكرة بشكل صريح. Cython أكثر قوة لنشر الإنتاج حيث تريد ثباتًا ثابتًا (Fixed Binary Artifact) بدلاً من الكود الذي يتم تجميعه أثناء التشغيل.

الخلاصة

لا يتطلب تحسين كود بايثون للأداء الحسابي مغادرة اللغة. من خلال تطبيق استراتيجي لـ Numba لتسريع JIT أو Cython للتجميع الثابت، يمكن للمطورين فتح مستويات أداء قابلة للمقارنة مع C و Fortran. سواء كنت تعالج مجموعات بيانات ضخمة، أو تشغل محاكيات معقدة، أو تبني واجهات برمجة تطبيقات عالية الأداء، فإن فهم متى وكيف تستخدم هذه الأدوات هو مهارة حاسمة لمطور بايثون الحديث.

ابدأ بمقاييس أداء (Profiling) للكود لتحديد الاختناقات، ثم جرب ديكورات Numba أو تسميات الأنواع في Cython لتحويل حلقاتك البطيئة إلى محركات عالية السرعة.

Share: