필자는 최근 Bose-Nelson 알고리즘을 사용하여 컴파일 타임에 정렬 네트워크를 생성 하는 작은 클래스 를 작성했습니다.
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
명령문 대신 min 및 max에 대한 삼항 연산자를 명시 적으로 코딩합니다. 이것은 분기없는 코드를 사용하도록 컴파일러를 조금씩 움직입니다.
벤치 마크
다음 벤치 마크는 clang -O3으로 컴파일되어 2012 년 중반 macbook air에서 실행되었습니다.
무작위 데이터 정렬
DarioP의 코드와 비교해 보면, 다음은 크기가 10 인 32 만 int 배열을 백만 초 단위로 정렬하는 데 걸린 시간입니다 (밀리 초).
Hardcoded Sort Net 10 : 88.774ms
템플릿 Bose-Nelson sort 10 : 27.815ms
이 템플릿 방식을 사용하면 컴파일 타임에 다른 요소 수에 대한 정렬 네트워크를 생성 할 수도 있습니다.
다양한 크기의 백만 개의 배열을 정렬하는 데 걸리는 시간 (밀리 초)입니다.
크기 2, 4, 8의 배열에 대한 밀리 초 수는 각각 1.943, 8.655, 20.246입니다.
크레딧 글렌 Teitelbaum 펼쳐진 삽입 정렬합니다.
다음은 6 개 요소의 작은 배열에 대한 정렬 당 평균 클럭입니다. 이 질문에서 벤치 마크 코드와 예제를 찾을 수 있습니다.
가장 빠른 종류의 고정 길이 6 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
6 요소에 대한 질문에서 가장 빠른 예만큼 빠릅니다.
정렬 된 데이터 정렬 성능
종종 입력 배열이 이미 정렬되었거나 대부분 정렬되어있을 수 있습니다.
이러한 경우 삽입 정렬이 더 나은 선택이 될 수 있습니다.
데이터에 따라 적절한 정렬 알고리즘을 선택할 수 있습니다.
벤치 마크에 사용 된 코드는 여기 에서 찾을 수 있습니다 .
if
진술이 가장 효과적입니다. 루프를 피하십시오.