সি ++ সহ জিপিইউ প্রোগ্রামিং

Gpu Programming With C



এই গাইডে, আমরা C ++ দিয়ে GPU প্রোগ্রামিংয়ের ক্ষমতা অন্বেষণ করব। ডেভেলপাররা C ++ এর সাথে অবিশ্বাস্য পারফরম্যান্স আশা করতে পারে, এবং নিম্ন স্তরের ভাষা দিয়ে GPU এর অভূতপূর্ব ক্ষমতা অ্যাক্সেস করলে বর্তমানে উপলব্ধ দ্রুততম গণনার কিছু পাওয়া যাবে।

প্রয়োজনীয়তা

যদিও লিনাক্সের একটি আধুনিক সংস্করণ চালাতে সক্ষম যেকোনো মেশিন একটি C ++ কম্পাইলারকে সমর্থন করতে পারে, এই ব্যায়ামটি অনুসরণ করার জন্য আপনার একটি NVIDIA- ভিত্তিক GPU প্রয়োজন হবে। আপনার যদি জিপিইউ না থাকে, তাহলে আপনি অ্যামাজন ওয়েব সার্ভিসেস বা আপনার পছন্দের অন্য ক্লাউড প্রোভাইডারে একটি জিপিইউ-চালিত উদাহরণ স্পিন করতে পারেন।







আপনি যদি একটি ফিজিক্যাল মেশিন বেছে নেন, দয়া করে নিশ্চিত করুন যে আপনার NVIDIA মালিকানা ড্রাইভার ইনস্টল আছে। আপনি এখানে এর জন্য নির্দেশাবলী খুঁজে পেতে পারেন: https://linuxhint.com/install-nvidia-drivers-linux/



ড্রাইভার ছাড়াও, আপনার CUDA টুলকিট লাগবে। এই উদাহরণে, আমরা উবুন্টু 16.04 এলটিএস ব্যবহার করব, কিন্তু নিচের ইউআরএলে বেশিরভাগ প্রধান বিতরণের জন্য ডাউনলোডগুলি উপলব্ধ রয়েছে: https://developer.nvidia.com/cuda-downloads



উবুন্টুর জন্য, আপনি .deb ভিত্তিক ডাউনলোড বেছে নেবেন। ডাউনলোড করা ফাইলে ডিফল্টরূপে .deb এক্সটেনশন থাকবে না, তাই আমি এটির নাম পরিবর্তন করে শেষে একটি .deb রাখার পরামর্শ দিচ্ছি। তারপরে, আপনি এর সাথে ইনস্টল করতে পারেন:





sudo dpkg -আইpackage-name.deb

আপনাকে সম্ভবত একটি জিপিজি কী ইনস্টল করার জন্য অনুরোধ করা হবে, এবং যদি তা হয় তবে এটি করার জন্য প্রদত্ত নির্দেশাবলী অনুসরণ করুন।

একবার আপনি এটি সম্পন্ন করলে, আপনার সংগ্রহস্থল আপডেট করুন:



sudo apt-get update
sudo apt-get installঅলৌকিক ঘটনা-এবং

একবার হয়ে গেলে, আমি সবকিছু পুনরায় লোড করা নিশ্চিত করার জন্য রিবুট করার পরামর্শ দিই।

জিপিইউ বিকাশের সুবিধা

CPU গুলি অনেকগুলি ইনপুট এবং আউটপুট পরিচালনা করে এবং কর্মসূচির একটি বিশাল ভাণ্ডার ধারণ করে যা কেবলমাত্র প্রোগ্রামের প্রয়োজনের বিস্তৃত ভাণ্ডার মোকাবেলা করার জন্য নয় বরং বিভিন্ন হার্ডওয়্যার কনফিগারেশন পরিচালনার জন্য। তারা মেমরি, ক্যাশিং, সিস্টেম বাস, সেগমেন্টিং এবং আইও কার্যকারিতা পরিচালনা করে, যা তাদের সমস্ত ব্যবসার একটি জ্যাক করে তোলে।

জিপিইউগুলি বিপরীত - এগুলিতে অনেকগুলি পৃথক প্রসেসর রয়েছে যা খুব সাধারণ গাণিতিক ক্রিয়াকলাপগুলিতে মনোনিবেশ করে। এই কারণে, তারা সিপিইউগুলির চেয়ে অনেকগুণ দ্রুত কাজ প্রক্রিয়া করে। স্কেলার ফাংশনে বিশেষীকরণের মাধ্যমে (একটি ফাংশন যা এক বা একাধিক ইনপুট নেয় কিন্তু শুধুমাত্র একটি আউটপুট ফেরত দেয়), তারা চরম বিশেষায়নের খরচে চরম কর্মক্ষমতা অর্জন করে।

উদাহরণ কোড

উদাহরণ কোডে, আমরা একসঙ্গে ভেক্টর যোগ করি। আমি গতির তুলনার জন্য কোডের একটি CPU এবং GPU সংস্করণ যোগ করেছি।
gpu-example.cpp নীচের বিষয়বস্তু:

#অন্তর্ভুক্ত 'cuda_runtime.h'
#অন্তর্ভুক্ত
#অন্তর্ভুক্ত
#অন্তর্ভুক্ত
#অন্তর্ভুক্ত
#অন্তর্ভুক্ত

টাইপডেফঘন্টার::ক্রোনো::high_resolution_clockঘড়ি;

#আইটিইআর 65535 নির্ধারণ করুন

// ভেক্টরের অ্যাড ফাংশনের CPU ভার্সন
অকার্যকরvector_add_cpu(int *প্রতি,int *খ,int *গ,intn) {
intআমি;

// ভেক্টর উপাদান a এবং b ভেক্টর c যোগ করুন
জন্য (আমি= 0;আমি<n; ++আমি) {
[আমি] =প্রতি[আমি] +[আমি];
}
}

// ভেক্টরের অ্যাড ফাংশনের জিপিইউ ভার্সন
__ বিশ্বব্যাপী__অকার্যকরvector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intn) {
intআমি=threadIdx।এক্স;
// লুপের জন্য প্রয়োজন নেই কারণ CUDA রানটাইম
// এটি ITER বার থ্রেড করবে
gpu_c[আমি] =gpu_a[আমি] +gpu_b[আমি];
}

intপ্রধান() {

int *প্রতি,*খ,*;
int *gpu_a,*gpu_b,*gpu_c;

প্রতি= (int *)malloc(আইটিইআর* আকার(int));
= (int *)malloc(আইটিইআর* আকার(int));
= (int *)malloc(আইটিইআর* আকার(int));

// আমাদের জিপিইউতে অ্যাক্সেসযোগ্য ভেরিয়েবল দরকার,
// তাই cudaMallocManaged এইগুলি প্রদান করে
cudaMallocManaged(&gpu_a, ITER* আকার(int));
cudaMallocManaged(&gpu_b, ITER* আকার(int));
cudaMallocManaged(&gpu_c, ITER* আকার(int));

জন্য (intআমি= 0;আমি<আইটিইআর; ++আমি) {
প্রতি[আমি] =আমি;
[আমি] =আমি;
[আমি] =আমি;
}

// CPU ফাংশন কল করুন এবং সময়
স্বয়ংক্রিয়cpu_start=ঘড়ি::এখন();
vector_add_cpu(a, b, c, ITER);
স্বয়ংক্রিয়cpu_end=ঘড়ি::এখন();
ঘন্টার::খরচ << 'vector_add_cpu:'
<<ঘন্টার::ক্রোনো::সময়কাল_কাস্ট<ঘন্টার::ক্রোনো::ন্যানোসেকেন্ড>(cpu_end-cpu_start)গণনা()
<< 'ন্যানোসেকেন্ডn';

// GPU ফাংশন কল করুন এবং এটি সময়
// ট্রিপল এঙ্গেল বন্ধনী একটি CUDA রানটাইম এক্সটেনশন যা অনুমতি দেয়
// একটি CUDA কার্নেল কলের প্যারামিটার পাস করতে হবে।
// এই উদাহরণে, আমরা ITER থ্রেড দিয়ে একটি থ্রেড ব্লক পার করছি।
স্বয়ংক্রিয়gpu_start=ঘড়ি::এখন();
vector_add_gpu<<<, আইটিইআর>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
স্বয়ংক্রিয়gpu_end=ঘড়ি::এখন();
ঘন্টার::খরচ << 'ভেক্টর_এড_জিপু:'
<<ঘন্টার::ক্রোনো::সময়কাল_কাস্ট<ঘন্টার::ক্রোনো::ন্যানোসেকেন্ড>(gpu_end-gpu_start)গণনা()
<< 'ন্যানোসেকেন্ডn';

// বিনামূল্যে GPU- ফাংশন ভিত্তিক মেমরি বরাদ্দ
চুদা ফ্রি(প্রতি);
চুদা ফ্রি();
চুদা ফ্রি();

// বিনামূল্যে CPU- ফাংশন ভিত্তিক মেমরি বরাদ্দ
বিনামূল্যে(প্রতি);
বিনামূল্যে();
বিনামূল্যে();

প্রত্যাবর্তন 0;
}

মেকফিল নীচের বিষয়বস্তু:

আইএনসি= -আমি/ইউএসআর/স্থানীয়/অলৌকিক ঘটনা/অন্তর্ভুক্ত
এনভিসিসি=/ইউএসআর/স্থানীয়/অলৌকিক ঘটনা/আমি/nvcc
NVCC_OPT= -এসটিডি = সি ++এগারো

সব:
$(এনভিসিসি)$(NVCC_OPT)gpu-example.cpp-অথবাজিপিইউ-উদাহরণ

পরিষ্কার:
-আরএম -ফজিপিইউ-উদাহরণ

উদাহরণটি চালানোর জন্য, এটি কম্পাইল করুন:

তৈরি করা

তারপর প্রোগ্রাম চালান:

/জিপিইউ-উদাহরণ

আপনি দেখতে পাচ্ছেন, CPU সংস্করণ (vector_add_cpu) GPU সংস্করণ (vector_add_gpu) এর তুলনায় যথেষ্ট ধীর গতিতে চলে।

যদি তা না হয়, তাহলে আপনাকে gpu-example.cu তে ITER সংজ্ঞাটি উচ্চতর সংখ্যায় সামঞ্জস্য করতে হতে পারে। এটি GPU সেটআপের সময় কিছু ছোট CPU- নিবিড় লুপের চেয়ে দীর্ঘ হওয়ার কারণে। আমি আমার মেশিনে ভাল কাজ করার জন্য 65535 খুঁজে পেয়েছি, কিন্তু আপনার মাইলেজ ভিন্ন হতে পারে। যাইহোক, একবার আপনি এই থ্রেশহোল্ডটি পরিষ্কার করলে, GPU নাটকীয়ভাবে CPU এর চেয়ে দ্রুততর হয়।

উপসংহার

আমি আশা করি আপনি C ++ দিয়ে GPU প্রোগ্রামিং -এ আমাদের ভূমিকা থেকে অনেক কিছু শিখেছেন। উপরের উদাহরণটি খুব বেশি কাজ করে না, তবে প্রদর্শিত ধারণাগুলি এমন একটি কাঠামো সরবরাহ করে যা আপনি আপনার জিপিইউ -এর শক্তি প্রকাশ করতে আপনার ধারণাগুলি অন্তর্ভুক্ত করতে ব্যবহার করতে পারেন।