GPU द्वारा निष्पादित किए जाने वाले थ्रेड्स कैसे व्यवस्थित किए जाते हैं?
GPU द्वारा निष्पादित किए जाने वाले थ्रेड्स कैसे व्यवस्थित किए जाते हैं?
जवाबों:
यदि एक जीपीयू डिवाइस में, उदाहरण के लिए, 4 मल्टीप्रोसेसिंग इकाइयां हैं, और वे प्रत्येक पर 768 थ्रेड चला सकते हैं: तो किसी भी समय 4 * 768 थ्रेड्स वास्तव में समानांतर में चल रहे होंगे (यदि आपने अधिक थ्रेड की योजना बनाई है, तो वे प्रतीक्षा कर रहे होंगे। उनकी बारी)।
धागे ब्लॉकों में आयोजित किए जाते हैं। एक ब्लॉक को एक मल्टीप्रोसेसिंग इकाई द्वारा निष्पादित किया जाता है। एक ब्लॉक के थ्रेड्स को 1 डेंसेशन (x), 2Dimensions (x, y) या 3Dim इंडेक्स (x, y, z) का उपयोग करके इंडेंट (अनुक्रमित) किया जा सकता है, लेकिन किसी भी स्थिति में x y z <= 768 हमारे उदाहरण के लिए (अन्य प्रतिबंध लागू होते हैं) एक्स, वाई, जेड, गाइड और अपनी डिवाइस की क्षमता देखें)।
जाहिर है, अगर आपको उन 4 * 768 धागे से अधिक की आवश्यकता है, तो आपको 4 से अधिक ब्लॉक की आवश्यकता है। ब्लॉक को 1 डी, 2 डी या 3 डी भी अनुक्रमित किया जा सकता है। GPU में प्रवेश करने के लिए ब्लॉक की एक कतार है (क्योंकि, हमारे उदाहरण में, GPU में 4 मल्टीप्रोसेसर हैं और केवल 4 ब्लॉक एक साथ निष्पादित किए जा रहे हैं)।
मान लें कि हम एक पिक्सेल (i, j) को संसाधित करने के लिए एक थ्रेड चाहते हैं।
हम 64 थ्रेड्स के ब्लॉक का उपयोग कर सकते हैं। फिर हमें 512 * 512/64 = 4096 ब्लॉक चाहिए (ताकि 512x512 धागे = 4096 * 64 हो)
2 डी ब्लॉक में धागे को ब्लॉक करना आसान है (छवि अनुक्रमणिका को आसान बनाने के लिए) ब्लॉकडिम = 8 x 8 (64 ब्लॉक प्रति ब्लॉक)। मैं इसे थ्रेडर्स कॉल करना पसंद करता हूं।
dim3 threadsPerBlock(8, 8); // 64 threads
और 2D ग्रिडडिम = 64 x 64 ब्लॉक (4096 ब्लॉक आवश्यक)। मैं इसे numBlocks कहना पसंद करता हूं।
dim3 numBlocks(imageWidth/threadsPerBlock.x, /* for instance 512/8 = 64*/
imageHeight/threadsPerBlock.y);
कर्नेल को इस तरह लॉन्च किया गया है:
myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );
अंत में: "4096 ब्लॉकों की एक कतार" जैसा कुछ होगा, जहां एक ब्लॉक को अपने 64 थ्रेड्स निष्पादित करने के लिए GPU के मल्टीप्रोसेसर में से एक को सौंपा जाना है।
कर्नेल में पिक्सेल (i, j) को एक थ्रेड द्वारा संसाधित किया जाता है, इसकी गणना इस प्रकार की जाती है:
uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
मान लीजिए 9800GT GPU:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
ब्लॉक में 512 से अधिक सक्रिय थ्रेड नहीं हो __syncthreads
सकते हैं इसलिए केवल सीमित संख्या में थ्रेड को सिंक्रनाइज़ कर सकते हैं। यानी यदि आप निम्नलिखित 600 धागे के साथ निष्पादित करते हैं:
func1();
__syncthreads();
func2();
__syncthreads();
तब कर्नेल को दो बार चलना चाहिए और निष्पादन का क्रम होगा:
ध्यान दें:
मुख्य बिंदु __syncthreads
एक ब्लॉक-वाइड ऑपरेशन है और यह सभी थ्रेड्स को सिंक्रनाइज़ नहीं करता है।
मैं थ्रेड्स की सटीक संख्या के बारे में निश्चित नहीं हूं जो __syncthreads
सिंक्रनाइज़ कर सकते हैं, क्योंकि आप 512 से अधिक थ्रेड्स के साथ एक ब्लॉक बना सकते हैं और ताना को शेड्यूलिंग को संभालने देते हैं। मेरी समझ से यह कहना अधिक सटीक है: func1 को कम से कम पहले 512 धागे के लिए निष्पादित किया जाता है ।
इससे पहले कि मैं इस जवाब को संपादित करता (2010 में वापस) मैंने 14x8x32 थ्रेड्स का उपयोग करके मापा गया था __syncthreads
।
अगर किसी ने अधिक सटीक जानकारी के लिए फिर से परीक्षण किया तो मैं बहुत सराहना करूंगा।
__syncthreads
एक ब्लॉक-वाइड ऑपरेशन है और तथ्य यह है कि यह वास्तव में सभी थ्रेड्स को सिंक्रनाइज़ नहीं करता है CUDA शिक्षार्थियों के लिए एक उपद्रव है। इसलिए मैंने आपके द्वारा मुझे दी गई जानकारी के आधार पर अपना उत्तर अपडेट किया। मैं वास्तव में इसकी प्रशंसा करता हूँ।