/** * @file sort_test5.cpp * @brief 安定ソートのテスト. * @note * ついでに他のソートも試してる... */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include <algorithm> #include <functional> #include <typeinfo> #include <string> #include <set> #include <vector> #include <list> #include "sort_etc.h" #include "quick_sort.h" #include "quick_sort_y.h" #include "merge_sort.h" #include "radix_sort.h" //#define CHECK_STABLE // 安定ソートの順番チェックをするなら定義 #ifdef CHECK_STABLE // 安定ソートチェックでは値を0〜255に限定しているのでビンソートも試してみる. #include "backet_sort.h" #endif // =========================================================================== using namespace std; #define MAC_STR_2(x) #x #define MAC_STR(x) MAC_STR_2(x) // 時間計測関係 #ifdef _WIN32 #include <windows.h> typedef unsigned __int64 PerfCnt_tick_t; PerfCnt_tick_t PerfCnt_getTick() { PerfCnt_tick_t c; QueryPerformanceCounter((LARGE_INTEGER*)&c); return c; } PerfCnt_tick_t PerfCnt_tickPerSec() { static PerfCnt_tick_t s = 0; if (!s) QueryPerformanceFrequency((LARGE_INTEGER*)&s); return s; } #elif defined LINUX #include <sys/resource.h> typedef double PerfCnt_tick_t; PerfCnt_tick_t PerfCnt_getTick() { struct rusage t; getrusage(RUSAGE_SELF, &t); return t.ru_utime.tv_sec * 1000000.0 + t.ru_utime.tv_usec; } #define PerfCnt_tickPerSec() 1000000.0 #else #include <time.h> typedef clock_t PerfCnt_tick_t; #define PerfCnt_getTick() clock() #define PerfCnt_tickPerSec() CLOCKS_PER_SEC #endif // 乱数生成. class Rand_XorShift { public: Rand_XorShift() { init(); } void init() { x = 123456789, y = 362436069, z = 521288629, w = 88675123; } int operator()() { unsigned t = (x^(x<<11)); x = y; y = z; z = w; w = (w ^ (w>>19)) ^ (t ^ (t>>8)); return (int)w; } private: unsigned x, y, z, w; }; #ifdef __WATCOMC__ // open watcom c++ (1.8)では未実装だったようなので, merge_sortで代用. #define stable_sort merge_sort #endif #ifdef __WATCOMC__ // open watcom c++ (1.8)では未実装だったのでようなので、代用品を用意. #define upper_bound Upper_Bound // std::upper_bound の置換 template <typename FwdIte, class T, class C> FwdIte Upper_Bound(FwdIte first, FwdIte last, const T& val, C cmp) { std::ptrdiff_t size = std::distance(first, last); if (size > 0) { FwdIte mid = first; do { std::ptrdiff_t half_size = size >> 1; std::advance(mid, half_size); if (cmp(val, *mid)) { size = half_size; mid = first; } else { ++mid; first = mid; size = size - half_size - 1; } } while (size > 0); } return first; } #endif // =========================================================================== // テストのソート対象の型関係 // TYPE : このテストでのソート対象の型 // TYPE_SMPCLASS : 0=SmpClass外 1={key_,no_}のみ(8bytes) 2=SmpClass* (4bytes) 3=SmpClass自身(128bytes) となる. #ifdef TYPE_SMPCLASS // サンプルクラスをソート対象にする場合. class SmpClass { public: SmpClass(float key=0, unsigned no=0) : key_(key), no_(no) { #if TYPE_SMPCLASS > 1 memset(rsv_,0,sizeof rsv_); #endif } const bool operator==(const SmpClass& r) const { return key_ == r.key_; } #if TYPE_SMPCLASS == 4 // ※ no_込みでquick_sortする場合用. const bool operator< (const SmpClass& r) const { return val() < r.val(); } unsigned long long val() const { return ((unsigned long long)(*(unsigned*)&key_) << 32) | no_; } #else const bool operator< (const SmpClass& r) const { return key_ < r.key_; } //const bool operator< (const SmpClass& r) const { return val_bin() < r.val_bin(); } float val() const { return key_; } #endif unsigned val_bin() const { return *(unsigned*)&key_; } unsigned no() const { return no_; } // 同一値のときの登録順番チェック用. #if 1 SmpClass(const SmpClass& r) : key_(r.key_), no_(r.no_) { #if TYPE_SMPCLASS > 1 memcpy(rsv_,r.rsv_,sizeof rsv_); #endif } void swap(SmpClass& r) { std::swap(key_,r.key_); std::swap(no_, r.no_); #if TYPE_SMPCLASS > 1 for (unsigned i = 0; i < sizeof(rsv_)/sizeof(rsv_[0]); ++i) std::swap(rsv_[i], r.rsv_[i]); #endif } SmpClass& operator=(const SmpClass& r) { SmpClass(r).swap(*this); return *this; } #endif private: float key_; // 比較で用いる値. unsigned no_; // 登録番号. 安定ソートのチェックに使う. #if TYPE_SMPCLASS == 4 // ※ no_込みでquick_sortする場合用. unsigned rsv_[1]; // 本来は何か処理用のメンバー変数. #elif TYPE_SMPCLASS > 1 // ※ 1の時は key_,no_のみとして扱う. unsigned rsv_[32-1-1]; // 本来は何か処理用のメンバー変数. #endif }; //namespace std { template<> inline void swap<SmpClass>(SmpClass& l, SmpClass& r) { l.swap(r); } } inline void swap(SmpClass& l, SmpClass& r) { l.swap(r); } class SmpClassMgr { public: SmpClassMgr() : no_(0) {} SmpClass& add(float key) { list_.push_back(SmpClass(key, newNo())); return list_.back(); } void init() { list_.clear(); } void term() { list_.clear(); } unsigned newNo() { return no_++; } private: std::list<SmpClass> list_; unsigned no_; }; SmpClassMgr g_smpClassMgr; // #if TYPE_SMPCLASS == 2 // クラスへのポインタをソートするとき #define TYPE SmpClass* #else #define TYPE SmpClass #endif #endif // SmpClass を使わない場合 #ifdef TYPE_DOUBLE #define TYPE double #endif #ifdef TYPE_INT #define TYPE int #endif #ifndef TYPE #error TYPEが未指定. #endif // =========================================================================== // ソート用の比較処理. #if TYPE_SMPCLASS == 2 // 要素がポインタの時用.(実体が小なり) template<typename T> struct Cmp { bool operator()(const T& l, const T& r) const { return *l < *r; } }; #else // ポインタ以外なら 小なり、で比較 #define Cmp std::less #endif // =========================================================================== // 各ソートのテスト.(ソートルーチンの呼び出しを統一するためのワンクッション) #define TEST_SORT_TBL_N(name) \ template<typename T> \ struct test_##name { \ void run(T* data, size_t size) { \ name(data, size, Cmp<T>()); \ } \ } TEST_SORT_TBL_N(bubble_sort); TEST_SORT_TBL_N(selection_sort); TEST_SORT_TBL_N(insertion_sort); TEST_SORT_TBL_N(insertion_sort_y); TEST_SORT_TBL_N(insertion_sort_y_mod); TEST_SORT_TBL_N(shell_sort); TEST_SORT_TBL_N(comb_sort); TEST_SORT_TBL_N(comb_sort11); TEST_SORT_TBL_N(simple_quick_sort); #ifdef USE_QSORT TEST_SORT_TBL_N(c_qsort); #endif #define TEST_SORT_BGN_END(name) \ template<typename T> \ struct test_##name { \ void run(T* data, size_t size) { \ name(data, data+size, Cmp<T>()); \ } \ } TEST_SORT_BGN_END(sort); // std::sort TEST_SORT_BGN_END(stable_sort); // std::stable_sort TEST_SORT_BGN_END(quick_sort); TEST_SORT_BGN_END(quick_sort_y); TEST_SORT_BGN_END(merge_sort); // 基数ソートのためのソートキー取得. #ifdef TYPE_SMPCLASS template<typename T> struct smpclassptr_radix_sort_getkey : public std::unary_function<T, unsigned> { unsigned operator()(const T& p) const { #if TYPE_SMPCLASS == 2 // pointerのとき. return p->val_bin(); #else return p.val_bin(); #endif } }; #endif // 基数ソートのテスト. template<typename T> struct test_radix_sort { void run(T* data, size_t size) { #ifdef TYPE_SMPCLASS radix_sort<8>( data, size, smpclassptr_radix_sort_getkey<T>() ); #elif defined TYPE_INT radix_sort<8>( data, size, radix_sort_ofsaddr_getkey<unsigned, int, 0>() ); #elif defined TYPE_DOUBLE radix_sort<8>( data, size, radix_sort_ofsaddr_getkey<unsigned long long, double, 0>() ); #else #error コンパイル設定がおかしい. #endif } }; #ifdef CHECK_STABLE // 安定ソートチェックでは値を0〜255に限定しているのでビンソートも試してみる. template<typename T> struct test_backet_sort { void run(T* data, size_t size) { backet_sort( data, size, T(0), T(255) ); } }; #endif // upper_boundを用いて安定ソート状態を維持しながら、値をvectorに挿入した結果をコピー. template<typename T> struct test_vec_insert { void run(T* data, size_t size) { std::vector<T> tbl; tbl.reserve(size); tbl.push_back(data[0]); for (unsigned i = 1; i < size; ++i) { tbl.insert( upper_bound(tbl.begin(), tbl.end(), data[i], Cmp<T>()), data[i] ); } std::copy(tbl.begin(), tbl.end(), data); } }; // multisetへ値を挿入してその結果をコピー. template<typename T> struct test_multiset_insert { void run(T* data, size_t size) { std::multiset<T, Cmp<T> > tbl; for (unsigned i = 0; i < size; ++i) { tbl.insert( data[i] ); } std::copy(tbl.begin(), tbl.end(), data); } }; // =========================================================================== // 計測&チェック template<typename T, class SortFunc> double benchmark(SortFunc sortFunc, size_t size, size_t count, unsigned flags, const char* ttl) { unsigned char stableFlag = (flags & 1); unsigned char verboss = (flags & 2); T* data = new T[ size * count + 16 ]; if (data == 0) { printf("not enough memory.\n"); exit(1); } if (verboss) fprintf(stderr, "\t%s:start\n", ttl); #if TYPE_SMPCLASS == 2 // SmpClass*のとき. g_smpClassMgr.init(); #endif // ソート対象のデータを作成. Rand_XorShift rnd; for (unsigned i = 0; i < size * count; ++i) { #if defined CHECK_STABLE // stableのチェックのときは unsigned r = (unsigned)rnd() >> 24; // 値を0〜255にして同一値の数を増やす. #else unsigned r = (unsigned)rnd() >> 1; // 基数ソートの比較を楽にするため負数無し. #endif // 乱数で初期化. #if TYPE_SMPCLASS == 2 data[i] = &g_smpClassMgr.add( r ); #elif TYPE_SMPCLASS > 0 data[i] = T( r, g_smpClassMgr.newNo() ); #else data[i] = T( r ); #endif //printf("%s : %d : %8x\n", ttl, i, data[i]); } // ソート時間の計測. unsigned ofs = 0; PerfCnt_tick_t start_time = PerfCnt_getTick(); // 計測開始 for (unsigned j = 0; j < count; ++j) { sortFunc.run(&data[ofs], size); // ソート中 ofs += size; } PerfCnt_tick_t elapsed = PerfCnt_getTick() - start_time; // 計測終了 #if 1 // 大小関係があっているかをチェック. bool err = false; unsigned stable_chk_count = 0; Cmp<T> cmp; ofs = 0; for (unsigned j = 0; j < count; ++j) { static int err_cnt = 0; for (unsigned i = 0; i < size-1; ++i) { // printf("%d : %d : %d : %8x, %8x\n", algoNo, j, i, data[i], data[i+1]); if (cmp(data[ofs+i+1] , data[ofs+i])) { printf("%s : <%d> [%d] : error\n", ttl, j, i); ++err_cnt; } #if defined TYPE_SMPCLASS && defined CHECK_STABLE // 同一値の時、登録順になっているかをチェック. #if TYPE_SMPCLASS == 2 // 生ポインタ版の時の同一チェック. else if (stableFlag && *data[ofs+i] == *data[ofs+i+1]) #else // ポインタクラス版の時の同一チェック. else if (stableFlag && data[ofs+i] == data[ofs+i+1]) #endif { ++stable_chk_count; #if TYPE_SMPCLASS == 2 // 生ポインタ版の時の同一チェック. if (data[ofs+i]->no() >= data[ofs+i+1]->no()) #else if (data[ofs+i].no() >= data[ofs+i+1].no()) #endif { printf("%s : <%d> [%d] : not stable\n", ttl, j, i); ++err_cnt; } } #endif } if (err_cnt >= 100) { printf("too many errors.\n"); err = 1; break; // exit(1); } ofs += size; } #if 0 //defined TYPE_SMPCLASS && defined CHECK_STABLE if (stableFlag) { printf("%s : stable_chk_count = %d\n", ttl, stable_chk_count); } #endif #endif #if TYPE_SMPCLASS == 2 // SmpClass*のとき. g_smpClassMgr.term(); #endif delete[] data; double e = elapsed * 1.0 / (PerfCnt_tickPerSec()*count); if (err) e = 0; if (verboss) fprintf(stderr, "\t%s:end (%f)\n", ttl, e * 1000000.0); return e; // ソート時間. } // =========================================================================== int main(int argc, char* argv[]) { struct St { size_t size; size_t count; double elaped[32]; }; St st[] = { { 10,10000, }, { 20, 5000, }, { 30, 3333, }, { 40, 2500, }, { 50, 2000, }, { 64, 1562, }, { 100, 1000, }, { 128, 781, }, { 256, 390, }, { 512, 195, }, { 1024, 98, }, { 10000, 10, }, { 100000, 1, }, { 1000000, 1, }, { 10000000, 1, }, {100000000, 1, }, }; enum { tblSz = 32 }; const char* nm[tblSz] = {0}; bool bFastType = 0; unsigned n = sizeof(st) / sizeof(st[0]); unsigned n_low = 13; // 100000 unsigned v = 0; // 状況で実行回数を微調整. --n; if (sizeof(TYPE) > 4) // 大き目のソートならチェックを減らす. --n_low; #if TYPE_SMPCLASS >= 2 // SmpClassは比較が重めになるのでチェック減らす. --n; --n_low; #endif unsigned mul = 1; // オプションチェック for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-low") == 0) { // 低スペックでの実行用. n -= 1; n_low -= 1; } else if (strcmp(argv[i], "-high") == 0) { // 高スペック環境の場合. ++n; } else if (strcmp(argv[i], "-v") == 0) { // verboss. v = 2; } else if (strncmp(argv[i], "-m",2) == 0) { // 回数をm倍する. mul = strtol(argv[i]+2,0,0); if (mul < 1) mul = 1; } else if (strcmp(argv[i], "-fast") == 0) { // 手っ取り早く、速いアルゴリズムのみ試す. bFastType = 1; } } // SJIS \ 問題回避で ソートをSORT に置換. fprintf(stderr, "\n%s (%dbytes) の SORT テスト\n", MAC_STR(TYPE), sizeof(TYPE) ); // 各ソートのチェック. for (unsigned i = 0; i < n; ++i) { St* t = &st[i]; unsigned count = t->count * mul; if (v) fprintf(stderr, "個数 %9d, 処理回数 %9d\n", t->size, count); unsigned k = 0; if (i < n_low && !bFastType) { nm[k] = "バブル SORT "; t->elaped[k] = benchmark<TYPE>(test_bubble_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; // nm[k] = "選択 SORT "; t->elaped[k] = benchmark<TYPE>(test_selection_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; nm[k] = "挿入 SORT "; t->elaped[k] = benchmark<TYPE>(test_insertion_sort<TYPE>() , t->size, count, 1|v, nm[k]); ++k; nm[k] = "挿入 SORT (yane) "; t->elaped[k] = benchmark<TYPE>(test_insertion_sort_y<TYPE>() , t->size, count, 1|v, nm[k]); ++k; // nm[k] = "挿入 SORT (y改) "; t->elaped[k] = benchmark<TYPE>(test_insertion_sort_y_mod<TYPE>() , t->size, count, 1|v, nm[k]); ++k; } k = 5; { nm[k] = "シェル SORT "; t->elaped[k] = benchmark<TYPE>(test_shell_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; nm[k] = "コム SORT "; t->elaped[k] = benchmark<TYPE>(test_comb_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; // nm[k] = "コム SORT 11 "; t->elaped[k] = benchmark<TYPE>(test_comb_sort11<TYPE>() , t->size, count, 0|v, nm[k]); ++k; nm[k] = "QUICK SORT(単純) "; t->elaped[k] = benchmark<TYPE>(test_simple_quick_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; } k = 9; { nm[k] = "QUICK SORT(+挿入) "; t->elaped[k] = benchmark<TYPE>(test_quick_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; nm[k] = "QUICK SORT(+yane) "; t->elaped[k] = benchmark<TYPE>(test_quick_sort_y<TYPE>() , t->size, count, 0|v, nm[k]); ++k; nm[k] = "std::sort "; t->elaped[k] = benchmark<TYPE>(test_sort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; nm[k] = "std::stable_sort "; t->elaped[k] = benchmark<TYPE>(test_stable_sort<TYPE>() , t->size, count, 1|v, nm[k]); ++k; nm[k] = "マージ SORT "; t->elaped[k] = benchmark<TYPE>(test_merge_sort<TYPE>() , t->size, count, 1|v, nm[k]); ++k; nm[k] = "基数 SORT "; t->elaped[k] = benchmark<TYPE>(test_radix_sort<TYPE>() , t->size, count, 1|v, nm[k]); ++k; } k = 16; if (i < n_low+1 && !bFastType) { nm[k] = "multiset insert "; t->elaped[k] = benchmark<TYPE>(test_multiset_insert<TYPE>() , t->size, count, 0|v, nm[k]); ++k; } k = 17; if (i < n_low && count <= 100000 && !bFastType) { nm[k] = "vector insert "; t->elaped[k] = benchmark<TYPE>(test_vec_insert<TYPE>() , t->size, count, 1|v, nm[k]); ++k; } #ifdef USE_QSORT k = 18; if (!bFastType) { nm[k] = "c qsort "; t->elaped[k] = benchmark<TYPE>(test_c_qsort<TYPE>() , t->size, count, 0|v, nm[k]); ++k; } #endif #if defined CHECK_STABLE && defined TYPE_INT // 安定ソートチェックでは値を0〜255に限定しているのでビンソートも試してみる. k = 19; { nm[k] = "BACKET SORT "; t->elaped[k] = benchmark<TYPE>(test_backet_sort<TYPE>() , t->size, count, 1|v, nm[k]); ++k; } #endif } // 実行結果の出力 printf("\n*** %s (%dbytes) の SORT テスト\n", MAC_STR(TYPE), sizeof(TYPE) ); printf("\n"); printf(",%18s ,", "個数"); for (unsigned i = 0; i < n; ++i) { printf("%10d個,", st[i].size); }; printf("\n"); printf(",%18s ,", "回数(結果は平均)"); for (unsigned i = 0; i < n; ++i) { printf(" (%5d回),", st[i].count*mul); }; printf("\n"); for (unsigned j = 0; j < tblSz; ++j) { if (nm[j] == 0) continue; printf(",%-18s ,", nm[j]); for (unsigned i = 0; i < n; ++i) { double e = st[i].elaped[j]*1000000.0; if (e > 0) printf("%12.3f,", e); else printf("%12s,", "---"); } printf("\n"); } printf("\n"); return 0; }