الدرس 26: ضرب المصفوفات — الـ kernel الساذج على GPU
ضرب المصفوفات C = A * B يحسب كل عنصر C[row][col] كمجموع لحواصل ضرب على امتداد البُعد المشترك K: C[row][col] = مجموع على k لـ A[row][k] * B[k][col]. الطريقة المباشرة (الساذجة) لتوزيع هذا الحساب على الـ GPU هي تخصيص thread واحد لكل عنصر من C. يحسب الـ thread قيمتَي row و col الخاصتين به من المؤشرات ثن
تخيّل شبكة ضرب ضخمة يجب ملؤها. في النسخة الساذجة يكون كل عامل مسؤولًا عن خلية واحدة فقط، لكن لملئها يمشي إلى مستودع بعيد ويحضر صفًّا كاملًا وعمودًا كاملًا — والعامل المجاور له يمشي إلى المستودع نفسه ويحضر الصف نفسه مرة أخرى. الكثير من الرحلات المهدورة.
- تعيين thread إلى عنصر
- كل thread يملك عنصرًا واحدًا من C. يحسب row و col من المؤشرات ثنائية الأبعاد ويُنتج C[row][col].
- حلقة k (حاصل الضرب النقطي)
- الحلقة على k التي تجمع sum += A[row*K + k] * B[k*N + col]. إنها حاصل ضرب نقطي بين صف من A وعمود من B.
- المؤشر المسطَّح row-major
- تُخزَّن مصفوفة بحجم rows x cols صفًّا بعد صف، بحيث يقع العنصر [r][c] في العنوان r*cols + c.
- قراءات زائدة من الذاكرة العامة
- في النسخة الساذجة، يُقرأ كل عنصر من A و B من الذاكرة العامة مرارًا وتكرارًا بواسطة threads مختلفة، مما يستنزف عرض النطاق.