Boost.Compute أو الحوسبة المتوازية GPU / CPU. الجزء الأول

المقدمة



مرحبا هبر!



وفقًا لمعاييري ، كنت أكتب كود C ++ لفترة طويلة جدًا ، لكن حتى ذلك الوقت لم أواجه بعد المهام المتعلقة بالحوسبة المتوازية. لم أر مقالاً واحداً عن مكتبة Boost.Compute ، لذلك ستكون هذه المقالة حولها.

كل الأجزاء





المحتوى



  • ما هو boost.compute
  • مشاكل في ربط boost.compute بالمشروع
  • مقدمة إلى boost.compute
  • فصول الحساب الأساسية
  • ابدء
  • خاتمة


ما هو boost.compute



توفر مكتبة c ++ هذه واجهة بسيطة عالية المستوى للتفاعل مع أجهزة الحوسبة متعددة النواة ووحدة معالجة الرسومات. تمت إضافة هذه المكتبة أولاً للتعزيز في الإصدار 1.61.0 وما زالت مدعومة.



مشاكل في ربط boost.compute بالمشروع



وهكذا ، واجهت بعض المشاكل أثناء استخدام هذه المكتبة. كان أحدها أن المكتبة ببساطة لا تعمل بدون OpenCL. يعطي المترجم الخطأ التالي:



صورة



بعد توصيل كل شيء يجب أن يترجم بشكل صحيح.



على حساب مكتبة التعزيز ، يمكن تنزيلها وتوصيلها بمشروع Visual Studio باستخدام مدير الحزم NuGet.



مقدمة إلى boost.compute



بعد تثبيت جميع المكونات المطلوبة ، يمكنك إلقاء نظرة على أجزاء بسيطة من التعليمات البرمجية. للتشغيل الصحيح ، يكفي تمكين وحدة الحساب بهذه الطريقة:



#include <boost/compute.hpp>
using namespace boost;


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



std::vector<float> std_vector(10);
compute::vector<float> compute_vector(std_vector.begin(), std_vector.end(), queue); 
//       ,     .


يمكنك استخدام وظيفة copy () للتحويل مرة أخرى إلى std :: vector:



compute::copy(compute_vector.begin(), compute_vector.end(), std_vector.begin(), queue);


فصول الحساب الأساسية



تتضمن المكتبة ثلاث فئات مساعدة ، وهي كافية للبدء بالحسابات على بطاقة الفيديو و / أو المعالج:



  • compute :: device (سيحدد الجهاز الذي سنعمل معه)
  • compute :: Context (كائن من هذه الفئة يخزن موارد OpenCL ، بما في ذلك مخازن الذاكرة والكائنات الأخرى)
  • compute :: command_queue (يوفر واجهة للتفاعل مع جهاز كمبيوتر)


يمكنك التصريح بهذا الأمر برمته مثل هذا:



auto device = compute::system::default_device(); //     
auto context = compute::context::context(device); //   
auto queue = compute::command_queue(context, device); //   


حتى باستخدام السطر الأول من الكود أعلاه ، يمكنك التأكد من أن كل شيء يعمل كما ينبغي عن طريق تشغيل الكود التالي:



std::cout << device.name() << std::endl; 


وهكذا ، حصلنا على اسم الجهاز الذي سنجري الحسابات عليه. النتيجة (قد يكون لديك شيء مختلف):



صورة



ابدء



دعنا نلقي نظرة على trasform () ونختصر () الدوال عن طريق المثال:



std::vector<float> host_vec = {1, 4, 9};

compute::vector<float> com_vec(host_vec.begin(), host_vec.end(), queue);
//           
//  copy()

compute::vector<float> buff_result(host_vec.size(), context);
transform(com_vec.begin(), com_vec.end(), buff_result.begin(), compute::sqrt<float>(), queue);

std::vector<float> transform_result(host_vec.size());
compute::copy(buff_result.begin(), buff_result.end(), transform_result.begin(), queue);
	
cout << "Transforming result: ";
for (size_t i = 0; i < transform_result.size(); i++)
{
	cout << transform_result[i] << " ";
}
cout << endl;

float reduce_result;
compute::reduce(com_vec.begin(), com_vec.end(), &reduce_result, compute::plus<float>(),queue);

cout << "Reducing result: " << reduce_result << endl;


عند تشغيل الكود أعلاه ، يجب أن ترى النتيجة التالية:



صورة



لقد استقرت على هاتين الطريقتين لأنهما يظهران جيدًا العمل البدائي مع الحسابات المتوازية دون كل شيء غير ضروري.



وهكذا ، تُستخدم وظيفة التحويل () لتغيير مصفوفة من البيانات (أو مصفوفتين ، إذا مررناهما) من خلال تطبيق دالة واحدة على جميع القيم.



transform(com_vec.begin(), 
   com_vec.end(), 
   buff_result.begin(), 
   compute::sqrt<float>(), 
   queue);


دعنا ننتقل إلى تحليل الحجج ، باستخدام الوسيطتين الأوليين ، نقوم بتمرير متجه لبيانات الإدخال ، مع الوسيطة الثالثة نقوم بتمرير مؤشر إلى بداية المتجه الذي سنكتب النتيجة فيه ، مع الوسيطة التالية نشير إلى ما نحتاج إلى القيام به. في المثال أعلاه ، نستخدم إحدى وظائف معالجة المتجهات القياسية ، وهي استخراج الجذر التربيعي. بالطبع ، يمكنك كتابة وظيفة مخصصة ، يوفر لنا التعزيز طريقتين كاملتين ، ولكن هذه هي بالفعل المادة للجزء التالي (إذا كان هناك أي منها على الإطلاق). حسنًا ، كالمعامل الأخير ، قمنا بتمرير كائن من فئة compute :: command_queue ، والتي تحدثت عنها أعلاه.



الوظيفة التالية هي تقليل () ، كل شيء أكثر إثارة للاهتمام هنا. ترجع هذه الطريقة نتيجة تطبيق الوسيطة الرابعة على جميع عناصر المتجه.



compute::reduce(com_vec.begin(), 
   com_vec.end(), 
   &reduce_result, 
   compute::plus<float>(),
   queue);


سأشرح الآن بمثال ، يمكن مقارنة الكود أعلاه بالمعادلة التالية:

1+4+تسع

في حالتنا ، نحصل على مجموع كل العناصر في المصفوفة.



خاتمة



حسنًا ، هذا كل شيء ، أعتقد أن هذا كافٍ لإجراء عمليات بسيطة على البيانات الضخمة. يمكنك الآن استخدام الوظيفة الأولية لمكتبة Boost.compute ، ويمكنك أيضًا منع بعض الأخطاء عند العمل مع هذه المكتبة.



سأكون سعيدا لتلقي ردود فعل إيجابية. شكرا لوقتك.



حظا موفقا للجميع!



All Articles