2012-05-10 10 views
19

उदाहरण के लिए निम्नलिखित कोड (वैक्टरों की योग) के प्रदर्शन में सुधार के लिए मैं दो उपकरणों का उपयोग कैसे कर सकता हूं? क्या "एक ही समय में" अधिक उपकरणों का उपयोग करना संभव है? यदि हां, तो मैं अलग-अलग उपकरणों की वैश्विक स्मृति पर वैक्टरों के आवंटन को कैसे प्रबंधित कर सकता हूं?बहु-जीपीयू मूल उपयोग

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include <time.h> 
#include <cuda.h> 

#define NB 32 
#define NT 500 
#define N NB*NT 

__global__ void add(double *a, double *b, double *c); 

//=========================================== 
__global__ void add(double *a, double *b, double *c){ 

    int tid = threadIdx.x + blockIdx.x * blockDim.x; 

    while(tid < N){ 
     c[tid] = a[tid] + b[tid]; 
     tid += blockDim.x * gridDim.x; 
    } 

} 

//============================================ 
//BEGIN 
//=========================================== 
int main(void) { 

    double *a, *b, *c; 
    double *dev_a, *dev_b, *dev_c; 

    // allocate the memory on the CPU 
    a=(double *)malloc(N*sizeof(double)); 
    b=(double *)malloc(N*sizeof(double)); 
    c=(double *)malloc(N*sizeof(double)); 

    // allocate the memory on the GPU 
    cudaMalloc((void**)&dev_a, N * sizeof(double)); 
    cudaMalloc((void**)&dev_b, N * sizeof(double)); 
    cudaMalloc((void**)&dev_c, N * sizeof(double)); 

    // fill the arrays 'a' and 'b' on the CPU 
    for (int i=0; i<N; i++) { 
     a[i] = (double)i; 
     b[i] = (double)i*2; 
    } 

    // copy the arrays 'a' and 'b' to the GPU 
    cudaMemcpy(dev_a, a, N * sizeof(double), cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_b, b, N * sizeof(double), cudaMemcpyHostToDevice); 

    for(int i=0;i<10000;++i) 
     add<<<NB,NT>>>(dev_a, dev_b, dev_c); 

    // copy the array 'c' back from the GPU to the CPU 
    cudaMemcpy(c, dev_c, N * sizeof(double), cudaMemcpyDeviceToHost); 

    // display the results 
    // for (int i=0; i<N; i++) { 
    //  printf("%g + %g = %g\n", a[i], b[i], c[i]); 
    // } 
    printf("\nGPU done\n"); 

    // free the memory allocated on the GPU 
    cudaFree(dev_a); 
    cudaFree(dev_b); 
    cudaFree(dev_c); 
    // free the memory allocated on the CPU 
    free(a); 
    free(b); 
    free(c); 

    return 0; 
} 

अग्रिम धन्यवाद। मिशेल

उत्तर

32

चूंकि सीयूडीए 4.0 जारी किया गया था, आप जिस प्रकार के बारे में पूछ रहे हैं उसके बहु-जीपीयू गणना अपेक्षाकृत आसान हैं। इससे पहले, आपको एक होस्ट-थ्रेड होस्ट एप्लिकेशन का उपयोग एक जीपीयू प्रति होस्ट थ्रेड के साथ और एक ही होस्ट अनुप्रयोग के अंदर mutliple GPUs का उपयोग करने के लिए इंटर-थ्रेड संचार प्रणाली के कुछ प्रकार के साथ करने की आवश्यकता होगी।

अब यह आपके मेजबान कोड की स्मृति आवंटन भाग के लिए इस तरह कुछ करने के लिए संभव है:

double *dev_a[2], *dev_b[2], *dev_c[2]; 
const int Ns[2] = {N/2, N-(N/2)}; 

// allocate the memory on the GPUs 
for(int dev=0; dev<2; dev++) { 
    cudaSetDevice(dev); 
    cudaMalloc((void**)&dev_a[dev], Ns[dev] * sizeof(double)); 
    cudaMalloc((void**)&dev_b[dev], Ns[dev] * sizeof(double)); 
    cudaMalloc((void**)&dev_c[dev], Ns[dev] * sizeof(double)); 
} 

(अस्वीकरण: ब्राउज़र में लिखा है, संकलित कभी नहीं, कभी नहीं परीक्षण किया है, अपने जोखिम पर उपयोग)।

मूलभूत विचार यह है कि आप डिवाइस पर संचालन को पूर्ववत करते समय डिवाइसों के बीच चयन करने के लिए cudaSetDevice का उपयोग करते हैं। तो उपर्युक्त स्निपेट में, मैंने दो जीपीयू और आवंटित स्मृति को पहले डिवाइस पर प्रत्येक [(एन/2) युगल और दूसरे पर एन- (एन/2) माना है।

// copy the arrays 'a' and 'b' to the GPUs 
for(int dev=0,pos=0; dev<2; pos+=Ns[dev], dev++) { 
    cudaSetDevice(dev); 
    cudaMemcpy(dev_a[dev], a+pos, Ns[dev] * sizeof(double), cudaMemcpyHostToDevice); 
    cudaMemcpy(dev_b[dev], b+pos, Ns[dev] * sizeof(double), cudaMemcpyHostToDevice); 
} 

:

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

गिरी अपने कोड की धारा की शुरूआत तो की तरह कुछ दे सकता है:

for(int i=0;i<10000;++i) { 
    for(int dev=0; dev<2; dev++) { 
     cudaSetDevice(dev); 
     add<<<NB,NT>>>(dev_a[dev], dev_b[dev], dev_c[dev], Ns[dev]); 
    } 
} 

(अस्वीकरण: ब्राउज़र में लिखा है, संकलित कभी नहीं,, परीक्षण कभी नहीं अपने जोखिम पर उपयोग)।

ध्यान दें कि मैंने आपके कर्नेल कॉल में एक अतिरिक्त तर्क जोड़ा है, क्योंकि कर्नेल के प्रत्येक उदाहरण को प्रक्रिया के लिए सरणी तत्वों की एक अलग संख्या के साथ बुलाया जा सकता है। आवश्यक संशोधनों को पूरा करने के लिए मैं इसे आपको छोड़ दूंगा। लेकिन, फिर, मूल विचार समान है: किसी दिए गए GPU का चयन करने के लिए cudaSetDevice का उपयोग करें, फिर सामान्य कर्नेल पर सामान्य कर्नेल चलाएं, प्रत्येक कर्नेल को अपने अनूठे तर्क प्राप्त होते हैं।

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

+1

बहुत बहुत धन्यवाद talonmies !! आपके सुझाव मुझे अच्छी तरह से शुरू कर देंगे ... मेरी बुरी अंग्रेजी के लिए खेद है – micheletuttafesta

+4

माफी माँगने के लिए कुछ भी नहीं, मैं सवाल और अंग्रेजी को पूरी तरह से लिखा था। – talonmies

+2

'cudaMemcpyAsync' का उपयोग समवर्ती निष्पादन प्राप्त करने के लिए अनुशंसित किया जाएगा, देखें [सीयूडीए बहु-जीपीयू निष्पादन में समेकन] (http://stackoverflow.com/questions/11673154/multiple-gpus-on-cuda-concurrency-issue/35010019# 35,010,019)। – JackOLantern