Occa: ماكرو مؤهل kernel جديد

تم إنشاؤها على ٢ أبريل ٢٠٢٠  ·  22تعليقات  ·  مصدر: libocca/occa

غالبًا ما يكون من المفيد إعطاء مترجم kernel بعض المعلومات الإضافية حول عدد سلاسل الرسائل (عناصر العمل) في كتلة سلسلة الرسائل (مجموعة العمل). على سبيل المثال ، يمكننا إعطاء مترجم HIP حدًا أعلى لعدد الخيوط في كتلة مؤشر ترابط (على سبيل المثال 1024) على النحو التالي:

__launch_bounds__(1024) __global__ void fooKernel(...) { ... }

في الواقع بالنسبة لإصدار HIP الحالي ، للأسف ، يجب تحديد ذلك عندما يتجاوز حجم كتلة مؤشر الترابط 256 (انظر https://github.com/ROCm-Developer-Tools/HIP/issues/1310)

CUDA لها نفس السمة أيضًا. هناك أيضًا وسيط إضافي لمؤهل حدود التشغيل للحد الأدنى لعدد كتل الخيوط (https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#launch-bounds).

تختلف مؤهلات kernel في OpenCL قليلاً (انظر 6.7.2 من https://www.khronos.org/registry/OpenCL/specs/2.2/pdf/OpenCL_C.pdf)

الاقتراح v1 - في عالم مثالي عندما نعرف حجم كتلة الخيط في وقت التجميع ، ستضيف OCCA مؤهلات kernel لحدود الإطلاق المناسبة (CUDA ، HIP) أو تلميح حجم مجموعة العمل (OpenCL).

الاقتراح v2 - إذا كان تنفيذ الاقتراح v1 معقدًا جدًا ، فسيكون من الرائع إضافة سمة okl لحدود التشغيلqualifier ("الأحجام الداخلية" ، B) حيث يمكن أن يكون B هو تعريف مترجم. سيتم توسيع هذا إلى __launch_bounds __ (قيمة B) لـ CUDA / HIP أو __ سمة __ ((work_group_size_hint (قيمة B))) لـ OpenCL. سيكون البديل متعدد التعتيم مفيدًا أيضًا.

feature

ال 22 كومينتر

نظرًا لأن هذا يبدو خاصًا بـ HIP / CUDA / OpenCL ، فماذا عن تمريره كخاصية بناء؟

  addVectors = device.buildKernel("addVectors.okl",
                                  "addVectors",
                                  "launch_bounds: 1024");

سيكون ذلك جيدا على ما أعتقد.

أنا فقط جريت في هذا. أنا سعيد بالمساهمة في هذا ولكن لدي سؤال. هل تعرف OCCA أبعاد الحلقة الداخلية في وقت JIT؟

أعتقد أنه يمكن تمرير أبعاد الحلقة كوسائط kernel.

هذا صحيح ، لكنني لا أفهم لماذا هذا مفيد. أسأل كيف يمكن لـ Occa إرسال سمة __launch_bounds__ في وقت JIT. يحتاج إلى معرفة أبعاد الحلقة في تلك المرحلة. هذا متعامد مع حجج النواة ، أليس كذلك؟

إنه يقول إن تعتيم الحلقات الداخلية يمكن تمريره كوسيطات ، أي أننا لا نعرف بالضرورة أبعاد كتلة الخيط في وقت التحويل البرمجي JIT.

صحيح. أرى ما تقوله الآنtcew. نقطة جيدة.

هذا ما كان يدور في خلدي:

<strong i="6">@kernel</strong> void runtimeArgs(const int B,
                         const int T,
                         const int N,
                         const float *x,
                         const float *y,
                         float *xPy) {
  for (int b=0;b<B;++b;@outer(0)){
    for (int t=0;t<T;++t;@inner(0)){

      if(b==0 && t==0) printf("B=%d, T=%d\n", B, T);

      int  n = t + T*b;
      if(n<N){
        xPy[n] = x[n] + y[n];
      }
    }
  }
}

من الواضح أن OCCA لا يمكنها معرفة حدود الحلقة العددية في وقت JIT.

ومع ذلك ، فإنه يقوم بإنشاء مشغل يحدد أبعاد شبكة مؤشر الترابط:

extern "C" void runtimeArgs(occa::modeKernel_t * *deviceKernel,
                            const int & B,
                            const int & T,
                            const int & N,
                            occa::modeMemory_t * x,
                            occa::modeMemory_t * y,
                            occa::modeMemory_t * xPy) {
  {
    occa::dim outer, inner;
    outer.dims = 1;
    inner.dims = 1;
    int b = 0;
    outer[0] = B - 0;
    int t = 0;
    inner[0] = T - 0;
    occa::kernel kernel(deviceKernel[0]);
    kernel.setRunDims(outer, inner);
    kernel(B, T, N, x, y, xPy);
  }
}

وبالتالي يمكن للمستخدم تحديد أي حجم لحدود الحلقة في وقت التشغيل.

عبرت المشاركات.

يستخدم SYCL بناء جملة مماثل:

sycl::range<2> global_range(Bx*Tx,By*Ty);
sycl::range<2> local_range(Tx,Ty);
sycl::nd_range<2> kernel_range(global_range, local_range);

device_queue.submit([&](sycl::handler &cgh) {
    ...
  cgh.parallel_for(kernel_range, kernel);
});

(المخازن المؤقتة لها نطاق مرتبط بها ، مما يتجنب الحاجة إلى المرور في N)

صيغة SYCL sycl::range مقتبسة من OpenCL ، والتي هي نفسها مقتبسة من CUDA.

في المثال الخاص بك ، تكون مواصفات أبعاد الخيط منفصلة عن جسم حلقات for المتوازية.

تم تصميم صيغة OCCA OKL خصيصًا لجلب أبعاد الحلقة ورمز الجسم إلى صيغة توازي مألوفة أكثر في بناء جملة الحلقة.

for (int b=0;b<B;++b;@outer(0)){ /*  grid dimension defined here */
    for (int t=0;t<T;++t;@inner(0)){ /* thread block dimension defined here */

      if(b==0 && t==0) printf("B=%d, T=%d\n", B, T);

      int  n = t + T*b;
      if(n<N){
        xPy[n] = x[n] + y[n];
      }
    }
  }

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

كانت إنشاءات نواة OKL اختيارًا مقصودًا ولدت من الاضطرار إلى شرح بنية نواة CUDA / OpenCL بشكل متكرر ، ومعلمات تشغيل kernel ، وفلسفة خيوط kernel عند تدريب الأشخاص.

هل يحتوي SYCL على تلميح مماثل لحجم كتلة مؤشر الترابط مثل CUDA / HIP __launch_bounds__ الذي نناقشه هنا؟

سؤال جيد. لقد راجعت معيار SYCL (v1.2.1) مرتين لمعرفة ذلك. يتم دعم أي سمات متوفرة في OpenCL C ويمكن منحها باستخدام محدد السمة C ++ 11 باستخدام مساحة الاسم cl . على سبيل المثال ، __attribute__(((reqd_work_group_size(n))) في OpenCL C يعادل [[cl::reqd_work_group_size(n)]] في SYCL.

هناك نوعان من النكهات المتاحة لتحديد حجم مجموعة سلاسل الرسائل: work_group_size_hint(n) هو الإصدار الناعم - مما يشير إلى أن حجم مجموعة سلاسل الرسائل سيكون n - بينما req_work_group_size(n) هو مطلب صارم .

بعض خيارات OCCA:

  1. إضافة دالة عضو "innerDimHint" إلى فئة occa :: kernel ، والتي تفرض إعادة ترجمة (إذا لم تكن موجودة بالفعل في التجزئة) مع تلميح خافت لمؤشر الترابط لـ CUDA / OpenCL / HIP.

  2. أضف بعض المنطق داخل المشغل لتشغيل إعادة التحويل البرمجي عند تحديد حجم صفيف مؤشر ترابط جديد. قد يتم تشغيل / إيقاف هذه الميزة بواسطة بعض OCCA_ * تحديد.

كلاهما يمكن أن يتم بطريقة متوافقة مع الإصدارات السابقة.

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

بالنسبة إلى CUDA و HIP ، يعد __launch_bound__ مجرد تلميح ، لذلك ربما يكون أقرب إلى OpenCL work_group_size_hint . يتم استخدامه فقط لإخبار المترجم بعدد السجلات التي يمكن أن يفترض أنه سيكون متاحًا لكل سلسلة رسائل في الكتلة. إذا انتهك المستخدم حد التشغيل ، فليس بالضرورة أن يكون خطأ ، لأن النواة قد لا تستخدم التسجيل بشكل كبير. في حالة انتهاك المستخدم لقيود الإطلاق وعدم وجود سجل كافٍ بالفعل ، فإن وقت التشغيل سيؤدي إلى حدوث خطأ يجب على OCCA اكتشافه.

عند تشغيل موالف تلقائي ، لاحظت أخطاء تجزئة لـ OCCA: نواة HIP التي تتطلب الكثير من LDS أو REG. كنت متشككًا في أن يتسبب HIP في حدوث أخطاء يتم اكتشافها. نأمل أن يكون قد تم إصلاحه الآن.

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

لمعلوماتك ، لقد كنت سعيدًا كقبرة تقوم فقط بتحديد حدود حلقة وقت التشغيل الصريحة من كود المضيف عبر الاستبدال الثابت للماكرو في كود نواة OKL (على سبيل المثال ، K_blockDim للخارج ، K_threadDim للداخل) عبر الدعائم kernel. بالطبع ، لدي كود API الخاص بي الذي يستعلم عن خصائص وحالة وحدة معالجة الرسومات. أستخدم هذه المعلومات لحساب حدود الحلقة لتمريرها. نعم ، يتسبب هذا النوع من الأشياء الديناميكية في إعادة تجميع JIT أحيانًا أثناء وقت التشغيل ، ولكن في حالتي يكون ذلك نادرًا لأنني تتغير عتبة الخطوة ، لذلك تظل حدود الحلقة الفعلية كما هي أو تقع في مجموعة مشتركة لنواة معينة. أنا أيضًا أستخدم الترجمة المسبقة لبعض نواة بلدي بحيث يقلل أيضًا من إعادة تجميع JIT.

إن تحديد حدود الحلقة هذه بشكل صريح في وقت التشغيل يسهل أيضًا تحديد حجم صفيفات الذاكرة المحلية لوحدة معالجة الرسومات في وقت التشغيل في كود OKL.

يبدو أن هذه الميزة ستكون مهمة:
https://rocmdocs.amd.com/ar/latest/Current_Release_Notes/Current-Release-Notes.html#performance -impact-for-kernel-launch-bound-attribute

مع الاعتذار عن التطبيق الخرقاء ، هذا حل بديل أستخدمه لنواة في libparanumal تتطلب سلاسل Np (> 256):

occa::properties kernelInfo; 
...
 if(platform.device.mode()=="HIP"){
      char newflag[BUFSIZ];
      sprintf(newflag, " --gpu-max-threads-per-block=%d", mesh.Np);
      kernelInfo["compiler_flags"] += newflag;
    }

اقترح نويل تشالمرز استخدام علامة مجمع hipcc لتحديد حدود الإطلاق. التنفيذ الإجمالي لي.

من المهم توخي الحذر عند القيام بذلك لأنه من غير الواضح ما يحدث إذا خالفت النواة الحد الأقصى.

لتجنب استخدام حدود غير مناسبة ، أقوم بإنشاء نسخ منفصلة من الكائن occa::properties للنواة التي تستخدم عددًا أقصى لعدد سلاسل الرسائل.

من المحتمل أنه في المستقبل ، سيصبح انتهاك حدود الإطلاق خطأً في وقت التشغيل.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

tcew picture tcew  ·  10تعليقات

amikstcyr picture amikstcyr  ·  11تعليقات

jeremylt picture jeremylt  ·  12تعليقات

dmed256 picture dmed256  ·  4تعليقات

awehrfritz picture awehrfritz  ·  7تعليقات