यहाँ अलेक्जेंड्रू के मूलांक के साथ ज़ारारेक के थ्रेडेड स्मार्ट पिविंग का मिश्रण है। इसके साथ संकलित करें
g++ -std=c++0x -pthread -O3 -march=native sorter_gaussian_radix.cxx -o sorter_gaussian_radix
आप STEP (जैसे ऐड -DSTEP = 11) को परिभाषित करके मूलांक का आकार बदल सकते हैं। मुझे अपने लैपटॉप के लिए सर्वश्रेष्ठ 8 (डिफ़ॉल्ट) मिला।
डिफ़ॉल्ट रूप से, यह समस्या को 4 टुकड़ों में विभाजित करता है और इसे कई थ्रेड्स पर चलाता है। आप कमांड लाइन के लिए एक गहरा पैरामीटर पास करके बदल सकते हैं। तो अगर आपके पास दो कोर हैं, तो इसे चलाएं
sorter_gaussian_radix 50000000 1
और अगर आपके पास 16 कोर हैं
sorter_gaussian_radix 50000000 4
अभी अधिकतम गहराई 6 (64 धागे) है। यदि आप बहुत अधिक स्तर रखते हैं, तो आप कोड को धीमा कर देंगे।
एक चीज जो मैंने भी आजमाई थी, वह इंटेल परफॉर्मेंस प्राइमिटिव्स (IPP) लाइब्रेरी का मूलांक था। अलेक्जेंड्रू के कार्यान्वयन में आईपीपी की तीव्रता कम होती है, जिसमें आईपीपी लगभग 30% धीमा होता है। यह भिन्नता यहाँ भी शामिल है (टिप्पणी की गई है)।
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <ctime>
#include <iostream>
#include <thread>
#include <vector>
#include <boost/cstdint.hpp>
// #include "ipps.h"
#ifndef STEP
#define STEP 8
#endif
const int step = STEP;
const int start_step=24;
const int num_steps=(64-start_step+step-1)/step;
int size;
double *dbuf, *copy;
clock_t c1, c2, c3, c4, c5;
const double distrib[]={-2.15387,
-1.86273,
-1.67594,
-1.53412,
-1.4178,
-1.31801,
-1.22986,
-1.15035,
-1.07752,
-1.00999,
-0.946782,
-0.887147,
-0.830511,
-0.776422,
-0.724514,
-0.67449,
-0.626099,
-0.579132,
-0.53341,
-0.488776,
-0.445096,
-0.40225,
-0.36013,
-0.318639,
-0.27769,
-0.237202,
-0.197099,
-0.157311,
-0.11777,
-0.0784124,
-0.0391761,
0,
0.0391761,
0.0784124,
0.11777,
0.157311,
0.197099,
0.237202,
0.27769,
0.318639,
0.36013,
0.40225,
0.445097,
0.488776,
0.53341,
0.579132,
0.626099,
0.67449,
0.724514,
0.776422,
0.830511,
0.887147,
0.946782,
1.00999,
1.07752,
1.15035,
1.22986,
1.31801,
1.4178,
1.53412,
1.67594,
1.86273,
2.15387};
class Distrib
{
const int value;
public:
Distrib(const double &v): value(v) {}
bool operator()(double a)
{
return a<value;
}
};
void recursive_sort(const int start, const int end,
const int index, const int offset,
const int depth, const int max_depth)
{
if(depth<max_depth)
{
Distrib dist(distrib[index]);
const int middle=std::partition(dbuf+start,dbuf+end,dist) - dbuf;
// const int middle=
// std::partition(dbuf+start,dbuf+end,[&](double a)
// {return a<distrib[index];})
// - dbuf;
std::thread lower(recursive_sort,start,middle,index-offset,offset/2,
depth+1,max_depth);
std::thread upper(recursive_sort,middle,end,index+offset,offset/2,
depth+1,max_depth);
lower.join(), upper.join();
}
else
{
// ippsSortRadixAscend_64f_I(dbuf+start,copy+start,end-start);
c1=clock();
double *dbuf_local(dbuf), *copy_local(copy);
boost::uint64_t mask = (1 << step) - 1;
int cnt[num_steps][mask+1];
boost::uint64_t *ibuf = reinterpret_cast<boost::uint64_t *> (dbuf_local);
for(int i=0;i<num_steps;++i)
for(uint j=0;j<mask+1;++j)
cnt[i][j]=0;
for (int i = start; i < end; i++)
{
for (int w = start_step, v = 0; w < 64; w += step, v++)
{
int p = (~ibuf[i] >> w) & mask;
(cnt[v][p])++;
}
}
c2=clock();
std::vector<int> sum(num_steps,0);
for (uint i = 0; i <= mask; i++)
{
for (int w = start_step, v = 0; w < 64; w += step, v++)
{
int tmp = sum[v] + cnt[v][i];
cnt[v][i] = sum[v];
sum[v] = tmp;
}
}
c3=clock();
for (int w = start_step, v = 0; w < 64; w += step, v++)
{
ibuf = reinterpret_cast<boost::uint64_t *>(dbuf_local);
for (int i = start; i < end; i++)
{
int p = (~ibuf[i] >> w) & mask;
copy_local[start+((cnt[v][p])++)] = dbuf_local[i];
}
std::swap(copy_local,dbuf_local);
}
// Do the last set of reversals
for (int p = start; p < end; p++)
if (dbuf_local[p] >= 0.)
{
std::reverse(dbuf_local+p, dbuf_local + end);
break;
}
c4=clock();
// Insertion sort
for (int i = start+1; i < end; i++) {
double value = dbuf_local[i];
if (value < dbuf_local[i - 1]) {
dbuf_local[i] = dbuf_local[i - 1];
int p = i - 1;
for (; p > 0 && value < dbuf_local[p - 1]; p--)
dbuf_local[p] = dbuf_local[p - 1];
dbuf_local[p] = value;
}
}
c5=clock();
}
}
int main(int argc, char **argv) {
size = atoi(argv[1]);
copy = new double[size];
dbuf = new double[size];
FILE *f = fopen("gaussian.dat", "r");
fread(dbuf, size, sizeof(double), f);
fclose(f);
clock_t c0 = clock();
const int max_depth= (argc > 2) ? atoi(argv[2]) : 2;
// ippsSortRadixAscend_64f_I(dbuf,copy,size);
recursive_sort(0,size,31,16,0,max_depth);
if(num_steps%2==1)
std::swap(dbuf,copy);
// for (int i=0; i<size-1; i++){
// if (dbuf[i]>dbuf[i+1])
// std::cout << "BAD "
// << i << " "
// << dbuf[i] << " "
// << dbuf[i+1] << " "
// << "\n";
// }
std::cout << "Finished after "
<< (double) (c1 - c0) / CLOCKS_PER_SEC << " "
<< (double) (c2 - c1) / CLOCKS_PER_SEC << " "
<< (double) (c3 - c2) / CLOCKS_PER_SEC << " "
<< (double) (c4 - c3) / CLOCKS_PER_SEC << " "
<< (double) (c5 - c4) / CLOCKS_PER_SEC << " "
<< "\n";
// delete [] dbuf;
// delete [] copy;
return 0;
}
संपादित करें : मैंने अलेक्जेंड्रू के कैश सुधारों को लागू किया, और मेरी मशीन पर लगभग 30% समय मुंडा।
EDIT : यह एक पुनरावर्ती प्रकार को लागू करता है, इसलिए इसे अलेक्जेंड्रू की 16 कोर मशीन पर अच्छी तरह से काम करना चाहिए। यह अलेक्जेंड्रू के अंतिम सुधार का भी उपयोग करता है और रिवर्स में से एक को हटा देता है। मेरे लिए, इसने 20% सुधार दिया।
EDIT : एक संकेत बग को ठीक किया गया जो कि 2 से अधिक कोर होने पर अक्षमता का कारण बना।
EDIT : लैम्ब्डा को हटा दिया गया है, इसलिए यह gcc के पुराने संस्करणों के साथ संकलित होगा। इसमें IPP कोड भिन्नता शामिल है जिस पर टिप्पणी की गई है। मैंने 16 कोर पर चलने के लिए दस्तावेज भी तय किए। जहां तक मैं बता सकता हूं, यह सबसे तेज कार्यान्वयन है।
संपादित करें : STEP 8 नहीं होने पर एक बग फिक्स्ड। थ्रेड्स की अधिकतम संख्या 64 तक बढ़ाई गई। कुछ समय की जानकारी जोड़ी गई।