CUDA ग्रिड आयाम, ब्लॉक आयाम और थ्रेड संगठन (सरल विवरण) को समझना [बंद]


161

GPU द्वारा निष्पादित किए जाने वाले थ्रेड्स कैसे व्यवस्थित किए जाते हैं?


CUDA प्रोग्रामिंग गाइड को इसके लिए शुरू करने के लिए एक अच्छी जगह होनी चाहिए। मैं यहाँ से CUDA परिचय की जाँच करने की भी सिफारिश करूँगा ।
टॉम

जवाबों:


287

हार्डवेयर

यदि एक जीपीयू डिवाइस में, उदाहरण के लिए, 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 ब्लॉक एक साथ निष्पादित किए जा रहे हैं)।

अब एक साधारण मामला: एक 512x512 छवि प्रसंस्करण

मान लें कि हम एक पिक्सेल (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;

11
यदि प्रत्येक ब्लॉक 768 धागे चला सकता है, तो केवल 64 का उपयोग क्यों करें? यदि आप 768 की अधिकतम सीमा का उपयोग करते हैं, तो आपके पास कम ब्लॉक और इतना बेहतर प्रदर्शन होगा।
अलीजा

10
@ अलीज़ा: ब्लॉक तार्किक हैं , 768 थ्रेड्स की सीमा प्रत्येक भौतिक प्रसंस्करण इकाई के लिए है। थ्रेड को कार्य वितरित करने के लिए आप अपनी समस्या के विनिर्देशों के अनुसार ब्लॉक का उपयोग करते हैं। यह संभावना नहीं है कि आप हमेशा हर समस्या के लिए 768 थ्रेड के ब्लॉक का उपयोग कर सकते हैं। कल्पना कीजिए कि आपको 64x64 छवि (4096 पिक्सेल) संसाधित करनी होगी। 4096/768 = 5.333333 ब्लॉक?
cibercitizen1

1
ब्लॉक तार्किक हैं, लेकिन प्रत्येक ब्लॉक एक कोर को सौंपा गया है। यदि कोर से अधिक ब्लॉक हैं, तो ब्लॉक तब तक कतारबद्ध हैं जब तक कि कोर मुक्त नहीं हो जाते। अपने उदाहरण में आप 6 ब्लॉकों का उपयोग कर सकते हैं और अतिरिक्त थ्रेड्स कुछ भी नहीं कर सकते हैं (6 वें ब्लॉक पर थ्रेड्स के 2/3)।
अलीजा

3
@ cibercitizen1 - मुझे लगता है कि अलीज़ा की बात एक अच्छी है: यदि संभव हो तो, एक व्यक्ति संभव के रूप में प्रति ब्लॉक कई थ्रेड का उपयोग करना चाहता है। यदि एक बाधा है जिसे कम धागे की आवश्यकता होती है, तो यह समझाने के लिए बेहतर है कि एक दूसरे उदाहरण में मामला क्यों हो सकता है (लेकिन अभी भी सरल और अधिक वांछनीय मामले की व्याख्या करें, पहले)।

6
@ हाँ, हाँ, शायद। लेकिन मामला यह है कि प्रत्येक थ्रेड द्वारा आवश्यक मेमोरी की मात्रा आवेदन पर निर्भर है। उदाहरण के लिए, मेरे अंतिम कार्यक्रम में, प्रत्येक थ्रेड एक न्यूनतम-वर्ग अनुकूलन फ़ंक्शन को आमंत्रित करता है, जिसमें "बहुत" मेमोरी की आवश्यकता होती है। इतना, कि ब्लॉक 4x4 धागे से बड़ा नहीं हो सकता। फिर भी, प्राप्त गति नाटकीय थी, बनाम अनुक्रमिक संस्करण।
cibercitizen1

9

मान लीजिए 9800GT GPU:

  • इसमें 14 मल्टीप्रोसेसर (SM) हैं
  • प्रत्येक SM में 8 थ्रेड-प्रोसेसर (AKA स्ट्रीम-प्रोसेसर, SP या कोर) हैं
  • प्रति ब्लॉक 512 धागे तक की अनुमति देता है
  • warpsize 32 है (जिसका अर्थ है कि प्रत्येक 14x8 = 112 थ्रेड-प्रोसेसर 32 थ्रेड तक शेड्यूल कर सकता है)

https://www.tutorialspoint.com/cuda/cuda_threads.htm

ब्लॉक में 512 से अधिक सक्रिय थ्रेड नहीं हो __syncthreadsसकते हैं इसलिए केवल सीमित संख्या में थ्रेड को सिंक्रनाइज़ कर सकते हैं। यानी यदि आप निम्नलिखित 600 धागे के साथ निष्पादित करते हैं:

func1();
__syncthreads();
func2();
__syncthreads();

तब कर्नेल को दो बार चलना चाहिए और निष्पादन का क्रम होगा:

  1. func1 को पहले 512 थ्रेड्स के लिए निष्पादित किया जाता है
  2. func2 को पहले 512 थ्रेड्स के लिए निष्पादित किया जाता है
  3. func1 को शेष थ्रेड्स के लिए निष्पादित किया जाता है
  4. func2 को शेष थ्रेड्स के लिए निष्पादित किया जाता है

ध्यान दें:

मुख्य बिंदु __syncthreadsएक ब्लॉक-वाइड ऑपरेशन है और यह सभी थ्रेड्स को सिंक्रनाइज़ नहीं करता है।


मैं थ्रेड्स की सटीक संख्या के बारे में निश्चित नहीं हूं जो __syncthreadsसिंक्रनाइज़ कर सकते हैं, क्योंकि आप 512 से अधिक थ्रेड्स के साथ एक ब्लॉक बना सकते हैं और ताना को शेड्यूलिंग को संभालने देते हैं। मेरी समझ से यह कहना अधिक सटीक है: func1 को कम से कम पहले 512 धागे के लिए निष्पादित किया जाता है

इससे पहले कि मैं इस जवाब को संपादित करता (2010 में वापस) मैंने 14x8x32 थ्रेड्स का उपयोग करके मापा गया था __syncthreads

अगर किसी ने अधिक सटीक जानकारी के लिए फिर से परीक्षण किया तो मैं बहुत सराहना करूंगा।


अगर func2 () func1 () के परिणामों पर निर्भर करता है तो क्या होता है। मुझे लगता है कि यह गलत है
क्रिस

@ क्रिस ने यह सात साल पहले लिखा था, लेकिन अगर मुझे सही से याद है तो मैंने इस पर एक परीक्षण किया और यह निष्कर्ष निकाला कि गुप्‍ता की तुलना में अधिक थ्रेड वाले कर्नेल इस तरह से व्यवहार करते हैं। यदि आप इस मामले का परीक्षण करते हैं और एक अलग परिणाम पर पहुंचते हैं तो मुझे इस पोस्ट को हटाना होगा।
बिशन

क्षमा करें मुझे लगता है कि यह गलत है, यह भी, कि GPU केवल समकालिक रूप से 112 थ्रेड चला सकता है।
स्टीवन लू

@StevenLu क्या आपने इसे आज़माया है? यह भी मुझे नहीं लगता कि 112 समवर्ती धागे एक GPU के लिए कोई मतलब रखते हैं। 112 स्ट्रीम प्रोसेसर की संख्या है। मैं शायद ही अब CUDA को याद कर सकता हूं :)
बिशन

1
@StevenLu थ्रेड्स की अधिकतम संख्या यहां मुद्दा नहीं है, __syncthreadsएक ब्लॉक-वाइड ऑपरेशन है और तथ्य यह है कि यह वास्तव में सभी थ्रेड्स को सिंक्रनाइज़ नहीं करता है CUDA शिक्षार्थियों के लिए एक उपद्रव है। इसलिए मैंने आपके द्वारा मुझे दी गई जानकारी के आधार पर अपना उत्तर अपडेट किया। मैं वास्तव में इसकी प्रशंसा करता हूँ।
बिशन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.