2008-09-17 16 views
7

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

चूंकि इस डेटा का उपयोग कई कार्यों में किया जाता है, इसलिए मैं इसे वैश्विक होना चाहता हूं। हां, मैं चारों ओर पॉइंटर्स पास कर सकता हूं, लेकिन मैं वास्तव में जानना चाहता हूं कि इस उदाहरण में ग्लोबल्स के साथ कैसे काम करना है।

तो, मेरे पास डिवाइस फ़ंक्शन हैं जो डिवाइस आवंटित सरणी तक पहुंच बनाना चाहते हैं।

आदर्श रूप में, मैं की तरह कुछ कर सकता है:

__device__ float* global_data; 

main() 
{ 
    cudaMalloc(global_data); 
    kernel1<<<blah>>>(blah); //access global data 
    kernel2<<<blah>>>(blah); //access global data again 
} 

हालांकि, मैं पता लगा नहीं किया एक गतिशील सरणी बनाने का तरीका। मैं सरणी की घोषणा इस प्रकार से चारों ओर एक काम पता लगा:

__device__ float global_data[REALLY_LARGE_NUMBER]; 

और जब कि एक cudaMalloc कॉल की आवश्यकता नहीं है, मैं गतिशील आवंटन दृष्टिकोण पसंद करेंगे।

+0

साझा स्मृति का उपयोग करने पर भी एक नज़र डालें, वैश्विक डिवाइस मेमोरी परतों का सबसे धीमा है। – SpaceghostAli

+0

आप कर्नेल को तर्क के रूप में डिवाइस पॉइंटर को पास करने के बजाय ग्लोबल्स का उपयोग क्यों करना चाहते हैं? ऐसा करने से आपको सीपीयू कोड में ग्लोबल मेमोरी का उपयोग करने के साथ ही सभी समान सीमाएं मिलती हैं। –

उत्तर

5

ऐसा कुछ शायद काम करना चाहिए।

#include <algorithm> 

#define NDEBUG 
#define CUT_CHECK_ERROR(errorMessage) do {         \ 
     cudaThreadSynchronize();           \ 
     cudaError_t err = cudaGetLastError();        \ 
     if(cudaSuccess != err) {           \ 
        fprintf(stderr, "Cuda error: %s in file '%s' in line %i : %s.\n", \ 
              errorMessage, __FILE__, __LINE__, cudaGetErrorString(err));\ 
        exit(EXIT_FAILURE);             \ 
       } } while (0) 


__device__ float *devPtr; 

__global__ 
void kernel1(float *some_neat_data) 
{ 
    devPtr = some_neat_data; 
} 

__global__ 
void kernel2(void) 
{ 
    devPtr[threadIdx.x] *= .3f; 
} 


int main(int argc, char *argv[]) 
{ 
    float* otherDevPtr; 
    cudaMalloc((void**)&otherDevPtr, 256 * sizeof(*otherDevPtr)); 
    cudaMemset(otherDevPtr, 0, 256 * sizeof(*otherDevPtr)); 

    kernel1<<<1,128>>>(otherDevPtr); 
    CUT_CHECK_ERROR("kernel1"); 

    kernel2<<<1,128>>>(); 

    CUT_CHECK_ERROR("kernel2"); 

    return 0; 
} 

इसे एक भंवर दें।

+0

दिलचस्प है। मैं देखता हूं कि cudaMalloc को सीधे devPtr पर क्यों नहीं कहा जा रहा है, लेकिन उस पहले कर्नेल कॉल में सेट किया जा रहा है। मैं इसे बाद में आज़मा दूंगा और आपको बताऊंगा कि यह काम करता है, बहुत बहुत धन्यवाद: डी – Voltaire

1

एनवीआईडीआईए द्वारा पेश किए गए विशाल दस्तावेज पर ध्यान केंद्रित करने में कुछ समय व्यतीत करें।

प्रोग्रामिंग गाइड से:

float* devPtr; 
cudaMalloc((void**)&devPtr, 256 * sizeof(*devPtr)); 
cudaMemset(devPtr, 0, 256 * sizeof(*devPtr)); 

कैसे स्मृति को आबंटित करने का एक सरल उदाहरण है कि। अब, अपने कर्नेल में, आप एक सूचक एक नाव के लिए इतना की तरह स्वीकार करना चाहिए:

__global__ 
void kernel1(float *some_neat_data) 
{ 
    some_neat_data[threadIdx.x]++; 
} 

__global__ 
void kernel2(float *potentially_that_same_neat_data) 
{ 
    potentially_that_same_neat_data[threadIdx.x] *= 0.3f; 
} 

तो अब आप उन्हें इतनी तरह आह्वान कर सकते हैं:

float* devPtr; 
cudaMalloc((void**)&devPtr, 256 * sizeof(*devPtr)); 
cudaMemset(devPtr, 0, 256 * sizeof(*devPtr)); 

kernel1<<<1,128>>>(devPtr); 
kernel2<<<1,128>>>(devPtr); 

इस डेटा कई में प्रयोग किया जाता है के रूप में कार्य, मैं इसे वैश्विक होना चाहता हूं।

ग्लोबल्स का उपयोग करने के कुछ अच्छे कारण हैं। यह निश्चित रूप से एक नहीं है। मैं इस उदाहरण को विस्तारित करने के लिए एक अभ्यास के रूप में छोड़ दूंगा ताकि "devPtr" को वैश्विक दायरे में ले जाया जा सके।

संपादित करें:

ठीक है, बुनियादी समस्या यह है: अपने कर्नेल केवल कर सकते हैं पहुँच डिवाइस स्मृति और केवल वैश्विक-गुंजाइश संकेत दिए गए हैं कि वे उपयोग कर सकते हैं GPU होते हैं। अपने सीपीयू से कर्नेल को कॉल करते समय, दृश्यों के पीछे क्या होता है यह है कि कर्नेल निष्पादित होने से पहले पॉइंटर्स और प्राइमेटिव जीपीयू रजिस्टरों और/या साझा मेमोरी में कॉपी हो जाते हैं।

तो सबसे नज़दीकी मैं सुझाव दे सकता हूं: अपने लक्ष्यों को प्राप्त करने के लिए cudaMemcpyToSymbol() का उपयोग करें। लेकिन, पृष्ठभूमि में, मान लें कि एक अलग दृष्टिकोण सही बात हो सकती है।

#include <algorithm> 

__constant__ float devPtr[1024]; 

__global__ 
void kernel1(float *some_neat_data) 
{ 
    some_neat_data[threadIdx.x] = devPtr[0] * devPtr[1]; 
} 

__global__ 
void kernel2(float *potentially_that_same_neat_data) 
{ 
    potentially_that_same_neat_data[threadIdx.x] *= devPtr[2]; 
} 


int main(int argc, char *argv[]) 
{ 
    float some_data[256]; 
    for (int i = 0; i < sizeof(some_data)/sizeof(some_data[0]); i++) 
    { 
     some_data[i] = i * 2; 
    } 
    cudaMemcpyToSymbol(devPtr, some_data, std::min(sizeof(some_data), sizeof(devPtr))); 
    float* otherDevPtr; 
    cudaMalloc((void**)&otherDevPtr, 256 * sizeof(*otherDevPtr)); 
    cudaMemset(otherDevPtr, 0, 256 * sizeof(*otherDevPtr)); 

    kernel1<<<1,128>>>(otherDevPtr); 
    kernel2<<<1,128>>>(otherDevPtr); 

    return 0; 
} 

इस उदाहरण के लिए '--ost-compilation = C++' को न भूलें।

+0

हाँ - वह मूल रूप से मेरा समाधान था। केवल, निरंतर स्मृति में नहीं, क्योंकि सरणी बड़ी है: < तो __constant__ float * devPtr पर निर्णय क्या है; (या मेरे मामले में __device__ फ्लोट * devPtr;) मुझे संदेह है कि एक बहुत अच्छा कारण है कि आपके पास डिवाइस डेटा – Voltaire

+0

पर वैश्विक सूचक नहीं हो सकता है - इसके अलावा - आपका संपादन नहीं देखा। हालांकि, मुझे अभी भी यकीन नहीं है कि एक सरणी ठीक होने पर डिवाइस मेमोरी के लिए * सूचक * अमान्य क्यों है। – Voltaire

0

एआरएम, यह वास्तव में डीवीपीआरटी को वैश्विक दायरे में ले जाने की समस्या थी जो मेरी समस्या थी।

मेरे पास एक कार्यान्वयन है जो वास्तव में करता है, जिसमें दो कर्नल डेटा पास करने के लिए पॉइंटर होते हैं।मैं स्पष्ट रूप से उन पॉइंटर्स में पास नहीं करना चाहता हूं।

मैंने दस्तावेज़ीकरण को काफी बारीकी से पढ़ा है, और एनवीडिया फ़ोरम (और Google ने एक घंटे या उससे भी अधिक खोज की) को मारा है, लेकिन मुझे एक वैश्विक गतिशील डिवाइस सरणी का कार्यान्वयन नहीं मिला है जो वास्तव में चलता है (मैंने कोशिश की है कई जो संकलित करते हैं और फिर नए और दिलचस्प तरीकों से असफल होते हैं)।

0

एसडीके के साथ शामिल नमूने देखें। उन नमूने परियोजनाओं में से कई उदाहरण के द्वारा सीखने का एक सभ्य तरीका हैं।

1

मैं आगे बढ़ गया और एक अस्थायी सूचक आवंटित करने के समाधान की कोशिश की और इसे कर्नेल 1 के समान एक साधारण वैश्विक कार्य में पास कर दिया। ": क्या सूचक अंक नहीं बता सकते हैं, वैश्विक स्मृति अंतरिक्ष मानते हुए सलाहकार" जब भी मैं का उपयोग करने की कोशिश

अच्छी खबर यह है कि यह काम :)

हालांकि करता है, मैं इसे संकलक confuses के रूप में मैं अब मिल लगता है वैश्विक डेटा। सौभाग्य से, धारणा सही होती है, लेकिन चेतावनियां परेशान होती हैं।

वैसे भी, रिकॉर्ड के लिए - मैंने कई उदाहरणों को देखा है और एनवीडिया अभ्यास के माध्यम से चलाया है जहां बिंदु "सही" कहने के लिए है। हालांकि, मैंने उन सभी पर नहीं देखा है। अगर कोई एसडीके उदाहरण के बारे में जानता है जहां वे गतिशील वैश्विक डिवाइस मेमोरी आवंटन करते हैं, तो भी मैं जानना चाहूंगा।

0

चूंकि इस डेटा का उपयोग कई कार्यों में किया जाता है, इसलिए मैं इसे वैश्विक होना चाहता हूं।

-

वहाँ वैश्विक उपयोग करने के लिए कुछ अच्छे कारण हैं। यह निश्चित रूप से एक नहीं है। मैं इस उदाहरण को विस्तारित करने के लिए अभ्यास के रूप में छोड़ दूंगा ताकि "devPtr" को वैश्विक दायरे में ले जाया जा सके।

क्या होगा अगर कर्नेल एक बड़ी संरचना संरचना पर संचालन करता है जिसमें सरणी होती है? तथाकथित निरंतर स्मृति का उपयोग करना एक विकल्प नहीं है, क्योंकि यह आकार में बहुत सीमित है .. तो फिर आपको इसे वैश्विक स्मृति में रखना होगा ..?

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