├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── sho.h ├── sho.natvis └── test ├── makefile └── test_sho.cc /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/* 2 | !test/test_sho.cc 3 | !test/makefile 4 | #README.md# 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | compiler: 8 | - clang 9 | - gcc 10 | 11 | dist: trusty 12 | sudo: false 13 | 14 | script: cd test && make && make test 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Copyright (c) 2017, Gregory Popovitch - greg7mdp@gmail.com 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // ----------------------------------------------------------------------------- 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/greg7mdp/sho.svg?branch=master)](https://travis-ci.org/greg7mdp/sho) 2 | 3 | # SHO: The Small Hash Optimization 4 | 5 | When you need a key to value map with the fastest lookup, it is hard to beat a good hash map. Hash maps are so efficient that they are sometimes used as essential parts of other data structures, causing many instances of a hash map to exist in memory. 6 | 7 | I decided to implement this class after working with Michael, a user of my [sparsepp](https://github.com/greg7mdp/sparsepp) hash map. His application builds a trie with close to one hundred million nodes, and each node keeps track of its children using a hash map. He originally used [std::unordered_map](http://www.cplusplus.com/reference/unordered_map/unordered_map/), but then switched to using [sparsepp](https://github.com/greg7mdp/sparsepp) in order to reduce the memory usage of his application. 8 | 9 | Michael was pretty happy with the reduced memory usage of sparsepp (memory usage went down from 49GB to 33GB, and execution time was reduced a little from 13 to 12 minutes. However we remained in contact and discussed whether using a custom allocator would help reducing memory usage even further. 10 | 11 | When testing with the new optional [allocator](https://github.com/greg7mdp/sparsepp/blob/master/sparsepp/spp_dlalloc.h) from sparsepp based on Doug Lea [malloc](http://g.oswego.edu/dl/html/malloc.html), Michael reported that memory usage was *significantly increased*, not what we had hoped for! 12 | 13 | What happened was that Michael's application creates close to one hundred million hash maps. While some of them are very large, most have zero or very few entries. The new allocator, which reserve a private memory space for each hash map, was very ill suited for the task and increased greatly the memory usage of all those (almost) empty hash maps. 14 | 15 | So we decided to try a small hash optimization, which would use a small array of `` pairs for small hash maps, and would expand to a full blown hash map (dynamically allocated) when the small array overflowed. This is similar to LLVM's [Small Vector Class](http://llvm.org/docs/doxygen/html/classllvm_1_1SmallVector.html), which uses a local array to avoid memory allocations (and fragmentation) for small vectors. 16 | 17 | This small hash optimization was a success even beyond our expectations for Michael's project, reducing the memory usage further from 33GB to 17GB, and the execution time from 12 to 11 minutes. 18 | 19 | ## Installation 20 | 21 | No compilation is needed, as this is a header-only library. The installation consist in copying the sho.h header wherever it will be convenient to include in your project(s). 22 | 23 | ## Example 24 | 25 | Suppose you are using a std::unordered_map through a typedef such as: 26 | 27 | 28 | ```c++ 29 | typedef std::unordered_map MyMap; 30 | ``` 31 | 32 | Adding the Small Hash Optimization is as easy as including sho.h and updating the typedef as follows: 33 | 34 | ```c++ 35 | #include 36 | 37 | typedef sho::smo<3, std::unordered_map, uint32_t, void *> MyMap; 38 | ``` 39 | 40 | The number 3 passed as the first template parameter means that the local array cache will be dimensioned to contain a maximum of 3 entries (you may want to test a few numbers to see which one offers the best size and speed improvement for your use case. 41 | 42 | The second template parameter is the hash map used when the array overflows. I have tested the code with both [std::unordered_map](http://www.cplusplus.com/reference/unordered_map/unordered_map/) and [sparsepp](https://github.com/greg7mdp/sparsepp) 43 | 44 | ## API support 45 | 46 | I implemented sho.h over a week-end, and it does not yet support the full std::unordered_map interface. Feel free to enter issues for extra APIs, or even better send me pull requests. 47 | 48 | ## TODO 49 | 50 | * Properly store the comparison functor and the allocator in sho, with minimal memory cost 51 | * Support maps parametrized with types without default constructors 52 | * support full std::unordered_map API 53 | * support std::unordered_set 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /sho.h: -------------------------------------------------------------------------------- 1 | #ifndef sho__h__ 2 | #define sho__h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // --------------------------------------------------------------------- 12 | // S M A L L H A S H O P T I M I Z A T I O N 13 | // ------------------------------------------------ 14 | // 15 | // When you have a lot of very small hash maps with just zero to a couple 16 | // entries => store a few element in a small array of possible, else 17 | // allocate a full hash_map. 18 | // 19 | // Assumptions: 20 | // - we assume that the value of a pointer is always greater then N. 21 | // --------------------------------------------------------------------- 22 | 23 | namespace sho 24 | { 25 | 26 | // ---------------- 27 | template 28 | class ShoIterator : public std::iterator 29 | { 30 | public: 31 | typedef Val value_type; 32 | 33 | template 34 | ShoIterator(const ShoIterator &o) : _p((value_type *)o._p), _it(*((MapIt *)&o._it)) {} 35 | 36 | explicit ShoIterator(const value_type *p = 0) : _p((value_type *)p) {} 37 | explicit ShoIterator(const MapIt &o) : _p(0), _it(o) {} 38 | 39 | ShoIterator& operator++() { if (_p) ++_p; else ++_it; return *this; } 40 | ShoIterator& operator--() { if (_p) --_p; else --_it; return *this; } 41 | ShoIterator operator++(int) { ShoIterator tmp(*this); ++*this; return tmp; } 42 | ShoIterator operator--(int) { ShoIterator tmp(*this); --*this; return tmp; } 43 | 44 | bool operator==(const ShoIterator &o) const { return _p ? _p == o._p : _it == o._it; } 45 | bool operator!=(const ShoIterator &o) const { return !(*this == o); } 46 | 47 | value_type& operator*() const { return _p ? *_p : *_it; } 48 | value_type* operator->() const { return &(operator*()); } 49 | 50 | const MapIt& getMapIt() const { assert(!_p); return _it; } 51 | value_type * getPtr() const { assert(_p); return _p; } 52 | 53 | value_type *_p; 54 | MapIt _it; 55 | }; 56 | 57 | // --------------------------------ShoI---------------------------------------- 58 | // S M A L L M A P O P T I M I Z A T I O N 59 | // ------------------------------------------------------------------------ 60 | template class RefMap, 62 | class K, 63 | class V, 64 | class Hash = std::hash, 65 | class Pred = std::equal_to, 66 | class Alloc = std::allocator > > 67 | class smo 68 | { 69 | public: 70 | typedef RefMap Map; 71 | typedef typename Map::key_type key_type; 72 | typedef typename Map::mapped_type mapped_type; 73 | typedef typename Map::value_type value_type; 74 | typedef typename Map::hasher hasher; 75 | typedef typename Map::key_equal key_equal; 76 | typedef typename Map::allocator_type allocator_type; 77 | typedef std::pair mutable_value_type; 78 | 79 | typedef typename Map::size_type size_type; 80 | typedef typename Map::difference_type difference_type; 81 | 82 | typedef typename Map::iterator map_iterator; 83 | typedef typename Map::const_iterator map_const_iterator; 84 | 85 | 86 | // ---------------- 87 | typedef ShoIterator iterator; 88 | typedef ShoIterator const_iterator; 89 | 90 | smo(size_type = 0) : _cnt(0) {} 91 | 92 | ~smo() 93 | { 94 | if (_hasMap()) 95 | _cleanMap(); 96 | } 97 | 98 | smo(const smo &o) : _cnt(0) 99 | { 100 | if (o._hasMap()) 101 | _cnt = (uintptr_t)(new Map(o._getMap())); 102 | else 103 | _copyLocal(o); 104 | } 105 | 106 | smo& operator=(const smo &o) 107 | { 108 | if (this == &o) 109 | return *this; 110 | 111 | if (_hasMap()) 112 | _cleanMap(); 113 | if (o._hasMap()) 114 | _cnt = (uintptr_t)(new Map(o._getMap())); 115 | else 116 | _copyLocal(o); 117 | return *this; 118 | } 119 | 120 | // Iterator functions 121 | iterator begin() const 122 | { 123 | return _hasMap() ? iterator(_getMap()->begin()) : iterator(&_items[0]); 124 | } 125 | 126 | iterator end() const 127 | { 128 | return _hasMap() ? iterator(_getMap()->end()) : iterator(&_items[_cnt]); 129 | } 130 | 131 | const_iterator cbegin() const 132 | { 133 | return _hasMap() ? const_iterator(_getMap()->begin()) : const_iterator(&_items[0]); 134 | } 135 | 136 | const_iterator cend() const 137 | { 138 | return _hasMap() ? const_iterator(_getMap()->end()) : const_iterator(&_items[_cnt]); 139 | } 140 | 141 | iterator find(const key_type& key) 142 | { 143 | if (_hasMap()) 144 | return iterator(_getMap()->find(key)); 145 | else 146 | for (size_t i=0; i<_cnt; ++i) 147 | if (key_equal()(_items[i].first, key)) 148 | return iterator(&_items[i]); 149 | return end(); 150 | } 151 | 152 | const_iterator find(const key_type& key) const 153 | { 154 | if (_hasMap()) 155 | return const_iterator(_getMap()->find(key)); 156 | else 157 | for (size_t i=0; i<_cnt; ++i) 158 | if (key_equal()(_items[i].first, key)) 159 | return const_iterator(&_items[i]); 160 | return cend(); 161 | } 162 | 163 | size_type erase(const key_type& k) 164 | { 165 | if (_hasMap()) 166 | return _getMap()->erase(k); 167 | 168 | for (size_t i=0; i<_cnt; ++i) 169 | { 170 | if (key_equal()(_items[i].first, k)) 171 | { 172 | // rotate item to delete to last position 173 | std::rotate((mutable_value_type *)&_items[i], 174 | (mutable_value_type *)&_items[i+1], 175 | (mutable_value_type *)&_items[_cnt]); 176 | --_cnt; 177 | allocator_type().destroy(&_items[_cnt]); 178 | return 1; 179 | } 180 | } 181 | return 0; 182 | } 183 | 184 | iterator erase(const_iterator it) 185 | { 186 | if (_hasMap()) 187 | { 188 | map_iterator res = _getMap()->erase(it.getMapIt()); 189 | return iterator(res); 190 | } 191 | const value_type *cur = it.getPtr(); 192 | size_t idx = cur - &_items[0]; 193 | assert(idx <= N); 194 | 195 | if (idx < N && _cnt) 196 | { 197 | // rotate item to delete to last position 198 | std::rotate((mutable_value_type *)cur, 199 | (mutable_value_type *)cur+1, 200 | (mutable_value_type *)&_items[_cnt]); 201 | --_cnt; 202 | allocator_type().destroy(&_items[_cnt]); 203 | return iterator(cur); 204 | } 205 | return end(); 206 | } 207 | 208 | mapped_type& operator[](const key_type& key) 209 | { 210 | if (_hasMap()) 211 | return _getMap()->operator[](key); 212 | else 213 | { 214 | for (size_t i=0; i<_cnt; ++i) 215 | if (key_equal()(_items[i].first, key)) 216 | return _items[i].second; 217 | } 218 | insert(value_type(key, mapped_type())); 219 | return this->operator[](key); 220 | } 221 | 222 | mapped_type& at(const key_type& key) 223 | { 224 | if (_hasMap()) 225 | return _getMap()->at(key); // throws if not found 226 | 227 | for (size_t i=0; i<_cnt; ++i) 228 | if (key_equal()(_items[i].first, key)) 229 | return _items[i].second; 230 | throw std::out_of_range("at: key not present"); 231 | } 232 | 233 | 234 | std::pair insert(const value_type& val) 235 | { 236 | if (_hasMap()) 237 | { 238 | std::pair res = _getMap()->insert(val); 239 | return std::make_pair(iterator(res.first), res.second); 240 | } 241 | 242 | for (size_t i=0; i<_cnt; ++i) 243 | if (key_equal()(_items[i].first, val.first)) 244 | return std::make_pair(iterator(&_items[i]), false); 245 | 246 | // not present ... insert it 247 | if (_cnt == N) 248 | { 249 | _switchToMap(); 250 | std::pair res = _getMap()->insert(val); 251 | assert(res.second); 252 | return std::make_pair(iterator(res.first), true); 253 | } 254 | *((K *)(&_items[_cnt].first)) = val.first; 255 | _items[_cnt].second = val.second; 256 | return std::make_pair(iterator(&_items[_cnt++]), true); 257 | } 258 | 259 | size_type size() const { return _hasMap() ? _getMap()->size() : _cnt; } 260 | 261 | size_type bucket_count() const { return _hasMap() ? _getMap()->bucket_count() : _cnt; } 262 | 263 | size_type count (const key_type& k) const 264 | { 265 | return find(k) == cend() ? 0 : 1; 266 | } 267 | 268 | bool empty() const { return size() == 0; } 269 | 270 | void clear() 271 | { 272 | if (_hasMap()) 273 | _cleanMap(); 274 | else 275 | { 276 | allocator_type alloc; 277 | for (size_t i=0; i<_cnt; ++i) 278 | alloc.destroy(&_items[i]); 279 | _cnt = 0; 280 | } 281 | } 282 | 283 | private: 284 | bool _hasMap() const { return _cnt > N; } 285 | 286 | Map * _getMap() const { assert(_hasMap()); return (Map *)_cnt; } 287 | 288 | void _cleanMap() { delete _getMap(); _cnt = 0; } 289 | 290 | void _copyLocal(const smo &o) 291 | { 292 | assert(!_hasMap()); 293 | _cnt = o._cnt; 294 | for (size_t i=0; i<_cnt; ++i) 295 | _items[i] = o._items[i]; 296 | } 297 | 298 | void _createMap() 299 | { 300 | assert(!_hasMap()); 301 | _cnt = (uintptr_t)(new Map); 302 | } 303 | 304 | void _switchToMap() 305 | { 306 | assert(!_hasMap()); 307 | 308 | uintptr_t cnt = _cnt; 309 | _cnt = 0; 310 | _createMap(); 311 | 312 | Map *map = _getMap(); 313 | for (size_t i=0; iinsert(_items[i]); 315 | } 316 | 317 | uintptr_t _cnt; 318 | value_type _items[N]; 319 | }; 320 | 321 | } // sho namespace 322 | 323 | 324 | #endif // sho__h__ 325 | -------------------------------------------------------------------------------- /sho.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ size={_cnt} }} 7 | {*(Map *)_cnt} 8 | 9 | (Map *)_cnt 10 | 11 | _cnt 12 | _items 13 | 14 | 15 | 16 | 17 | 18 | {*_p} 19 | {_it} 20 | 21 | *_p 22 | _it 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS = -O2 -std=c++11 -I.. 2 | CXXFLAGS += -Wall -pedantic -Wextra -D_XOPEN_SOURCE=700 3 | DEPS = ../sho.h 4 | TARGETS = test_sho 5 | 6 | 7 | ifeq ($(OS),Windows_NT) 8 | LDFLAGS = -lpsapi 9 | endif 10 | 11 | all: $(TARGETS) 12 | 13 | clean: 14 | rm -rf $(TARGETS) 15 | 16 | test: 17 | ./test_sho 18 | 19 | %: %.cc $(DEPS) makefile 20 | $(CXX) $(CXXFLAGS) -DNDEBUG $< -o $@ $(LDFLAGS) 21 | 22 | -------------------------------------------------------------------------------- /test/test_sho.cc: -------------------------------------------------------------------------------- 1 | // compile on windows with: CL -O2 -I.. -EHsc test_sho.cc -o test_sho 2 | // ---------------------------------------------------------------------- 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | // ----------------------------------------------------------- 17 | // Timer 18 | // ----------------------------------------------------------- 19 | namespace dltest 20 | { 21 | template 22 | class Timer 23 | { 24 | public: 25 | Timer() { reset(); } 26 | void reset() { _start = _snap = clock::now(); } 27 | void snap() { _snap = clock::now(); } 28 | 29 | float get_total() const { return get_diff(_start, clock::now()); } 30 | float get_delta() const { return get_diff(_snap, clock::now()); } 31 | 32 | private: 33 | using clock = std::chrono::high_resolution_clock; 34 | using point = std::chrono::time_point; 35 | 36 | template 37 | static T get_diff(const point& start, const point& end) 38 | { 39 | using duration_t = std::chrono::duration; 40 | 41 | return std::chrono::duration_cast(end - start).count(); 42 | } 43 | 44 | point _start; 45 | point _snap; 46 | }; 47 | } 48 | 49 | // ----------------------------------------------------------- 50 | // Memory usage 51 | // ----------------------------------------------------------- 52 | #if defined(_WIN32) || defined( __CYGWIN__) 53 | #define SHO_TEST_WIN 54 | #endif 55 | 56 | #ifdef SHO_TEST_WIN 57 | #include 58 | #include 59 | #undef min 60 | #undef max 61 | #elif !defined(__APPLE__) 62 | #include 63 | #include 64 | #endif 65 | 66 | namespace dltest 67 | { 68 | uint64_t GetSystemMemory() 69 | { 70 | #ifdef SHO_TEST_WIN 71 | MEMORYSTATUSEX memInfo; 72 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 73 | GlobalMemoryStatusEx(&memInfo); 74 | return static_cast(memInfo.ullTotalPageFile); 75 | #elif !defined(__APPLE__) 76 | struct sysinfo memInfo; 77 | sysinfo (&memInfo); 78 | auto totalVirtualMem = memInfo.totalram; 79 | 80 | totalVirtualMem += memInfo.totalswap; 81 | totalVirtualMem *= memInfo.mem_unit; 82 | return static_cast(totalVirtualMem); 83 | #else 84 | return 0; 85 | #endif 86 | } 87 | 88 | uint64_t GetTotalMemoryUsed() 89 | { 90 | #ifdef SHO_TEST_WIN 91 | MEMORYSTATUSEX memInfo; 92 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 93 | GlobalMemoryStatusEx(&memInfo); 94 | return static_cast(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile); 95 | #elif !defined(__APPLE__) 96 | struct sysinfo memInfo; 97 | sysinfo(&memInfo); 98 | auto virtualMemUsed = memInfo.totalram - memInfo.freeram; 99 | 100 | virtualMemUsed += memInfo.totalswap - memInfo.freeswap; 101 | virtualMemUsed *= memInfo.mem_unit; 102 | 103 | return static_cast(virtualMemUsed); 104 | #else 105 | return 0; 106 | #endif 107 | } 108 | 109 | uint64_t GetProcessMemoryUsed() 110 | { 111 | #ifdef SHO_TEST_WIN 112 | PROCESS_MEMORY_COUNTERS_EX pmc; 113 | GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); 114 | return static_cast(pmc.PrivateUsage); 115 | #elif !defined(__APPLE__) 116 | auto parseLine = 117 | [](char* line)->int 118 | { 119 | auto i = strlen(line); 120 | 121 | while(*line < '0' || *line > '9') 122 | { 123 | line++; 124 | } 125 | 126 | line[i-3] = '\0'; 127 | i = atoi(line); 128 | return i; 129 | }; 130 | 131 | auto file = fopen("/proc/self/status", "r"); 132 | auto result = -1; 133 | char line[128]; 134 | 135 | while(fgets(line, 128, file) != nullptr) 136 | { 137 | if(strncmp(line, "VmSize:", 7) == 0) 138 | { 139 | result = parseLine(line); 140 | break; 141 | } 142 | } 143 | 144 | fclose(file); 145 | return static_cast(result) * 1024; 146 | #else 147 | return 0; 148 | #endif 149 | } 150 | 151 | uint64_t GetPhysicalMemory() 152 | { 153 | #ifdef SHO_TEST_WIN 154 | MEMORYSTATUSEX memInfo; 155 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 156 | GlobalMemoryStatusEx(&memInfo); 157 | return static_cast(memInfo.ullTotalPhys); 158 | #elif !defined(__APPLE__) 159 | struct sysinfo memInfo; 160 | sysinfo(&memInfo); 161 | 162 | auto totalPhysMem = memInfo.totalram; 163 | 164 | totalPhysMem *= memInfo.mem_unit; 165 | return static_cast(totalPhysMem); 166 | #else 167 | return 0; 168 | #endif 169 | } 170 | } 171 | 172 | // ----------------------------------------------------------- 173 | // test code 174 | // ----------------------------------------------------------- 175 | using namespace std; 176 | 177 | static float _to_mb(uint64_t m) { return (float)((double)m / (1024 * 1024)); } 178 | 179 | // ----------------------------------------------------------- 180 | // ----------------------------------------------------------- 181 | template 182 | void run_test(const char *container_name, size_t num_iter) 183 | { 184 | // ----------------------- some checks 185 | { 186 | Hash h; 187 | 188 | typename Hash::iterator it(h.begin()); 189 | typename Hash::const_iterator cit(h.cbegin()); 190 | cit = it; // assign const & non-const iters 191 | //it = cit; 192 | if (cit != h.end() || it != h.cend()) // compare const & non-const iters 193 | abort(); 194 | } 195 | 196 | printf("---------------- testing %s\n", container_name); 197 | 198 | printf("\nmem usage before the test: %4.1f\n", _to_mb(dltest::GetProcessMemoryUsed())); 199 | srand(43); // always same sequence of random numbers 200 | 201 | Hash *hashes = new Hash[num_iter]; 202 | 203 | dltest::Timer timer; 204 | 205 | for (size_t i=0; i 250 | #define BASE_MAP spp::sparse_hash_map 251 | #else 252 | #define BASE_MAP std::unordered_map 253 | #endif 254 | 255 | // ----------------------------------------------------------- 256 | // ----------------------------------------------------------- 257 | int main() 258 | { 259 | typedef BASE_MAP StdMap; 260 | typedef sho::smo<3, BASE_MAP, uint32_t, void *> ShoMap; 261 | 262 | run_test(TOSTRING(BASE_MAP), 5000000); 263 | 264 | char cont_name[128]; 265 | sprintf(cont_name, "%s with sho", TOSTRING(BASE_MAP)); 266 | run_test(cont_name, 5000000); 267 | } 268 | --------------------------------------------------------------------------------