
مقدمة
Emu هي لغة برمجة لرسومات الرسومات عالية المستوى يمكن تضمينها في الكود العادي بلغة برمجة نظام Rust .
سوف تركز هذه المقالة على بناء جملة Emu ، وميزاته ، وسوف تعرض أيضًا بعض الأمثلة التوضيحية لاستخدامه في الكود الحقيقي.
تركيب
- تحتاج المكتبة التي تبحث عنها إلى تبعية OpenCL خارجية. تحتاج إلى تثبيت برنامج التشغيل المناسب لجهازك.
Cargo.toml
النص أدناه. سيؤدي ذلك إلى تنزيل أحدث الإصدارات المتاحة (إذا كنت بحاجة إلى تجميع محدد ، ثم بدلاً من *
ضع الإصدار الذي تحتاجه):
[dependencies] em = "*" // Emu ocl = "*" // OpenCL
بناء الجملة
بناء جملة Emu بسيط للغاية ، لأن هذه اللغة مخصصة فقط لكتابة وظائف kernel التي يتم تجميعها في OpenCL .
أنواع البيانات
تحتوي لغة Emu على تسعة أنواع من البيانات تشبه تلك الموجودة في Rust. فيما يلي جدول بهذه الأنواع من البيانات:
المتغيرات
يتم التصريح عن المتغيرات باستخدام الكلمة الرئيسية let
، والتي تقع خلف المعرّف والنقطتين ونوع البيانات وعلامة المساواة والقيمة المخصصة والفاصلة المنقوطة.
let age: i32 = 54; let growth: f32 = 179.432; let married: bool = true;
تحويل
يتم تحويل أنواع البيانات البدائية باستخدام العامل الثنائي as
يلي ، بعد النوع المستهدف. ألاحظ أن النوع المستهدف يمكن أن يكون أيضًا وحدة قياس (انظر القسم التالي):
let width: i16 = 324; let converted_width: i64 = width as i64;
وحدات القياس
تسمح لك لغة Emu بمعالجة الأرقام كوحدات قياس ، والتي تم تصميمها لتبسيط الحسابات العلمية. في هذا المثال ، length
تعريف length
المتغير مبدئيًا بالمتر ، ولكن بعد ذلك يتم إضافة وحدات قياس أخرى إليه:
let length: f32 = 3455.345;
الثوابت المعرفة مسبقا
يوجد لدى Emu مجموعة من الثوابت المحددة مسبقًا والتي يمكن استخدامها في الممارسة العملية. أدناه هو الجدول المقابل.
كما يتم تحديد الثوابت المقابلة للبيانات العلمية. يمكنك العثور على الجدول الذي يتكون من هذه الثوابت هنا .
البيانات الشرطية
تشبه عبارات Emu الشرطية العبارات المقابلة في Rust. تستخدم التعليمة البرمجية التالية بنيات شرطية:
let number: i32 = 2634; let satisfied: bool = false; if (number > 0) && (number % 2 == 0) { satisfied = true; }
للحلقات
يتم تعريف رأس حلقة For كـ for NUM in START..END
، حيث NUM
هو متغير يأخذ قيمًا من النطاق [START; END)
[START; END)
خلال وحدة.
let sum: u64 = 0; for i in 0..215 { sum += i; }
بينما الحلقات
يتم تعريف عنوان حلقة في while (CONDITION)
، حيث CONDITION
هو الشرط للحلقة للمتابعة إلى التكرار التالي. يشبه هذا الرمز المثال السابق:
let sum: u64 = 0; let idx: i32 = 0; while (idx < 215) { sum += idx; idx += 1; }
حلقات لا نهاية لها
لا تحتوي الحلقات اللانهائية على شرط خروج صريح ويتم تعريفها بواسطة الكلمة الأساسية loop
. ومع ذلك ، يمكن متابعتها أو مقاطعتها بواسطة break
ومتابعة العبارات (مثل النوعين الآخرين من الحلقات).
let collapsed: u64 = 1; let idx: i32 = 0; loop { if idx % 2 == 0 { continue; } sum *= idx; if idx == 12 { break; } }
العودة من الوظيفة
كما هو الحال في جميع لغات البرمجة الأخرى ، فإن return
هي ناتج الوظيفة الحالية. يمكنه أيضًا إرجاع قيمة معينة إذا كان توقيع الوظيفة (انظر الأقسام التالية) يسمح بذلك.
let result: i32 = 23446; return result;
المشغلين الآخرين
- عوامل التشغيل المتاحة:
=
، +=
، -=
، *=
، /=
، %=
، &=
، ^=
، <<=
، >>=
؛ - عامل الفهرس هو
[IDX]
؛ - استدعاء المشغل -
(ARGS)
؛ - العوامل الأحادية:
*
للتراجع ،! لعكس البيانات المنطقية ، -
لإنكار الأرقام ؛ - العوامل الثنائية:
+
، -
، *
، /
، %
، &&
، ||
&
، |
، ^
، >>
، <<
، >
، <
، >=
، <=
، ==
، ==
!=
.
وظائف
هناك ثلاثة أجزاء من الوظائف في Emu: المعرف والمعلمات ونص الوظيفة ، وتتألف من سلسلة من التعليمات القابلة للتنفيذ. النظر في وظيفة إضافة رقمين:
add(left f32, right f32) f32 { return left + right; }
كما قد تلاحظ ، تقوم هذه الوظيفة بإرجاع مجموع الوسيطتين الذي تم تمريرها إليها باستخدام نوع البيانات f32
.
مساحات العنوان
يتوافق كل معلمة للدالة مع مساحة عنوان محددة . بشكل افتراضي ، تتوافق جميع المعلمات مع مساحة __private__
.
تشير إضافة البادئات global_
و local_
إلى معرّف المعلمة بوضوح إلى مساحة العنوان الخاصة بها.
تنصح الوثائق باستخدام البادئة global_
لجميع المتجهات وليس بادئة أي شيء آخر.
وظائف مدمجة
يوفر Emu مجموعة صغيرة من الوظائف المدمجة (مأخوذة من OpenCL) التي تتيح لك إدارة بيانات GPU:
get_work_dim()
- إرجاع عدد الأبعاد ؛get_global_size()
- إرجاع عدد العناصر العالمية لبعد معين ؛get_global_id()
- إرجاع المعرف الفريد للعنصر الخاص بالبعد المحدد ؛get_global_size()
- إرجاع عدد العناصر العالمية لبعد معين ؛get_local_id()
- إرجاع معرف فريد لعنصر محلي داخل مجموعة عمل محددة لبعد معين ؛get_num_groups()
- إرجاع عدد مجموعات العمل لبعد معين ؛get_group_id()
- إرجاع معرف فريد لمجموعة العمل.
في رمز التطبيق ، ستجد في أغلب الأحيان التعبير get_global_id(0)
، والذي يُرجع الفهرس الحالي لعنصر المتجه المرتبط get_global_id(0)
إلى وظيفة kernel.
تنفيذ التعليمات البرمجية
النظر في بناء جملة لاستدعاء وظائف Emu من رمز الصدأ العادية. على سبيل المثال ، سوف نستخدم دالة تضاعف جميع عناصر المتجه برقم معين:
use em::emu; emu! { multiply(global_vector [f32], scalar f32) { global_vector[get_global_id(0)] *= scalar; } }
لترجمة هذه الوظيفة إلى كود OpenCL ، تحتاج إلى وضع توقيعها في build!
الماكرو build!
على النحو التالي:
use em::build;
تأتي الإجراءات الإضافية لاستدعاء وظائف Emu التي كتبتها من كود Rust. لا يمكن أن يكون أسهل:
fn main() { let vector = vec![0.4445, 433.245, 87.539503, 2.0]; let result = multiply(vector, 2.0).unwrap(); dbg!(result); }
مثال التطبيق
يأخذ هذا البرنامج عدديًا باعتباره الوسيطة الأولى ، حيث من الضروري ضرب الوسائط التالية. ستتم طباعة الموجه الناتج إلى وحدة التحكم:
use em::{build, emu};
يمكنك تنفيذ هذا الكود باستخدام أمر cargo run -- 3 2.1 3.6 6.2
. النتيجة الناتجة تلبي التوقعات:
[src/main.rs:33] result = [ 6.2999997, 10.799999, 18.599998, ]
رابط OpenCL
كما ذكرنا سابقًا ، Emu هو مجرد تجريد على OpenCL ، وبالتالي لديه القدرة على التفاعل مع صندوق ocl. الكود أدناه مأخوذ من مثال في المستودع الرسمي :
use em::emu;
اكتمال
أتمنى أن تستمتعوا بالمقال. يمكنك الحصول على إجابة سريعة على أسئلتك في الدردشة باللغة الروسية في Rust ( إصدار للمبتدئين ).