Gần đây tôi đã viết một lớp học nhỏ sử dụng thuật toán Bose-Nelson để tạo ra một mạng sắp xếp theo thời gian biên dịch.
Nó có thể được sử dụng để tạo ra một loại rất nhanh cho 10 số.
/**
* 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;
}
Lưu ý rằng thay vì một if (compare) swap
câu lệnh, chúng tôi mã hóa rõ ràng các toán tử ternary cho min và max. Điều này là để giúp thúc đẩy trình biên dịch sử dụng mã không phân nhánh.
Điểm chuẩn
Các điểm chuẩn sau đây được tổng hợp bằng clang -O3 và chạy trên macbook air giữa năm 2012 của tôi.
Sắp xếp dữ liệu ngẫu nhiên
So sánh nó với mã của DarioP, đây là số mili giây được thực hiện để sắp xếp 1 triệu mảng int 32 bit có kích thước 10:
Hardcoding Sort Net 10: 88,774 ms
Templated Bose-Nelson sort 10: 27.815 ms
Sử dụng phương pháp tạo khuôn mẫu này, chúng tôi cũng có thể tạo các mạng sắp xếp theo thời gian biên dịch cho số lượng phần tử khác.
Thời gian (tính bằng mili giây) để sắp xếp 1 triệu mảng với các kích cỡ khác nhau.
Số mili giây cho các mảng có kích thước 2, 4, 8 lần lượt là 1.943, 8.655, 20.246.
Tín dụng cho Glenn Teitelbaum cho loại chèn không được kiểm soát.
Dưới đây là các đồng hồ trung bình trên mỗi loại cho các mảng nhỏ gồm 6 yếu tố. Mã điểm chuẩn và ví dụ có thể được tìm thấy tại câu hỏi này:
Loại nhanh nhất có độ dài cố định 6 mảng int
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
Nó thực hiện nhanh như ví dụ nhanh nhất trong câu hỏi cho 6 yếu tố.
Hiệu suất để sắp xếp dữ liệu được sắp xếp
Thông thường, các mảng đầu vào có thể đã được sắp xếp hoặc chủ yếu được sắp xếp.
Trong những trường hợp như vậy, sắp xếp chèn có thể là sự lựa chọn tốt hơn.
Bạn có thể muốn chọn một thuật toán sắp xếp phù hợp tùy thuộc vào dữ liệu.
Mã được sử dụng cho điểm chuẩn có thể được tìm thấy ở đây .
if
tuyên bố lồng nhau sẽ hoạt động tốt nhất. Tránh các vòng lặp.