كيفية الحصول على 1.5 TFlops من أداء FP32 على نواة واحدة لوحدة المعالجة المركزية M1
جوت - 1.5tflop_m1
# كيفية الحصول على 1.5 TFlops من أداء FP32 على نواة M1 CPU واحدة
* بواسطة [bvasti] (https://twitter.com/bvasti) ([mastodon] (https://sigmoid.social/@bvasti)) *
****
إذا كنت في السوق لتدريب شبكات عصبية حديثة كبيرة ،
لن تكون هذه المشاركة ذات صلة حقًا ،
نظرًا لأنه أبطأ 100 مرة من A100 (156TFlops).
إذن ما هي قيمة 1.5 TFlops على الأرض؟
- يعمل هذا على نواة واحدة من جهاز MacBook Air 2020 الذي يعمل بالبطارية
- يعمل مع زمن انتقال يبلغ حوالي 0.5 * نانوثانية * لكل تعليمات
نحن لا نعمل في مجال المسرعات السميكة أو نوى موتر GPU.
نحن نتحدث عن أداء الجبر الخطي في العالم الحقيقي
يعيش ** دورة واحدة ** بعيدًا عن سجلات وحدة المعالجة المركزية.
الغريب أن أبل أخفت هذا عنا!
في هذه المقالة ، سنتعرف على بعض التعليمات البرمجية لرفع تلك الستارة.
كل التعليمات البرمجية تستخدم رأس `aarch.h` في كورسيكس
مستودع رائع: https://github.com/corsix/amx
## ما هو معالج AMX المشترك؟
انها في الأساس SIMD على المنشطات. فارق مهم هو
أن نسبة AMX: CPU ليست 1: 1 ؛ لا تحتوي كل النوى على معالج AMX الخاص بها.
فيما يلي الأحجام التي يمكن للمرء استخدامها لتحميل أو تخزين القيم:
! [] (https://i.imgur.com/3gimUQ7.png)
* الحد الأدنى * واسع مثل تسجيل AVX512 الكامل.
ولكن من أين يتم تحميل هذه القيم أو تخزينها؟
من الواضح أن مثل هذه الأحجام ستستهلك ملف تسجيل النيون بأكمله بسرعة كبيرة.
حسنًا ، هناك ملف تسجيل منفصل لـ AMX فقط وهذا غريب نوعًا ما.
يتم تقسيم السجلات إلى مجموعات: X و Y و Z.
لكل تعليمات ، تحتوي المجموعتان X و Y على إدخالات ومجموعة Z.
يحمل النواتج.
! [] (https://i.imgur.com/PUTfqIY.png)
كما نرى ، X و Y كبيرتان جدًا! كيلو بايت كامل بينهما.
لكن Z يأخذ الكعكة وأكثر:
! [] (https://i.imgur.com/Xqtu1xG.png)
(* المفسد: يمكن ملء 1024 بايت (1/4 من سجلات Z)
بتعليمات AMX واحدة. *)
إذن كيف ننتقل من X و Y إلى Z؟
حسنًا ، عدد الطرق كبير جدًا لدرجة أنه لا يتناسب تمامًا
في ترميز ISA. وهكذا ، قررت شركة آبل ترميز معظم
معلومات التعليمات في سجل للأغراض العامة.
اتضح أن هذا رائع للعمل معه ، لأنه يسمح بذلك
* تكوين وقت التشغيل * (سريعًا) للرمز الذي يعمل على AMX.
الغرض من هذه المقالة هو ببساطة استخدام المعالج المشترك بأكبر قدر ممكن من الكفاءة.
هناك تعليمات متجه إلى متجه والتي ستنتج متجهات بنفس الطول ،
لكنها بعيدة كل البعد عن تشبع القدرات الحاسوبية لهذه الشريحة.
بدلاً من ذلك ، سيتعين علينا استخدام منتج خارجي لإنجاز الأمور حقًا.
ما هو المنتج الخارجي؟
بافتراض أن لديك متجهي إدخال $ \ mathbf {u} $ و $ \ mathbf {v} $:
$$
\ mathbf {u} = \ begin {bmatrix} u_1 \\\\ u_2 \\\\ \ vdots \\\\ u_m \ end {bmatrix} ،
\ رباعي
\ mathbf {v} = \ start {bmatrix} v_1 \\\\ v_2 \\\\ \ vdots \\\\ v_n \ end {bmatrix}
$$
المنتج الخارجي هو المصفوفة التي تحتوي على منتج
كل المجموعات الزوجية الممكنة لعناصرها.
(هذا يعطي بعض الدلائل عن سبب تسجيل مجموعة Z.
أكبر بكثير من X و Y.)
$$
\ mathbf {u} \ otimes \ mathbf {v} =
\ ابدأ {bmatrix}
u_1v_1 & \ Points & u_1v_n \\\\
u_2v_1 & \ Points & u_2v_n \\\\
\ vdots & \ ddots & \ vdots \\\\
u_mv_1 & \ dots & u_mv_n
\ نهاية {bmatrix}
$$
على شريحة AMX ، يتلخص هذا في تعليمات بسيطة للغاية
يبدو كثيرًا مثل هذا:
! [] (https://i.imgur.com/yQa4cdq.png)
وهناك علامة يمكنك تعيينها لتجميعها أيضًا من ملف
النتيجة السابقة:
! [] (https://i.imgur.com/MPsmwnX.png)
بذلك ، لدينا كل ما نحتاجه لكتابة ضرب المصفوفة:
بشكل متكرر تحميل 16 تعويمًا من مصفوفات الإدخال الخاصة بنا وتجميعها
المنتجات الخارجية في إخراج 16x16. لا يهم بعد التخفيض K!
لنبسط المسألة ونبدل ضرب المصفوفة ضمنيًا.
سيحتوي "A" و "B" (مدخلاتنا) على "K" (بُعد الاختزال الخاص بنا)
كبعد * رئيسي *. لا يهم حقًا في الممارسة ،
لكنه يبسط الكود لدينا كثيرًا.
إليك مرجع يمكننا استخدامه للتحقق من حلنا المقترح:
قسم غير مصنف في الرياض
Reference_16x16xK باطل (عائم * A ، عائم * B ، عائم * C ، uint64_t K) {
لـ (uint32_t m = 0 ؛ م <16 ؛ ++ م) {
لـ (uint32_t n = 0 ؛ n <16 ؛ ++ n) {
ج [ن * 16 + م] = 0 ؛
لـ (uint32_t k = 0؛ k
What's Your Reaction?