কোনও জিপিইউ দ্বারা চালিত করার জন্য থ্রেডগুলি কীভাবে সংগঠিত করা হয়?
কোনও জিপিইউ দ্বারা চালিত করার জন্য থ্রেডগুলি কীভাবে সংগঠিত করা হয়?
উত্তর:
যদি কোনও জিপিইউ ডিভাইসটিতে, উদাহরণস্বরূপ, 4 টি মাল্টিপ্রসেসিং ইউনিট রয়েছে এবং তারা প্রতিটি 768 থ্রেড চালাতে পারে: তবে নির্দিষ্ট মুহুর্তে 4 * 768 টির বেশি থ্রেড আসলেই সমান্তরালে চলবে না (যদি আপনি আরও থ্রেডের পরিকল্পনা করেন তবে তারা অপেক্ষা করবে) তাদের পালা)।
থ্রেডগুলি ব্লকগুলিতে সংগঠিত হয়। একটি ব্লক একটি মাল্টিপ্রসেসিং ইউনিট দ্বারা কার্যকর করা হয়। 1 মাত্রা (এক্স), 2 মাত্রা (এক্স, ওয়াই) বা 3 ডিিম ইনডেক্স (এক্স, ওয়াই, জেড) ব্যবহার করে একটি ব্লকের থ্রেডগুলি শোধিত (সূচিকৃত) হতে পারে তবে যে কোনও ক্ষেত্রে x y z <= 768 আমাদের উদাহরণের জন্য (অন্যান্য বিধিনিষেধগুলি প্রযোজ্য) এক্স, ওয়াই, জেড, গাইড এবং আপনার ডিভাইসের ক্ষমতা দেখুন)।
স্পষ্টতই, যদি আপনার 4 * 768 থ্রেডের বেশি প্রয়োজন হয় তবে আপনার 4 টিরও বেশি ব্লক প্রয়োজন। ব্লকগুলি 1D, 2D বা 3 ডি সূচকযুক্তও হতে পারে। জিপিইউতে প্রবেশের জন্য অপেক্ষারত ব্লকের একটি সারি রয়েছে (কারণ, আমাদের উদাহরণস্বরূপ, জিপিইউতে 4 টি মাল্টিপ্রসেসর রয়েছে এবং কেবলমাত্র 4 টি ব্লক একযোগে কার্যকর করা হচ্ছে)।
মনে করুন আমরা একটি পিক্সেল (i, j) প্রক্রিয়াকরণের জন্য একটি থ্রেড চাই।
আমরা প্রতিটি 64 টি থ্রেডের ব্লক ব্যবহার করতে পারি। তারপরে আমাদের 512 * 512/64 = 4096 ব্লক প্রয়োজন (সুতরাং 512x512 থ্রেড থাকতে হবে = 4096 * 64)
2D ব্লকের ব্লকডিম = 8 x 8 (প্রতি ব্লকের 64 টি থ্রেড) থ্রেডগুলি সংগঠিত করা (চিত্রটিকে আরও সহজ করার জন্য সহজ করার জন্য) সাধারণ। আমি এটিকে থ্রেড পেরব্লক বলতে পছন্দ করি।
dim3 threadsPerBlock(8, 8); // 64 threads
এবং 2 ডি গ্রিডডিম = 64 এক্স 64 ব্লক (4096 ব্লক প্রয়োজন)। আমি এটিকে নাম্বার ব্লক করতে পছন্দ করি।
dim3 numBlocks(imageWidth/threadsPerBlock.x, /* for instance 512/8 = 64*/
imageHeight/threadsPerBlock.y);
কার্নেলটি এভাবে চালু হয়:
myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );
শেষ পর্যন্ত: "4096 ব্লকের একটি সারি" এর মতো কিছু থাকবে, যেখানে ব্লকটি তার 64 টি থ্রেড কার্যকর করার জন্য জিপিইউর মাল্টিপ্রসেসরগুলির মধ্যে একটি নির্ধারিত হওয়ার অপেক্ষায় রয়েছে।
কার্নেলে থ্রেড দ্বারা প্রক্রিয়া করা পিক্সেল (i, j) এইভাবে গণনা করা হয়:
uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
ধরুন একটি 9800GT জিপিইউ:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
একটি ব্লকে 512 এর চেয়ে বেশি সক্রিয় থ্রেড থাকতে পারে না তাই __syncthreads
কেবল সীমিত সংখ্যক থ্রেডকে সিঙ্ক্রোনাইজ করতে পারে। যেমন আপনি যদি 600 টি থ্রেড দিয়ে নিম্নলিখিতটি সম্পাদন করেন:
func1();
__syncthreads();
func2();
__syncthreads();
তারপরে কার্নেলটি অবশ্যই দু'বার চালানো হবে এবং কার্যকর করার আদেশটি হবে:
বিঃদ্রঃ:
মূল বিষয়টি হ'ল __syncthreads
একটি ব্লক-ওয়াইড অপারেশন এবং এটি সমস্ত থ্রেডকে সিঙ্ক্রোনাইজ করে না।
__syncthreads
সিঙ্ক্রোনাইজ করা যায় এমন থ্রেডগুলির সঠিক সংখ্যা সম্পর্কে আমি নিশ্চিত নই , যেহেতু আপনি 512 টিরও বেশি থ্রেড সহ একটি ব্লক তৈরি করতে এবং ওয়ার্পকে সময়সূচীটি পরিচালনা করতে দিতে পারেন। আমার বোঝার জন্য এটি বলার অপেক্ষা রাখে না আরও সঠিক: ফানক 1 কমপক্ষে প্রথম 512 থ্রেডের জন্য কার্যকর করা হয়।
আমি এই উত্তরটি সম্পাদনা করার আগে (২০১০ সালে ফিরে) আমার মাপা 14x8x32 থ্রেডগুলি ব্যবহার করে সিঙ্ক্রোনাইজ করা হয়েছিল __syncthreads
।
যদি কেউ আরও তথ্যের আরও সঠিক অংশের জন্য এটি আবার পরীক্ষা করে দেখে আমি প্রশংসা করব।
__syncthreads
এটি একটি ব্লক-ওয়াইড অপারেশন এবং এটি সত্য যে এটি সমস্ত থ্রেডকে সিঙ্ক্রোনাইজ করে না এটি সিউডিএ শিখারীদের উপদ্রব। সুতরাং আপনি আমাকে যে তথ্য দিয়েছেন তার ভিত্তিতে আমি আমার উত্তর আপডেট করেছি। আমি সত্যিই এটার প্রশংসা করছি.