এখানে জাজেরেকের থ্রেডেড স্মার্ট পাইভটিংয়ের সাথে আলেকজান্দ্রির রডিক্স সাজানোর মিশ্রণ রয়েছে। এটি দিয়ে সংকলন
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 থ্রেড)। আপনি যদি অনেকগুলি স্তর রাখেন তবে আপনি কোডটি কমিয়ে দেবেন।
একটি জিনিস আমি চেষ্টা করেছিলাম সেটি হ'ল ইনটেল পারফরম্যান্স প্রিমিটিভস (আইপিপি) লাইব্রেরি থেকে রেডিক্স সাজান। আলেকজান্দ্রুর প্রয়োগ বাস্তবায়িতভাবে আইপিপিকে কষ্ট দেয়, আইপিপি প্রায় 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% সময় কেটে গেছে।
সম্পাদনা : এটি একটি পুনরাবৃত্তিক বাছাই কার্যকর করে, তাই এটি আলেকজান্দ্রুর 16 কোর মেশিনে ভালভাবে কাজ করা উচিত। এটি এর আলেকজান্দ্রুর শেষ উন্নতিও ব্যবহার করে এবং বিপরীতগুলির একটি অপসারণ করে। আমার জন্য, এটি একটি 20% উন্নতি দিয়েছে।
সম্পাদনা : 2 টি কোরের বেশি থাকাকালীন একটি সাইন বাগ সমাধান করা হয়েছে যা অদক্ষতার কারণ হয়ে দাঁড়ায়।
সম্পাদনা : ল্যাম্বদা সরানো হয়েছে, সুতরাং এটি জিসিসির পুরানো সংস্করণগুলির সাথে সংকলন করবে। এটিতে মন্তব্য করা আইপিপি কোড প্রকরণের অন্তর্ভুক্ত। আমি 16 টি কোর চালানোর জন্য ডকুমেন্টেশনও স্থির করেছি। আমি যতদূর বলতে পারি এটি দ্রুততম বাস্তবায়ন।
সম্পাদনা : স্টিপ ৮ না হলে একটি বাগ স্থির করা হয়েছে threads