// Compares the number of operations with items in gheap-based algorithms // to the number of operations with items in the corresponding STL algorithms. // // Pass -DNDEBUG for eliminating operations related to debugging checks. #include "galgorithm.hpp" #include "gheap.hpp" #include #include #include #include #include #include #include #include // for uintptr_t ( is missing in C++03). #include using namespace std; // Simulates a LRU list of pages, which can contain up to _max_lru_size entries. // Each page's size is PAGE_MASK + 1 bytes. struct lru { typedef list lru_t; static const uintptr_t PAGE_MASK = (((uintptr_t)1) << 12) - 1; // Maximum number of pages in LRU list. static const size_t MAX_LRU_SIZE = 20; // LRU list of pages. Front of the list contains least recently used pages. static lru_t lru_pages; // The number of simulated pagefaults since the last lru::init() call. static int pagefaults; // Resets the model to initial state. static void reset() { lru_pages.clear(); pagefaults = 0; } // Simulates access to a memory pointed by ptr. // Brings the accessed page to the back of LRU list. // If the page is absent in LRU list, then increments pagefaults counter. static void access_ptr(const void *const ptr) { assert(lru_pages.size() <= MAX_LRU_SIZE); const uintptr_t page_num = ((uintptr_t)ptr) & ~PAGE_MASK; lru_t::iterator it = find(lru_pages.begin(), lru_pages.end(), page_num); if (it == lru_pages.end()) { const uintptr_t prev_page_num = page_num - PAGE_MASK - 1; if (count(lru_pages.begin(), lru_pages.end(), prev_page_num) == 0) { // Count pagefault only if the previous page is not in the LRU list. // If the previous page is in the LRU list, then assume that the current // page is already pre-fetched, so no hard pagefault. ++pagefaults; } lru_pages.push_front(page_num); if (lru_pages.size() > MAX_LRU_SIZE) { lru_pages.pop_back(); } assert(lru_pages.size() <= MAX_LRU_SIZE); } else { lru_pages.splice(lru_pages.begin(), lru_pages, it); } } }; lru::lru_t lru::lru_pages; int lru::pagefaults = 0; struct A { static int default_ctors; static int copy_ctors; static int copy_assignments; static int swaps; static int cheap_dtors; static int expensive_dtors; static int move_ctors; static int cheap_move_assignments; static int expensive_move_assignments; static int comparisons; static void reset() { default_ctors = 0; copy_ctors = 0; copy_assignments = 0; swaps = 0; cheap_dtors = 0; expensive_dtors = 0; move_ctors = 0; cheap_move_assignments = 0; expensive_move_assignments = 0; comparisons = 0; lru::reset(); } static void print() { cout << "default_ctors=" << default_ctors << ", copy_ctors=" << copy_ctors << ", copy_assignments=" << copy_assignments << ", swaps=" << swaps << ", cheap_dtors=" << cheap_dtors << ", expensive_dtors=" << expensive_dtors << ", move_ctors=" << move_ctors << ", cheap_move_assignments=" << cheap_move_assignments << ", expensive_move_assignments=" << expensive_move_assignments << ", comparisons=" << comparisons << ", pagefaults=" << lru::pagefaults << endl; } int value; bool has_value() const { return (value >= 0); } int get_value() const { assert(has_value()); lru::access_ptr(this); return value; } void set_value(const int v) { assert(v >= 0); value = v; lru::access_ptr(this); } void set_value(const A &a) { int v = a.get_value(); set_value(v); } void clear_value() { value = -1; lru::access_ptr(this); } A() { ++default_ctors; clear_value(); } A(const int v) { set_value(v); } A(const A &a) { ++copy_ctors; set_value(a); } void operator = (const A &a) { if (this == &a) { return; } assert(has_value()); ++copy_assignments; set_value(a); } ~A() { if (has_value()) { ++expensive_dtors; } else { ++cheap_dtors; } clear_value(); } #ifdef GHEAP_CPP11 A(A &&a) { ++move_ctors; set_value(a); a.clear_value(); } void operator = (A &&a) { if (this == &a) { return; } if (has_value()) { ++expensive_move_assignments; } else { ++cheap_move_assignments; } set_value(a); a.clear_value(); } #endif }; int A::default_ctors; int A::copy_ctors; int A::copy_assignments; int A::swaps; int A::cheap_dtors; int A::expensive_dtors; int A::move_ctors; int A::cheap_move_assignments; int A::expensive_move_assignments; int A::comparisons; namespace std { template <> void swap(A &a, A &b) { ++A::swaps; int tmp = a.get_value(); a.set_value(b); b.set_value(tmp); } } bool operator < (const A &a, const A &b) { ++A::comparisons; return (a.get_value() < b.get_value()); } struct stl { static const char *name() { return "stl"; } template static void push_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { ::std::push_heap(first, last); } template static void pop_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { ::std::pop_heap(first, last); } template static void make_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { ::std::make_heap(first, last); } template static void sort_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { ::std::sort_heap(first, last); } }; struct gtl { typedef gheap<> heap; static const char *name() { return "gheap<>"; } template static void push_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { heap::push_heap(first, last); } template static void pop_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { heap::pop_heap(first, last); } template static void make_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { heap::make_heap(first, last); } template static void sort_heap(const RandomAccessIterator &first, const RandomAccessIterator &last) { heap::sort_heap(first, last); } }; namespace { void init_array(vector &a, const size_t n) { a.clear(); srand(0); generate_n(back_inserter(a), n, rand); } template void test_push_heap(vector &a, const size_t n) { cout << " test_push_heap(" << Heap::name() << "): "; init_array(a, n); A::reset(); for (size_t i = 2; i <= n; ++i) { Heap::push_heap(a.begin(), a.begin() + i); } A::print(); } template void test_pop_heap(vector &a, const size_t n) { cout << " test_pop_heap(" << Heap::name() << "): "; init_array(a, n); Heap::make_heap(a.begin(), a.end()); A::reset(); for (size_t i = 0; i < n - 1; ++i) { Heap::pop_heap(a.begin(), a.end() - i); } A::print(); } template void test_make_heap(vector &a, const size_t n) { cout << " test_make_heap(" << Heap::name() << "): "; init_array(a, n); A::reset(); Heap::make_heap(a.begin(), a.end()); A::print(); } template void test_sort_heap(vector &a, const size_t n) { cout << " test_sort_heap(" << Heap::name() << "): "; init_array(a, n); Heap::make_heap(a.begin(), a.end()); A::reset(); Heap::sort_heap(a.begin(), a.end()); A::print(); } void test_nway_mergesort_avg(vector &a, const size_t n) { cout << " test_nway_mergesort_avg(" << gtl::name() << "): "; typedef galgorithm algorithm; init_array(a, n); A::reset(); algorithm::nway_mergesort(a.begin(), a.end()); A::print(); } void test_nway_mergesort_worst(vector &a, const size_t n) { cout << " test_nway_mergesort_worst(" << gtl::name() << "): "; typedef galgorithm algorithm; // Simulate worst case for SGI STL sort implementation (aka introsort) - // see http://en.wikipedia.org/wiki/Introsort . // Actually n-way mergesort must be free of bad cases. for (size_t i = 0; i < n; ++i) { a[i] = n - i; } init_array(a, n); A::reset(); algorithm::nway_mergesort(a.begin(), a.end()); A::print(); } void test_sort_avg(vector &a, const size_t n) { cout << " test_sort_avg(" << stl::name() << "): "; init_array(a, n); A::reset(); sort(a.begin(), a.end()); A::print(); } void test_sort_worst(vector &a, const size_t n) { cout << " test_sort_worst(" << stl::name() << "): "; // Simulate worst case for SGI STL sort implementation (aka introsort) - // see http://en.wikipedia.org/wiki/Introsort . for (size_t i = 0; i < n; ++i) { a[i] = n - i; } A::reset(); sort(a.begin(), a.end()); A::print(); } } // end of anonymous namespace int main() { const size_t N = 1000000; cout << "N=" << N << endl; vector a; a.reserve(N); test_push_heap(a, N); test_push_heap(a, N); test_pop_heap(a, N); test_pop_heap(a, N); test_make_heap(a, N); test_make_heap(a, N); test_sort_heap(a, N); test_sort_heap(a, N); test_nway_mergesort_avg(a, N); test_nway_mergesort_worst(a, N); test_sort_avg(a, N); test_sort_worst(a, N); }