मैंने हाल ही में एक छोटा वर्ग लिखा है जो संकलन समय पर सॉर्टिंग नेटवर्क उत्पन्न करने के लिए बोस-नेल्सन एल्गोरिदम का उपयोग करता है।
इसका उपयोग 10 नंबर के लिए एक बहुत तेज़ सॉर्ट बनाने के लिए किया जा सकता है।
/**
* A Functor class to create a sort for fixed sized arrays/containers with a
* compile time generated Bose-Nelson sorting network.
* \tparam NumElements The number of elements in the array or container to sort.
* \tparam T The element type.
* \tparam Compare A comparator functor class that returns true if lhs < rhs.
*/
template <unsigned NumElements, class Compare = void> class StaticSort
{
template <class A, class C> struct Swap
{
template <class T> inline void s(T &v0, T &v1)
{
T t = Compare()(v0, v1) ? v0 : v1; // Min
v1 = Compare()(v0, v1) ? v1 : v0; // Max
v0 = t;
}
inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); }
};
template <class A> struct Swap <A, void>
{
template <class T> inline void s(T &v0, T &v1)
{
// Explicitly code out the Min and Max to nudge the compiler
// to generate branchless code.
T t = v0 < v1 ? v0 : v1; // Min
v1 = v0 < v1 ? v1 : v0; // Max
v0 = t;
}
inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); }
};
template <class A, class C, int I, int J, int X, int Y> struct PB
{
inline PB(A &a)
{
enum { L = X >> 1, M = (X & 1 ? Y : Y + 1) >> 1, IAddL = I + L, XSubL = X - L };
PB<A, C, I, J, L, M> p0(a);
PB<A, C, IAddL, J + M, XSubL, Y - M> p1(a);
PB<A, C, IAddL, J, XSubL, M> p2(a);
}
};
template <class A, class C, int I, int J> struct PB <A, C, I, J, 1, 1>
{
inline PB(A &a) { Swap<A, C> s(a, I - 1, J - 1); }
};
template <class A, class C, int I, int J> struct PB <A, C, I, J, 1, 2>
{
inline PB(A &a) { Swap<A, C> s0(a, I - 1, J); Swap<A, C> s1(a, I - 1, J - 1); }
};
template <class A, class C, int I, int J> struct PB <A, C, I, J, 2, 1>
{
inline PB(A &a) { Swap<A, C> s0(a, I - 1, J - 1); Swap<A, C> s1(a, I, J - 1); }
};
template <class A, class C, int I, int M, bool Stop = false> struct PS
{
inline PS(A &a)
{
enum { L = M >> 1, IAddL = I + L, MSubL = M - L};
PS<A, C, I, L, (L <= 1)> ps0(a);
PS<A, C, IAddL, MSubL, (MSubL <= 1)> ps1(a);
PB<A, C, I, IAddL, L, MSubL> pb(a);
}
};
template <class A, class C, int I, int M> struct PS <A, C, I, M, true>
{
inline PS(A &a) {}
};
public:
/**
* Sorts the array/container arr.
* \param arr The array/container to be sorted.
*/
template <class Container> inline void operator() (Container &arr) const
{
PS<Container, Compare, 1, NumElements, (NumElements <= 1)> ps(arr);
};
/**
* Sorts the array arr.
* \param arr The array to be sorted.
*/
template <class T> inline void operator() (T *arr) const
{
PS<T*, Compare, 1, NumElements, (NumElements <= 1)> ps(arr);
};
};
#include <iostream>
#include <vector>
int main(int argc, const char * argv[])
{
enum { NumValues = 10 };
// Arrays
{
int rands[NumValues];
for (int i = 0; i < NumValues; ++i) rands[i] = rand() % 100;
std::cout << "Before Sort: \t";
for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
std::cout << "\n";
StaticSort<NumValues> staticSort;
staticSort(rands);
std::cout << "After Sort: \t";
for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
std::cout << "\n";
}
std::cout << "\n";
// STL Vector
{
std::vector<int> rands(NumValues);
for (int i = 0; i < NumValues; ++i) rands[i] = rand() % 100;
std::cout << "Before Sort: \t";
for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
std::cout << "\n";
StaticSort<NumValues> staticSort;
staticSort(rands);
std::cout << "After Sort: \t";
for (int i = 0; i < NumValues; ++i) std::cout << rands[i] << " ";
std::cout << "\n";
}
return 0;
}
ध्यान दें कि एक if (compare) swap
बयान के बजाय , हम स्पष्ट रूप से न्यूनतम और अधिकतम के लिए टर्नरी ऑपरेटरों को कोड करते हैं। यह संकलक कोड का उपयोग करके संकलक को कुहनी से हलका करने में मदद करता है।
मानक
निम्नलिखित बेंचमार्क clang -O3 के साथ संकलित किए गए हैं और मेरे मध्य 2012 मैकबुक एयर पर चले गए हैं।
यादृच्छिक डेटा को सॉर्ट करना
इसे DarioP के कोड के साथ तुलना करते हुए, यहां मिलीसेकंड की संख्या को आकार 10 की 1 मिलियन 32-बिट अंतर सारणी की तरह लिया जाता है:
हार्डकोड सॉर्ट नेट 10: 88.774 एमएस अस्थायी
बोस-नेल्सन सॉर्ट 10: 27.815 एमएस
इस अस्थायी दृष्टिकोण का उपयोग करके, हम अन्य तत्वों की संख्या के लिए संकलन समय पर छँटाई नेटवर्क भी उत्पन्न कर सकते हैं।
समय (मिलीसेकंड में) विभिन्न आकारों के 1 मिलियन सरणियों को सॉर्ट करने के लिए।
आकार 2, 4, 8 के सरणियों के लिए मिलीसेकंड की संख्या क्रमशः 1.943, 8.655, 20.246 है।
क्रेडिट अनियंत्रित प्रविष्टि प्रकार के लिए ग्लेन Teitelbaum को।
यहां 6 तत्वों के छोटे सरणियों के लिए प्रति घंटे औसत घड़ियां हैं। बेंचमार्क कोड और उदाहरण इस सवाल पर पाए जा सकते हैं:
निश्चित लंबाई 6 इंट सरणी का सबसे तेज़ प्रकार
Direct call to qsort library function : 326.81
Naive implementation (insertion sort) : 132.98
Insertion Sort (Daniel Stutzbach) : 104.04
Insertion Sort Unrolled : 99.64
Insertion Sort Unrolled (Glenn Teitelbaum) : 81.55
Rank Order : 44.01
Rank Order with registers : 42.40
Sorting Networks (Daniel Stutzbach) : 88.06
Sorting Networks (Paul R) : 31.64
Sorting Networks 12 with Fast Swap : 29.68
Sorting Networks 12 reordered Swap : 28.61
Reordered Sorting Network w/ fast swap : 24.63
Templated Sorting Network (this class) : 25.37
यह 6 तत्वों के लिए प्रश्न में सबसे तेज़ उदाहरण के रूप में तेज़ प्रदर्शन करता है।
सॉर्ट किए गए डेटा को सॉर्ट करने के लिए प्रदर्शन
अक्सर, इनपुट सरणियों को पहले से ही हल किया जा सकता है या अधिकतर क्रमबद्ध किया जा सकता है।
ऐसे मामलों में, प्रविष्टि प्रकार बेहतर विकल्प हो सकता है।
आप डेटा के आधार पर एक उपयुक्त छँटाई एल्गोरिथ्म चुनना चाह सकते हैं।
बेंचमार्क के लिए इस्तेमाल किया गया कोड यहां पाया जा सकता है ।
if
स्टेटमेंट की एक श्रृंखला को सबसे अच्छा काम करना चाहिए। छोरों से बचें।