अद्यतन GPU संस्करण
__global__ void hash (float *largeFloatingPointArray,int largeFloatingPointArraySize, int *dictionary, int size, int num_blocks)
{
int x = (threadIdx.x + blockIdx.x * blockDim.x); // Each thread of each block will
float y; // compute one (or more) floats
int noOfOccurrences = 0;
int a;
while(x < size) // While there is work to do each thread will:
{
dictionary[x] = 0; // Initialize the position in each it will work
noOfOccurrences = 0;
for(int j = 0 ;j < largeFloatingPointArraySize; j ++) // Search for floats
{ // that are equal
// to it assign float
y = largeFloatingPointArray[j]; // Take a candidate from the floats array
y *= 10000; // e.g if y = 0.0001f;
a = y + 0.5; // a = 1 + 0.5 = 1;
if (a == x) noOfOccurrences++;
}
dictionary[x] += noOfOccurrences; // Update in the dictionary
// the number of times that the float appears
x += blockDim.x * gridDim.x; // Update the position here the thread will work
}
}
यह एक मैं सिर्फ छोटे आदानों के लिए परीक्षण किया है, क्योंकि मैं मैं अपने लैपटॉप का परीक्षण कर रहा हूँ। फिर भी, यह काम किया। हालांकि, आगे परीक्षण टेस्ट करना आवश्यक है।
अद्यतन अनुक्रमिक संस्करण
मैं सिर्फ इस अनुभवहीन संस्करण है कि कम से कम 20 सेकंड (पहले से ही डेटा उत्पन्न करने के लिए समारोह की गिनती) में 30,000,000 के लिए अपने एल्गोरिथ्म प्रदर्शन किया।
असल में, यह आपके फ्लोट की सरणी को सॉर्ट करता है। यह क्रमबद्ध सरणी पर यात्रा करेगा, यह मानने के लिए कि सरणी में लगातार मूल्य कितनी बार दिखाई देता है और फिर यह मान एक शब्दकोश में प्रदर्शित होने की संख्या के साथ एक शब्द में डाल देता है।
आप इस्तेमाल किए गए unordered_map के बजाय क्रमबद्ध मानचित्र का उपयोग कर सकते हैं।
यहाँ कोड:
#include <stdio.h>
#include <stdlib.h>
#include "cuda.h"
#include <algorithm>
#include <string>
#include <iostream>
#include <tr1/unordered_map>
typedef std::tr1::unordered_map<float, int> Mymap;
void generator(float *data, long int size)
{
float LO = 0.0;
float HI = 100.0;
for(long int i = 0; i < size; i++)
data[i] = LO + (float)rand()/((float)RAND_MAX/(HI-LO));
}
void print_array(float *data, long int size)
{
for(long int i = 2; i < size; i++)
printf("%f\n",data[i]);
}
std::tr1::unordered_map<float, int> fill_dict(float *data, int size)
{
float previous = data[0];
int count = 1;
std::tr1::unordered_map<float, int> dict;
for(long int i = 1; i < size; i++)
{
if(previous == data[i])
count++;
else
{
dict.insert(Mymap::value_type(previous,count));
previous = data[i];
count = 1;
}
}
dict.insert(Mymap::value_type(previous,count)); // add the last member
return dict;
}
void printMAP(std::tr1::unordered_map<float, int> dict)
{
for(std::tr1::unordered_map<float, int>::iterator i = dict.begin(); i != dict.end(); i++)
{
std::cout << "key(string): " << i->first << ", value(int): " << i->second << std::endl;
}
}
int main(int argc, char** argv)
{
int size = 1000000;
if(argc > 1) size = atoi(argv[1]);
printf("Size = %d",size);
float data[size];
using namespace __gnu_cxx;
std::tr1::unordered_map<float, int> dict;
generator(data,size);
sort(data, data + size);
dict = fill_dict(data,size);
return 0;
}
आप पुस्तकालय जोर आप मशीन में स्थापित है, तो आप इस का उपयोग करना चाहिए: सुनिश्चित करें कि के लिए
#include <thrust/sort.h>
thrust::sort(data, data + size);
इस
sort(data, data + size);
के बजाय
यह तेजी से होगा।
मूल पोस्ट
"मैं जो 10 containin एक बड़े सरणी है एक सांख्यिकीय आवेदन पर काम कर रहा हूँ - अंक मान चल के 30 लाखों लोगों की"।
"क्या ऐसी गणनाओं को तेज करने के लिए जीपीयू का उपयोग करने के लिए यह संभव है (और यह समझ में आता है)?"
हाँ, यह है। एक महीने पहले मैंने पूरी तरह से जीपीयू पर आण्विक गतिशील सिमुलेशन लगाया था। कणों में से एक, जो कणों के जोड़ों के बीच बल की गणना करता है, 500,000 युगल के साथ प्रत्येक 6 सरणी प्राप्त करता है, कुल 3 मिलियन युगल (22 एमबी)।
तो आप 30 मिलियन फ्लोट प्वाइंट डालने की योजना बना रहे हैं, यह लगभग 114 एमबी वैश्विक मेमोरी है, इसलिए यह कोई समस्या नहीं है, यहां तक कि मेरे लैपटॉप में 250 एमबी भी है।
गणना आपके मामले में कोई समस्या हो सकती है? आण्विक गतिशील (एमडी) के साथ अपने अनुभव के आधार पर मैं नहीं कहता हूं। अनुक्रमिक एमडी संस्करण को पूरा होने में लगभग 25 घंटे लगते हैं जबकि GPU में 45 मिनट लगते हैं। आपने कहा कि आपके आवेदन में कुछ घंटे लगे हैं, जो आपके कोड उदाहरण में भी आधारित है, यह आण्विक गतिशील से नरम दिखता है।
ग में:
for(int i = 0; i < N; i++)
c[i] = a[i] + b[i];
Cuda में
__global__ void add(double *fx, double *fy, double *fz,
double *x, double *y, double *z,...){
int pos = (threadIdx.x + blockIdx.x * blockDim.x);
...
while(pos < particles)
{
for (i = 0; i < particles; i++)
{
if(//inside of the same radius)
{
// calculate force
}
}
pos += blockDim.x * gridDim.x;
}
}
Cuda में एक कोड का एक सरल उदाहरण दो 2 डी सरणियों का योग हो सकता है:
यहाँ बल गणना उदाहरण है :
__global__ add(int *c, int *a, int*b, int N)
{
int pos = (threadIdx.x + blockIdx.x)
for(; i < N; pos +=blockDim.x)
c[pos] = a[pos] + b[pos];
}
Cuda में आप मूल रूप से प्रत्येक थ्रेड द्वारा यात्रा और भाग के लिए प्रत्येक ले लिया,
1) threadIdx.x + blockIdx.x*blockDim.x;
प्रत्येक ब्लॉक 0 से N-1 के लिए एक आईडी (एन ब्लॉक की संख्या अधिकतम) है और प्रत्येक ब्लॉक धागे की एक एक्स नंबर है 0 से एक्स -1 तक आईडी के साथ।
1) आपको पुनरावृत्ति के लिए देता है कि प्रत्येक थ्रेड उस आईडी और ब्लॉक आईडी पर आधारित गणना करेगा जहां थ्रेड है, blockDim.x एक ब्लॉक है जो थ्रेड की संख्या है।
तो अगर आप 2 ब्लॉक 10 धागे से हर एक है और एक एन = 40, है:
__global__ hash (float *largeFloatingPointArray, int *dictionary)
// You can turn the dictionary in one array of int
// here each position will represent the float
// Since x = 0f; x < 100f; x += 0.0001f
// you can associate each x to different position
// in the dictionary:
// pos 0 have the same meaning as 0f;
// pos 1 means float 0.0001f
// pos 2 means float 0.0002f ect.
// Then you use the int of each position
// to count how many times that "float" had appeared
int x = blockIdx.x; // Each block will take a different x to work
float y;
while(x < 1000000) // x < 100f (for incremental step of 0.0001f)
{
int noOfOccurrences = 0;
float z = converting_int_to_float(x); // This function will convert the x to the
// float like you use (x/0.0001)
// each thread of each block
// will takes the y from the array of largeFloatingPointArray
for(j = threadIdx.x; j < largeFloatingPointArraySize; j += blockDim.x)
{
y = largeFloatingPointArray[j];
if (z == y)
{
noOfOccurrences++;
}
}
if(threadIdx.x == 0) // Thread master will update the values
atomicAdd(&dictionary[x], noOfOccurrences);
__syncthreads();
}
: मैं क्या यह CUDA में हो सकता है की इस मसौदा बनाया
Thread 0 Block 0 will execute pos 0
Thread 1 Block 0 will execute pos 1
...
Thread 9 Block 0 will execute pos 9
Thread 0 Block 1 will execute pos 10
....
Thread 9 Block 1 will execute pos 19
Thread 0 Block 0 will execute pos 20
...
Thread 0 Block 1 will execute pos 30
Thread 9 Block 1 will execute pos 39
अपने कोड को खोज रहे हैं
आपको परमाणु का उपयोग करना है क्योंकि अलग-अलग ब्लॉक से अलग-अलग थ्रेड एक ही समय में नहीं लिख सकते हैं, इसलिए आपको पारस्परिक बहिष्करण अनिश्चित करना होगा।
यह केवल एक ही दृष्टिकोण है जिसे आप ब्लॉक के बजाय बाहरी लूप के पुनरावृत्तियों को भी दे सकते हैं।
ट्यूटोरियल
रोब किसान द्वारा डॉ डोब्स जर्नल श्रृंखला CUDA: Supercomputing for the masses उत्कृष्ट है और सिर्फ अपनी चौदह किश्तों में सब कुछ के बारे में शामिल किया गया। यह बल्कि धीरे-धीरे शुरू होता है और इसलिए काफी शुरुआती-अनुकूल है।
और दूसरे:
अंतिम आइटम पर एक नजर डालें, तो आप CUDA जानने के लिए कई लिंक मिल जाएगा।
OpenCL: OpenCL Tutorials | MacResearch
कोई मौका करके, आप C/C++ में अपने कोड में परिवर्तित करने की कोशिश की है? नीचे दिए गए कोड स्निपेट के आधार पर, आप सी # का उपयोग कर रहे हैं। यदि आपका कोड शब्दकोश के लिए स्मृति आवंटित करने में अपना अधिकांश समय बिताता है तो मुझे आश्चर्य नहीं होगा। – Martin
नहीं, लेकिन शब्दकोश के लिए स्मृति आवंटित करने में केवल कुछ एमएस या उससे कम समय लगता है और CPU उपयोग हमेशा 93% - 98% के बीच होता है, इसलिए मुझे लगता है कि स्मृति इस मामले में (मुख्य) प्रदर्शन समस्या नहीं है। – Mike
मुझे सच में लगता है कि आपका कोड GPU का उपयोग किये बिना तेजी से चमक रहा होना चाहिए। क्या आपने एक शब्दकोश का उपयोग करने से दूर जाने की कोशिश की है (सब कुछ आवंटित करें)। Foreach का उपयोग न करें लेकिन के लिए। जीपीयू अधिक है। सी में पूरी चीज दोबारा लिखें, यह आपको स्मृति आवंटन के बारे में सोचने के लिए मजबूर करेगा। – Martin