/*************************************************************************** * tests/containers/hash_map/test_hash_map.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include using stxxl::unsigned_type; struct rand_pairs { stxxl::random_number32& rand_; rand_pairs(stxxl::random_number32& rand) : rand_(rand) { } std::pair operator () () { int v = (int)rand_(); return std::pair(v, v); } }; struct hash_int { size_t operator () (int key) const { // a simple integer hash function return (size_t)(key * 2654435761u); } }; struct cmp : public std::less { int min_value() const { return std::numeric_limits::min(); } int max_value() const { return std::numeric_limits::max(); } }; // forced instantiation template class stxxl::unordered_map; struct structA { int x, y; structA() { } structA(int _x, int _y) : x(_x), y(_y) { } }; struct structB { double u, v; }; struct hash_structA { size_t operator () (const structA& key) const { // a simple integer hash function return (size_t)((key.x + key.y) * 2654435761u); } }; struct cmp_structA { bool operator () (const structA& a, const structA& b) const { if (a.x == b.x) return a.y < b.y; return a.x < b.x; } structA min_value() const { return structA(std::numeric_limits::min(), std::numeric_limits::min()); } structA max_value() const { return structA(std::numeric_limits::max(), std::numeric_limits::max()); } }; // forced instantiation of a struct template class stxxl::unordered_map< structA, structB, hash_structA, cmp_structA, 4* 1024, 4 >; void basic_test() { typedef std::pair value_type; const unsigned_type value_size = sizeof(value_type); const unsigned_type n_values = 20000; const unsigned_type n_tests = 10000; // make sure all changes will be buffered (*) const unsigned_type buffer_size = 5 * n_values * (value_size + sizeof(int*)); const unsigned_type mem_to_sort = 32 * 1024 * 1024; const unsigned_type subblock_raw_size = 4 * 1024; const unsigned_type block_size = 4; typedef stxxl::unordered_map unordered_map; typedef unordered_map::iterator iterator; typedef unordered_map::const_iterator const_iterator; stxxl::stats_data stats_begin; unordered_map map; map.max_buffer_size(buffer_size); const unordered_map& cmap = map; // generate random values stxxl::random_number32 rand32; std::vector values1(n_values); std::vector values2(n_values); std::vector values3(n_values / 2); std::generate(values1.begin(), values1.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values2.begin(), values2.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values3.begin(), values3.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); // --- initial import std::cout << "Initial import..."; stats_begin = *stxxl::stats::get_instance(); STXXL_CHECK(map.begin() == map.end()); map.insert(values1.begin(), values1.end(), mem_to_sort); STXXL_CHECK(map.begin() != map.end()); STXXL_CHECK(map.size() == n_values); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // (*) all these values are stored in external memory; the remaining // changes will be buffered in internal memory // --- insert: new (from values2) and existing (from values1) values, with // --- and without checking std::cout << "Insert..."; stats_begin = *stxxl::stats::get_instance(); for (unsigned_type i = 0; i < n_values / 2; i++) { // new without checking map.insert_oblivious(values2[2 * i]); // new with checking std::pair res = map.insert(values2[2 * i + 1]); STXXL_CHECK(res.second && (*(res.first)).first == values2[2 * i + 1].first); // existing without checking map.insert_oblivious(values1[2 * i]); // exiting with checking res = map.insert(values1[2 * i + 1]); STXXL_CHECK(!res.second && (*(res.first)).first == values1[2 * i + 1].first); } STXXL_CHECK(map.size() == 2 * n_values); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // "old" values are stored in external memory, "new" values are stored in // internal memory // --- find: existing (from external and internal memory) and non-existing // --- values std::cout << "Find..."; stats_begin = *stxxl::stats::get_instance(); std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { STXXL_CHECK(cmap.find(values1[i].first) != cmap.end()); STXXL_CHECK(cmap.find(values2[i].first) != cmap.end()); STXXL_CHECK(cmap.find(values3[i].first) == cmap.end()); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // --- insert with overwriting std::cout << "Insert with overwriting..."; stats_begin = *stxxl::stats::get_instance(); std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { value_type value1 = values1[i]; // in external memory value1.second++; map.insert_oblivious(value1); value_type value2 = values2[i]; // in internal memory value2.second++; map.insert_oblivious(value2); } // now check STXXL_CHECK(map.size() == 2 * n_values); // nothing added, nothing removed for (unsigned_type i = 0; i < n_tests; i++) { const_iterator it1 = cmap.find(values1[i].first); const_iterator it2 = cmap.find(values2[i].first); STXXL_CHECK((*it1).second == values1[i].second + 1); STXXL_CHECK((*it2).second == values2[i].second + 1); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // --- erase: existing and non-existing values, with and without checking std::cout << "Erase..."; stats_begin = *stxxl::stats::get_instance(); std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); std::random_shuffle(values3.begin(), values3.end()); for (unsigned_type i = 0; i < n_tests / 2; i++) { // external // existing without checking map.erase_oblivious(values1[2 * i].first); // existing with checking STXXL_CHECK(map.erase(values1[2 * i + 1].first) == 1); } for (unsigned_type i = 0; i < n_tests / 2; i++) { // internal // existing without checking map.erase_oblivious(values2[2 * i].first); // existing with checking STXXL_CHECK(map.erase(values2[2 * i + 1].first) == 1); // non-existing without checking map.erase_oblivious(values3[i].first); // non-existing with checking } STXXL_CHECK(map.size() == 2 * n_values - 2 * n_tests); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); map.clear(); STXXL_CHECK(map.size() == 0); // --- find and manipulate values by []-operator // make sure there are some values in our unordered_map: externally // [0..n/2) and internally [n/2..n) from values1 std::cout << "[ ]-operator..."; stats_begin = *stxxl::stats::get_instance(); map.insert(values1.begin(), values1.begin() + n_values / 2, mem_to_sort); for (unsigned_type i = n_values / 2; i < n_values; i++) { map.insert_oblivious(values1[i]); } // lookup of existing values STXXL_CHECK(map[values1[5].first] == values1[5].second); // external STXXL_CHECK(map[values1[n_values / 2 + 5].first] == values1[n_values / 2 + 5].second); // internal // manipulate existing values ++(map[values1[7].first]); ++(map[values1[n_values / 2 + 7].first]); { const_iterator cit1 = cmap.find(values1[7].first); STXXL_CHECK((*cit1).second == (*cit1).first + 1); const_iterator cit2 = cmap.find(values1[n_values / 2 + 7].first); STXXL_CHECK((*cit2).second == (*cit2).first + 1); } // lookup of non-existing values STXXL_CHECK(map[values2[5].first] == unordered_map::mapped_type()); // assignment of non-existing values map[values2[7].first] = values2[7].second; { const_iterator cit = cmap.find(values2[7].first); STXXL_CHECK((*cit).first == values2[7].second); } STXXL_CHECK(map.size() == n_values + 2); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); map.clear(); STXXL_CHECK(map.size() == 0); // --- additional bulk insert test std::cout << "additional bulk-insert..."; stats_begin = *stxxl::stats::get_instance(); map.insert(values1.begin(), values1.begin() + n_values / 2, mem_to_sort); map.insert(values1.begin() + n_values / 2, values1.end(), mem_to_sort); STXXL_CHECK(map.size() == n_values); // lookup some random values std::random_shuffle(values1.begin(), values1.end()); for (unsigned_type i = 0; i < n_tests; i++) STXXL_CHECK(cmap.find(values1[i].first) != cmap.end()); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // --- test equality predicate unordered_map::key_equal key_eq = map.key_eq(); STXXL_CHECK(key_eq(42, 42)); STXXL_CHECK(!key_eq(42, 6 * 9)); std::cout << "\nAll tests passed" << std::endl; map.buffer_size(); } int main() { basic_test(); return 0; }