2012-08-09 12 views
41

जब cudaDeviceSynchronize फ़ंक्शन पर कॉल कर रहा है तो वास्तव में आवश्यकता है?कब cudaDevice सिंक्रनाइज़ करने के लिए?

जहां तक ​​मैं सीयूडीए दस्तावेज से समझता हूं, सीयूडीए कर्नेल असीमित हैं, इसलिए ऐसा लगता है कि हमें प्रत्येक कर्नेल लॉन्च के बाद cudaDeviceSynchronize पर कॉल करना चाहिए। हालांकि, मैंने समय माप से पहले एक को छोड़कर, cudaDeviceSynchronize के साथ और बिना किसी कोड (प्रशिक्षण तंत्रिका नेटवर्क) की कोशिश की है। मैंने पाया है कि मुझे एक ही परिणाम मिलता है लेकिन 7-12x (मैट्रिक्स आकारों के आधार पर) के बीच की गति के साथ।

तो सवाल यह है कि समय माप के अलावा cudaDeviceSynchronize का उपयोग करने के कोई कारण हैं।

उदाहरण के लिए:

  • यह GPU वापस cudaMemcpy साथ होस्ट करने से डेटा की प्रतिलिपि से पहले की जरूरत है?

  • तरह

    C = A * B 
    D = C * F 
    

मैट्रिक्स गुणा मैं दोनों के बीच cudaDeviceSynchronize रखना चाहिए अगर मैं कर सकता हूँ?

मेरे प्रयोग से ऐसा लगता है कि मैं नहीं करता हूं।

cudaDeviceSynchronize प्रोग्राम इतना धीमा क्यों करता है?

+0

एक उदाहरण होगा यदि आपके पास कर्नेल में कोई प्रिंट स्टेटमेंट है, तो बफर सिंक्रनाइज़ेशन ईवेंट तक प्रिंट नहीं करेगा। –

उत्तर

12

एक स्थिति जहां cudaDeviceSynchronize() का उपयोग करना उचित होगा जब आपके पास cudaStream चल रहा है, और आप उन्हें कुछ जानकारी का आदान-प्रदान करना चाहते हैं। क्वांटम मोंटे कार्लो सिमुलेशन में इसका वास्तविक जीवन मामला समानांतर tempering है। इस मामले में, हम यह सुनिश्चित करना चाहते हैं कि प्रत्येक स्ट्रीम ने निर्देशों के कुछ सेट को समाप्त कर लिया है और एक दूसरे को संदेश पास करने से पहले कुछ परिणाम प्राप्त किए हैं, या हम कचरे की जानकारी उत्तीर्ण करेंगे। इस कमांड का उपयोग करने का कारण प्रोग्राम को धीमा कर देता है कि cudaDeviceSynchronize() प्रोग्राम को जारी रखने से पहले खत्म करने के लिए डिवाइस पर सभी स्ट्रीमों में पहले जारी किए गए सभी आदेशों की प्रतीक्षा करने के लिए मजबूर करता है (CUDA C प्रोग्रामिंग गाइड से)। जैसा कि आपने कहा था, कर्नेल निष्पादन आमतौर पर असीमित होता है, इसलिए जब GPU डिवाइस आपके कर्नेल को निष्पादित कर रहा है, तो CPU कुछ अन्य आदेशों पर काम करना जारी रख सकता है, डिवाइस के लिए अधिक निर्देश जारी कर सकता है, प्रतीक्षा करने के बजाए। हालांकि जब आप इस सिंक्रनाइज़ेशन कमांड का उपयोग करते हैं, तो CPU को तब तक निष्क्रिय करने के लिए मजबूर होना पड़ता है जब तक कि कुछ भी करने से पहले सभी GPU कार्य पूरा हो जाए। डिबगिंग करते समय यह व्यवहार उपयोगी होता है, क्योंकि आपके पास डिवाइस कोड के असीमित निष्पादन (चाहे एक स्ट्रीम या कई में) के कारण "यादृच्छिक" समय पर एक segfault हो सकता है। cudaDeviceSynchronize() प्रोग्राम को मजबूर करने के लिए मजबूर करेगा कि धारा (ओं) के कर्नल/memcpys जारी रखने से पहले पूर्ण हो जाएं, जिससे यह पता लगाना आसान हो सकता है कि अवैध पहुंच कहां हो रही है (क्योंकि विफलता सिंक के दौरान दिखाई देगी)।

43

हालांकि सीयूडीए कर्नेल लॉन्च एसिंक्रोनस हैं, एक स्ट्रीम (जो डिफ़ॉल्ट व्यवहार है) में रखे गए सभी जीपीयू-संबंधित कार्यों को अनुक्रमिक रूप से निष्पादित किया जाता है।

तो, उदाहरण के लिए,

kernel1<<<X,Y>>>(...); // kernel start execution, CPU continues to next statement 
kernel2<<<X,Y>>>(...); // kernel is placed in queue and will start after kernel1 finishes, CPU continues to next statement 
cudaMemcpy(...); // CPU blocks until ememory is copied, memory copy starts only after kernel2 finishes 

तो अपने उदाहरण में वहाँ cudaDeviceSynchronize की कोई आवश्यकता नहीं है। हालांकि, यह पता लगाने के लिए डिबगिंग के लिए उपयोगी हो सकता है कि आपके कर्नेल में से कौन सा त्रुटि हुई है (यदि कोई है)।

cudaDeviceSynchronize कुछ मंदी का कारण बन सकता है, लेकिन 7-12x बहुत अधिक लगता है।हो सकता है कि समय माप के साथ कुछ समस्या हो, या हो सकता है कि कर्नल वास्तव में तेज़ हों, और स्पष्ट सिंक्रनाइज़ेशन का ओवरहेड वास्तविक गणना समय के सापेक्ष विशाल है।

+0

"अन्य डिफ़ॉल्ट निर्दिष्ट GPU स्ट्रीम जब तक कि अन्यथा निर्दिष्ट नहीं किया गया" हमेशा nvcc द्वारा आयोजित नहीं किया जाता है। मैंने बस एक प्रोग्राम डीबग किया जहां मैंने एक कर्नेल पर एक टुकड़े की गणना में एक लंबी गणना तोड़ दी जिसने एक समय में() लूप में कर्नेल लॉन्च किया।() लूप कर्नेल के लिए लगातार शुरू होता है जहां पिछले() लूप कर्नेल डिवाइस-साइड से बाहर निकलता है। बग यह था कि एनवीसीसी कंपाइलर इसे होस्ट कोड से नहीं देख सका और एक ही समय में प्रत्येक कर्नेल को लॉन्च करने का प्रयास किया। इसका मतलब था कि सभी कर्नल लेकिन पहला कर्नेल कचरा कंप्यूटिंग कर रहा था। – opetrenko

+2

@opetrenko यह नहीं है कि कैसे CUDA काम करता है। –

+0

@AleksandrDubinsky कृपया मेरी टिप्पणी को अधिक सावधानी से पढ़ें। मैं बहुत स्पष्ट रूप से नीचे डाल दिया "हमेशा nvcc द्वारा आयोजित नहीं किया जाता है"। मैंने फिर एक विशिष्ट बग का एक उदाहरण दिया जो मैंने cuda-gdb का उपयोग करके पीछा किया जो कि उदाहरण के रूप में कार्य करता है। मैं निश्चित रूप से सहमत हूं कि एनवीडिया के साहित्य के आधार पर यह नहीं है कि सीयूडीए कैसे काम कर रहा है ... लेकिन जो मैं कह रहा था वह एक राय नहीं थी: यह एक विशिष्ट उदाहरण में काम करने के तरीके के बारे में डीबगिंग के दौरान किया गया एक अवलोकन था। – opetrenko

3

जब आप अपने जीपीयू को कुछ डेटा प्रोसेस करना शुरू करना चाहते हैं, तो आप आमतौर पर कर्नल आमंत्रण करते हैं। जब आप ऐसा करते हैं, तो आपका डिवाइस (GPU) जो कुछ भी करने के लिए कहा जाता है, वह करना शुरू कर देगा। हालांकि, आपके होस्ट (सीपीयू) पर एक सामान्य अनुक्रमिक प्रोग्राम के विपरीत आपके प्रोग्राम में कोड की अगली पंक्तियां निष्पादित करना जारी रहेगा। cudaDevice सिंक्रनाइज़ करने से होस्ट (सीपीयू) तब तक प्रतीक्षा करता है जब तक डिवाइस (जीपीयू) आपके द्वारा शुरू किए गए सभी थ्रेड को निष्पादित करने के समाप्त नहीं कर लेता है, और इस प्रकार आपका प्रोग्राम जारी रहेगा जैसे कि यह एक सामान्य अनुक्रमिक प्रोग्राम था।

छोटे सरल कार्यक्रमों में आप आमतौर पर cudaDevice सिंक्रनाइज़ेशन का उपयोग करते हैं, जब आप गणना करने के लिए जीपीयू का उपयोग करते हैं, परिणाम के अनुरोध के लिए सीपीयू के बीच समय विसंगतियों से बचने के लिए और जीपीयू गणना को समाप्त करने के लिए। CudaDeviceSynchronize का उपयोग करने के लिए यह आपके प्रोग्राम को कोड करना बहुत आसान बनाता है, लेकिन एक बड़ी कमी है: आपका सीपीयू हर समय निष्क्रिय रहता है, जबकि GPU गणना करता है। इसलिए, उच्च प्रदर्शन कंप्यूटिंग में, आप अक्सर अपने सीपीयू बनाने की गणना करने की कोशिश करते हैं जबकि GPU समाप्त होने की प्रतीक्षा करते हैं।

संबंधित मुद्दे