NumPy: تحسين الأداء
آخر تحديث: 2026-08-26
1. ما ستتعلمه
- ❶ المتجهة — قاعدة الأداء رقم 1
- ❷ تخطيط الذاكرة: C-contiguous مقابل Fortran-contiguous
- ❸ العمليات في المكان مقابل النسخ
- ❹ قياس أداء كود NumPy
- ❺ أخطاء الأداء الشائعة
2. المفاهيم الأساسية
(1) المتجهة
PYTHON
import numpy as np
import time
n = 10_000_000
a = np.random.rand(n)
b = np.random.rand(n)
# بطيء: حلقة Python
t0 = time.time()
c = np.empty(n)
for i in range(n):
c[i] = a[i] + b[i]
t1 = time.time()
---
## 3. سريع: مُتجه
t2 = time.time()
d = a + b
t3 = time.time()
print(f"Loop: {t1-t0:.3f}s, Vectorized: {t3-t2:.6f}s")
print(f"Speedup: {(t1-t0)/(t3-t2):.0f}x")
(2) العمليات في المكان
PYTHON
---
## 4. تجنب النسخ باستخدام معامل out=
a = np.random.rand(1000000)
b = np.random.rand(1000000)
---
## 5. نسخ (يخصص ذاكرة جديدة)
c = a + b
---
## 6. في المكان (لا تخصيص)
np.add(a, b, out=a)
▶ مثال: مقارنة سرعة المتجهة (الصعوبة ⭐⭐)
PYTHON
import numpy as np
import time
n = 1_000_000
a = np.random.rand(n)
b = np.random.rand(n)
---
## 7. بطيء: حلقة Python
t0 = time.time()
c = np.empty(n)
for i in range(n):
c[i] = a[i] + b[i]
t1 = time.time()
---
## 8. سريع: مُتجه
t2 = time.time()
d = a + b
t3 = time.time()
loop_time = t1 - t0
vec_time = t3 - t2
print(f"Loop: {loop_time:.3f}s, Vectorized: {vec_time:.6f}s")
print(f"Speedup: {loop_time/vec_time:.0f}x")
المخرجات:
TEXT
📖 للعرض فقط
# تم التنفيذ بنجاح
المخرجات:
TEXT 📖 للعرض فقطLoop: 0.312s, Vectorized: 0.002s Speedup: 156x
▶ مثال: العمليات في المكان باستخدام out= (الصعوبة ⭐⭐)
PYTHON
import numpy as np
a = np.array([10, 20, 30, 40, 50])
b = np.array([1, 2, 3, 4, 5])
---
## 9. بدون out: ينشئ مصفوفة جديدة
c = np.add(a, b)
print("Without out:", c)
---
## 10. مع out: يعيد استخدام الذاكرة الموجودة
result = np.empty(5)
np.add(a, b, out=result)
print("With out:", result)
---
## 11. تسلسل العمليات في المكان
np.multiply(result, 2, out=result)
np.add(result, 1, out=result)
print("Chained:", result)
المخرجات:
TEXT
📖 للعرض فقط
Without out:
With out:
Chained:
المخرجات:
TEXT 📖 للعرض فقطWithout out: [11 22 33 44 55] With out: [11 22 33 44 55] Chained: [23 45 67 89 111]
▶ مثال: تأثير تخطيط الذاكرة (الصعوبة ⭐)
PYTHON
import numpy as np
---
## 12. C-contiguous (صف-رئيسي) — الافتراضي
c = np.array([[1, 2, 3], [4, 5, 6]])
print("C-contiguous:", c.flags.c_contiguous)
print("Fortran-contiguous:", c.flags.f_contiguous)
---
## 13. تحويل إلى Fortran-contiguous (عمود-رئيسي)
f = np.asfortranarray(c)
print("Fortran-contiguous:", f.flags.f_contiguous)
---
## 14. الشكل والخطوات
print("C strides:", c.strides)
print("F strides:", f.strides)
المخرجات:
TEXT
📖 للعرض فقط
C-contiguous:
Fortran-contiguous:
Fortran-contiguous:
C strides:
F strides:
المخرجات:
TEXT 📖 للعرض فقطC-contiguous: True Fortran-contiguous: False Fortran-contiguous: True C strides: (24, 8) F strides: (8, 24)
❓ أسئلة شائعة
س ما هي أهم قاعدة أداء واحدة؟
ج تجنب حلقات Python. كل مرة تكتب فيها
for i in range(n)، اسأل: "هل يمكنني جعلها مُتجهة؟" إذا كانت الإجابة نعم، سيكون NumPy أسرع بـ 10-100 مرة.س ما الفرق بين ترتيب C وترتيب Fortran؟
ج ترتيب C (صف-رئيسي) يخزن الصفوف بشكل متجاور. ترتيب Fortran (عمود-رئيسي) يخزن الأعمدة بشكل متجاور. استخدم الترتيب الذي يتناسب مع نمط وصولك للحصول على أفضل أداء للذاكرة المؤقتة.
س متى يجب استخدام معامل
out=؟ج عندما تقوم بالعديد من العمليات على نفس المصفوفة الكبيرة،
out= يتجنب تخصيص ذاكرة جديدة لكل عملية. استخدمه في الحلقات وخطوط الأنابيب.📖 ملخص
- المتجهة هي قاعدة الأداء رقم 1 — تجنب حلقات Python بأي ثمن
- استخدم معامل
out=للعمليات في المكان لتجنب التخصيصات - C-contiguous (صف-رئيسي) هو الافتراضي؛ طابق نمط الوصول مع تخطيط الذاكرة
- قس الأداء باستخدام
%timeitفي Jupyter أوtime.perf_counter()في النصوص - الأخطاء الشائعة: الحلقات، النسخ غير الضرورية، الذاكرة غير المتجاورة بعد التحويل
📝 تمارين
-
مبتدئ (الصعوبة ⭐): قارن الوقت اللازم لحساب
a + bلـ 10 ملايين عنصر باستخدام حلقة مقابل المتجهة. سجّل عامل التسريع. -
متوسط (الصعوبة ⭐⭐): قارن وقت
a + bمع وبدونout=a. قس الفرق في overhead التخصيص. -
متقدم (الصعوبة ⭐⭐⭐): أنشئ مصفوفة كبيرة، حولها، ثم اجمع على طول محور. قارن أداء المصفوفة المحولة مقابل نسخة متجاورة. اشرح الفرق.