2012-02-16 27 views
24

पर पॉइंटर्स युक्त संरचना की प्रतिलिपि बनाना मैं एक ऐसे प्रोजेक्ट पर काम कर रहा हूं जहां मुझे पॉइंटर्स युक्त संरचना पर कंप्यूटेशंस बनाने के लिए मेरे CUDA डिवाइस की आवश्यकता है।सीयूडीए डिवाइस

typedef struct StructA { 
    int* arr; 
} StructA; 

जब मैं struct के लिए स्मृति को आबंटित और फिर डिवाइस पर प्रतिलिपि, यह केवल struct और नहीं सूचक की सामग्री की नकल करेंगे। अभी मैं पॉइंटर को आवंटित करके इस पर काम कर रहा हूं, फिर उस नए सूचक (जो GPU पर रहता है) का उपयोग करने के लिए होस्ट स्ट्रक्चर सेट करें। निम्नलिखित कोड नमूना ऊपर से इस दृष्टिकोण struct का उपयोग कर का वर्णन करता है:

#define N 10 

int main() { 

    int h_arr[N] = {1,2,3,4,5,6,7,8,9,10}; 
    StructA *h_a = (StructA*)malloc(sizeof(StructA)); 
    StructA *d_a; 
    int *d_arr; 

    // 1. Allocate device struct. 
    cudaMalloc((void**) &d_a, sizeof(StructA)); 

    // 2. Allocate device pointer. 
    cudaMalloc((void**) &(d_arr), sizeof(int)*N); 

    // 3. Copy pointer content from host to device. 
    cudaMemcpy(d_arr, h_arr, sizeof(int)*N, cudaMemcpyHostToDevice); 

    // 4. Point to device pointer in host struct. 
    h_a->arr = d_arr; 

    // 5. Copy struct from host to device. 
    cudaMemcpy(d_a, h_a, sizeof(StructA), cudaMemcpyHostToDevice); 

    // 6. Call kernel. 
    kernel<<<N,1>>>(d_a); 

    // 7. Copy struct from device to host. 
    cudaMemcpy(h_a, d_a, sizeof(StructA), cudaMemcpyDeviceToHost); 

    // 8. Copy pointer from device to host. 
    cudaMemcpy(h_arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost); 

    // 9. Point to host pointer in host struct. 
    h_a->arr = h_arr; 
} 

मेरा प्रश्न है: इस तरह से यह करने के लिए है?

ऐसा लगता है कि यह बहुत काम करता है, और मैं आपको याद दिलाता हूं कि यह एक बहुत ही सरल संरचना है। यदि मेरे ढांचे में पॉइंटर्स के साथ बहुत सारे पॉइंटर्स या structs शामिल हैं, तो आवंटन और प्रतिलिपि के लिए कोड काफी व्यापक और भ्रमित होगा।

+2

चरण 7 और 9 अनावश्यक हैं, लेकिन अन्यथा यह काफी है कि यह कितना है।जैसा कि नीचे दिया गया जवाब कहता है, आप GPU पर जटिल, सूचक आधारित डेटा संरचनाओं से बचकर सबसे अच्छी सेवा कर रहे हैं। GPU पर प्रदर्शन खराब है, और एपीआई वास्तव में इसके लिए डिज़ाइन नहीं किए गए हैं। – talonmies

+0

मैं देख सकता हूं कि चरण 7 अनावश्यक है, लेकिन क्यों कदम 9? –

+0

अच्छी तरह से 'h_a' होस्ट मेमोरी में रखी गई डिवाइस संरचना की "छवि" है (या होना चाहिए)। होस्ट मेमोरी में पॉइंटर रखने के लिए इसे असाइन करना शायद आपके असली इरादों के आधार पर खराब अभ्यास/गलत/डिवाइस मेमोरी रिसाव का कुछ संयोजन है। 'D_a' की सामग्री को' h_a' पर वापस कॉपी करने के बाद आपके पास "पूर्ण सर्कल आ गया है" और वापस जहां आप से शुरू किया गया है। – talonmies

उत्तर

22

संपादित कदम 8 और 9 बदल सकते हैं: CUDA 6 एकीकृत मेमोरी का परिचय है, जो इस "गहरी प्रति" समस्या को बहुत आसान बनाता है। अधिक जानकारी के लिए this post देखें।


मत भूलना कि तुम कर सकते हैं पास के दाने के लिए मूल्य द्वारा संरचनाओं। इस कोड काम करता है:

// pass struct by value (may not be efficient for complex structures) 
__global__ void kernel2(StructA in) 
{ 
    in.arr[threadIdx.x] *= 2; 
} 

ऐसा करने से मतलब है कि आप केवल डिवाइस के लिए सरणी कॉपी करने के लिए है, नहीं संरचना:

int h_arr[N] = {1,2,3,4,5,6,7,8,9,10}; 
StructA h_a; 
int *d_arr; 

// 1. Allocate device array. 
cudaMalloc((void**) &(d_arr), sizeof(int)*N); 

// 2. Copy array contents from host to device. 
cudaMemcpy(d_arr, h_arr, sizeof(int)*N, cudaMemcpyHostToDevice); 

// 3. Point to device pointer in host struct. 
h_a.arr = d_arr; 

// 4. Call kernel with host struct as argument 
kernel2<<<N,1>>>(h_a); 

// 5. Copy pointer from device to host. 
cudaMemcpy(h_arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost); 

// 6. Point to host pointer in host struct 
// (or do something else with it if this is not needed) 
h_a.arr = h_arr; 
-3

सरणी की संरचना कुडा में एक दुःस्वप्न है। आपको प्रत्येक पॉइंटर को एक नई संरचना में कॉपी करना होगा जिसे डिवाइस उपयोग कर सकता है। शायद आप बजाय structs की एक सरणी का उपयोग कर सकते हैं? यदि मुझे एकमात्र रास्ता नहीं मिला है तो आप जिस तरह से करते हैं उस पर हमला करना है, जो कि किसी भी तरह से सुंदर नहीं है।

संपादित करें: के बाद से मैं शीर्ष पोस्ट पर टिप्पणियों नहीं दे सकता: कदम 9, बेमानी है जब से तुम में

// 8. Copy pointer from device to host. 
cudaMemcpy(h->arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost); 
+5

पहला, यह उत्तर खतरनाक है क्योंकि यह समांतर कंप्यूटिंग में एओएस/एसओए के बारे में मानक ज्ञान के खिलाफ जाता है। एसएसई/एवीएक्स निर्देश सेट के साथ मल्टीकोर सीपीयू समेत सभी समांतर कंप्यूटिंग में संरचनाओं के एरे (एसओएस) पर संरचनाओं (एओएस) का संरचना बेहतर है। इसका कारण यह है कि एसओए धागे के संदर्भ में इलाके के इलाके को बनाए रखता है (उदा। D_a.arr के आसन्न तत्वों को निकटवर्ती धागे द्वारा एक्सेस किया जाता है जो एक साथ चल रहे हैं)। इसमें एक सूचक के साथ एक संरचना Arrays के ढांचे के समान नहीं है। दूसरा, आप मूल्य से संरचना को पार करके इस कोड को सरल बना सकते हैं। – harrism

+1

@harrism क्यों स्ट्रू का ऐरे क्यूडा में बेहतर नहीं है? मैं इसे समझ नहीं पा रहा हूं, क्या आप मुझे एक उदाहरण या एक लिंक दे सकते हैं? धन्यवाद – BugShotGG

+0

@ जीओपैप्स [यहां] (http://stackoverflow.com/questions/18136785/kernel-using-aos-is-faster-than-using-soa/18137311#18137311) एक प्रश्न/उत्तर है जो एसओए बनाम पर चर्चा करता है। उदाहरण के साथ एओएस। –

1

के रूप में मार्क हैरिस ने कहा, संरचनाओं के मूल्यों से पारित किया जा सकता CUDA कर्नल। हालांकि, कुछ देखभाल को उचित विनाशक स्थापित करने के लिए समर्पित होना चाहिए क्योंकि कर्नेल से बाहर निकलने पर विनाशक को बुलाया जाता है।

uncommented नाशक (क्या कोड वास्तव में करता है पर बहुत ज्यादा ध्यान नहीं देते हैं) के साथ निम्नलिखित उदाहरण पर विचार

#include <stdio.h> 

#include "Utilities.cuh" 

#define NUMBLOCKS 512 
#define NUMTHREADS 512 * 2 

/***************/ 
/* TEST STRUCT */ 
/***************/ 
struct Lock { 

    int *d_state; 

    // --- Constructor 
    Lock(void) { 
     int h_state = 0;          // --- Host side lock state initializer 
     gpuErrchk(cudaMalloc((void **)&d_state, sizeof(int))); // --- Allocate device side lock state 
     gpuErrchk(cudaMemcpy(d_state, &h_state, sizeof(int), cudaMemcpyHostToDevice)); // --- Initialize device side lock state 
    } 

    // --- Destructor (wrong version) 
    //~Lock(void) { 
    // printf("Calling destructor\n"); 
    // gpuErrchk(cudaFree(d_state)); 
    //} 

    // --- Destructor (correct version) 
// __host__ __device__ ~Lock(void) { 
//#if !defined(__CUDACC__) 
//  gpuErrchk(cudaFree(d_state)); 
//#else 
// 
//#endif 
// } 

    // --- Lock function 
    __device__ void lock(void) { while (atomicCAS(d_state, 0, 1) != 0); } 

    // --- Unlock function 
    __device__ void unlock(void) { atomicExch(d_state, 0); } 
}; 

/**********************************/ 
/* BLOCK COUNTER KERNEL WITH LOCK */ 
/**********************************/ 
__global__ void blockCounterLocked(Lock lock, int *nblocks) { 

    if (threadIdx.x == 0) { 
     lock.lock(); 
     *nblocks = *nblocks + 1; 
     lock.unlock(); 
    } 
} 

/********/ 
/* MAIN */ 
/********/ 
int main(){ 

    int h_counting, *d_counting; 
    Lock lock; 

    gpuErrchk(cudaMalloc(&d_counting, sizeof(int))); 

    // --- Locked case 
    h_counting = 0; 
    gpuErrchk(cudaMemcpy(d_counting, &h_counting, sizeof(int), cudaMemcpyHostToDevice)); 

    blockCounterLocked << <NUMBLOCKS, NUMTHREADS >> >(lock, d_counting); 
    gpuErrchk(cudaPeekAtLastError()); 
    gpuErrchk(cudaDeviceSynchronize()); 

    gpuErrchk(cudaMemcpy(&h_counting, d_counting, sizeof(int), cudaMemcpyDeviceToHost)); 
    printf("Counting in the locked case: %i\n", h_counting); 

    gpuErrchk(cudaFree(d_counting)); 
} 

। आपको लगता है कि कोड को चलाने, तो आप निम्न उत्पादन

Calling destructor 
Counting in the locked case: 512 
Calling destructor 
GPUassert: invalid device pointer D:/Project/passStructToKernel/passClassToKernel/Utilities.cu 37 

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

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