├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bench.md ├── conanfile.py ├── docs └── .gitignore ├── examples ├── emplace.cc ├── hash_std.cc ├── insert_rvalue.cc ├── makefile ├── movable.cc ├── piecewise.cc ├── serialize_file.cc ├── serialize_large.cc ├── serialize_stream.cc ├── test.cc └── vsprojects │ ├── serialize_stream.vcxproj │ ├── serialize_stream.vcxproj.filters │ └── spp_examples.sln ├── sparsepp ├── spp.h ├── spp_config.h ├── spp_dlalloc.h ├── spp_memory.h ├── spp_smartptr.h ├── spp_stdint.h ├── spp_timer.h ├── spp_traits.h └── spp_utils.h ├── spp.natvis ├── test_package ├── CMakeLists.txt ├── conanfile.py └── src │ └── conantest.cpp └── tests ├── makefile ├── perftest1.cc ├── spp_alloc_test.cc ├── spp_bitset_test.cc ├── spp_relative_include_test.cc ├── spp_test.cc └── vsprojects ├── spp.sln ├── spp_alloc_test.vcxproj ├── spp_alloc_test.vcxproj.filters ├── spp_test.vcxproj ├── spp_test.vcxproj.filters └── spp_test.vcxproj.user /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: greg7mdp 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | test_package/build -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | matrix: 3 | include: 4 | 5 | - os: linux 6 | compiler: gcc 7 | env: COMPILER='g++' CXXSTD='c++11' 8 | 9 | - os: linux 10 | compiler: clang 11 | env: COMPILER='clang++' CXXSTD='c++11' 12 | 13 | - os: osx 14 | compiler: gcc 15 | env: COMPILER='g++' CXXSTD='c++11' 16 | 17 | - os: osx 18 | compiler: clang 19 | env: COMPILER='clang++' CXXSTD='c++11' 20 | 21 | - os: linux 22 | compiler: gcc 23 | addons: 24 | apt: 25 | sources: 26 | - ubuntu-toolchain-r-test 27 | packages: 28 | - g++-5 29 | env: COMPILER='g++-5' CXXSTD='c++11' 30 | 31 | - os: linux 32 | compiler: gcc 33 | addons: 34 | apt: 35 | sources: 36 | - ubuntu-toolchain-r-test 37 | packages: 38 | - g++-6 39 | env: COMPILER='g++-6' CXXSTD='c++11' 40 | 41 | - os: linux 42 | compiler: gcc 43 | addons: 44 | apt: 45 | sources: 46 | - ubuntu-toolchain-r-test 47 | packages: 48 | - g++-7 49 | env: COMPILER='g++-7' CXXSTD='c++11' 50 | 51 | - os: linux 52 | compiler: gcc 53 | addons: 54 | apt: 55 | sources: 56 | - ubuntu-toolchain-r-test 57 | packages: 58 | - g++-8 59 | env: COMPILER='g++-8' CXXSTD='c++14' 60 | 61 | - os: linux 62 | compiler: gcc 63 | addons: 64 | apt: 65 | sources: 66 | - ubuntu-toolchain-r-test 67 | packages: 68 | - g++-8 69 | env: COMPILER='g++-8' CXXSTD='c++17' 70 | 71 | - os: linux 72 | compiler: clang 73 | addons: 74 | apt: 75 | sources: 76 | - llvm-toolchain-trusty-3.9 77 | packages: 78 | - clang-3.9 79 | env: COMPILER='clang++-3.9' CXXSTD='c++11' 80 | 81 | - os: linux 82 | compiler: clang 83 | addons: 84 | apt: 85 | sources: 86 | - llvm-toolchain-trusty-4.0 87 | packages: 88 | - clang-4.0 89 | env: COMPILER='clang++-4.0' CXXSTD='c++11' 90 | 91 | - os: linux 92 | compiler: clang 93 | addons: 94 | apt: 95 | sources: 96 | - llvm-toolchain-trusty-5.0 97 | packages: 98 | - clang-5.0 99 | env: COMPILER='clang++-5.0' CXXSTD='c++14' 100 | 101 | - os: linux 102 | compiler: clang 103 | addons: 104 | apt: 105 | sources: 106 | - llvm-toolchain-trusty-5.0 107 | packages: 108 | - clang-5.0 109 | env: COMPILER='clang++-5.0' CXXSTD='c++17' 110 | 111 | dist: trusty 112 | sudo: false 113 | 114 | script: 115 | cd tests && make CXX=$COMPILER && make test 116 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.95 2 | 3 | * not single header anymore (this was just too much of a hassle). 4 | * custom allocator not quite ready yet. Checked in, but still using old allocator (easy to toggle - line 15 of spp_config.h) 5 | 6 | 7 | # 0.90 8 | 9 | * stable release (single header) 10 | * known issues: 11 | - memory usage can be excessive in Windows 12 | 13 | sparsepp has a very simple default allocator based on the system malloc/realloc/free implementation, 14 | and the default Windows realloc() appears to fragment the memory, causing significantly higher 15 | memory usage than on linux. To solve this issue, I am working on a new allocator which will 16 | remedy the problem. 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | // Copyright (c) 2016, Gregory Popovitch - greg7mdp@gmail.com 3 | // All rights reserved. 4 | // 5 | // This work is derived from Google's sparsehash library 6 | // 7 | // Copyright (c) 2005, Google Inc. 8 | // All rights reserved. 9 | // 10 | // Redistribution and use in source and binary forms, with or without 11 | // modification, are permitted provided that the following conditions are 12 | // met: 13 | // 14 | // * Redistributions of source code must retain the above copyright 15 | // notice, this list of conditions and the following disclaimer. 16 | // * Redistributions in binary form must reproduce the above 17 | // copyright notice, this list of conditions and the following disclaimer 18 | // in the documentation and/or other materials provided with the 19 | // distribution. 20 | // * Neither the name of Google Inc. nor the names of its 21 | // contributors may be used to endorse or promote products derived from 22 | // this software without specific prior written permission. 23 | // 24 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | // ---------------------------------------------------------------------- 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | > I now recommend using the [parallel hashmap](https://github.com/greg7mdp/parallel-hashmap) instead of sparsepp, unless if you are stuck with a non C++11 compatible compiler, or if using a little bit more memory is not acceptable. I will personally switch to using the [parallel hashmap](https://github.com/greg7mdp/parallel-hashmap), which I believe offers a significantly better tradeoff (slightly higher memory usage, much faster). 3 | 4 | # Sparsepp: A fast, memory efficient hash map for C++ [![Build Status](https://travis-ci.org/greg7mdp/sparsepp.svg?branch=master)](https://travis-ci.org/greg7mdp/sparsepp) 5 | 6 | 7 | Sparsepp is derived from Google's excellent [sparsehash](https://github.com/sparsehash/sparsehash) implementation. It aims to achieve the following objectives: 8 | 9 | - A drop-in alternative for unordered_map and unordered_set. 10 | - **Extremely low memory usage** (typically about one byte overhead per entry), and most importantly **very small memory overhead when resizing**. 11 | - **Very efficient**, typically faster than your compiler's unordered map/set or Boost's. 12 | - **C++11 support** (if supported by compiler). 13 | - ~~Single header~~ not anymore 14 | - **Tested** on Windows (vs2010-2015, g++), linux (g++, clang++) and MacOS (clang++). 15 | 16 | We believe Sparsepp provides an unparalleled combination of performance and memory usage, and will outperform your compiler's unordered_map on both counts. Only Google's `dense_hash_map` is consistently faster, at the cost of much greater memory usage (especially when the final size of the map is not known in advance, and insertions cause a resizing). 17 | 18 | This hash map. like Google's `dense_hash_map`, uses open addressing (meaning that the hash table entries are conceptually stored in a big array, and collisions are not resolved using a linked list, but by probing). A big drawback of open addressing is that when the array needs to be resized larger, the high mark for memory usage is typically 3 times the current map size (as we allocate a new array twice as big as the current one, and copy from the old array to the new one). Sparsepp's implementation resolves this memory usage issue, and the memory usage high mark shows only a small bump when resizing. 19 | 20 | For a detailed comparison of various hash implementations, including Sparsepp, please see our [write-up](bench.md). 21 | 22 | ## Example 23 | 24 | ```c++ 25 | #include 26 | #include 27 | #include 28 | 29 | using spp::sparse_hash_map; 30 | 31 | int main() 32 | { 33 | // Create an unordered_map of three strings (that map to strings) 34 | sparse_hash_map email = 35 | { 36 | { "tom", "tom@gmail.com"}, 37 | { "jeff", "jk@gmail.com"}, 38 | { "jim", "jimg@microsoft.com"} 39 | }; 40 | 41 | // Iterate and print keys and values 42 | for (const auto& n : email) 43 | std::cout << n.first << "'s email is: " << n.second << "\n"; 44 | 45 | // Add a new entry 46 | email["bill"] = "bg@whatever.com"; 47 | 48 | // and print it 49 | std::cout << "bill's email is: " << email["bill"] << "\n"; 50 | 51 | return 0; 52 | } 53 | ``` 54 | 55 | ## Installation 56 | 57 | No compilation is needed, as this is a header-only library. The installation consist in copying the sparsepp directory wherever it will be convenient to include in your project(s). 58 | 59 | ## Warning - iterator and reference invalidation on erase/insert 60 | 61 | 1. erasing elements is likely to invalidate iterators (for example when calling `erase()`) 62 | 63 | 2. inserting new elements is likely to invalidate iterators (iterator invalidation can also happen with std::unordered_map if rehashing occurs due to the insertion) 64 | 65 | 3. references to values stored in a sparse_hash_map or set are likely to be invalidated on insert()/erase(). This is not the case for std::unordered_map or set. 66 | 67 | ## Usage 68 | 69 | As shown in the example above, you need to include the header file: `#include ` 70 | 71 | This provides the implementation for the following classes: 72 | 73 | ```c++ 74 | namespace spp 75 | { 76 | template , 79 | class EqualKey = std::equal_to, 80 | class Alloc = libc_allocator_with_realloc>> 81 | class sparse_hash_map; 82 | 83 | template , 85 | class EqualKey = std::equal_to, 86 | class Alloc = libc_allocator_with_realloc> 87 | class sparse_hash_set; 88 | }; 89 | ``` 90 | 91 | These classes provide the same interface as std::unordered_map and std::unordered_set, with the following differences: 92 | 93 | - Calls to `erase()` may invalidate iterators. However, conformant to the C++11 standard, the position and range erase functions return an iterator pointing to the position immediately following the last of the elements erased. This makes it easy to traverse a sparse hash table and delete elements matching a condition. For example to delete odd values: 94 | 95 | ```c++ 96 | for (auto it = c.begin(); it != c.end(); ) 97 | if (it->first % 2 == 1) 98 | it = c.erase(it); 99 | else 100 | ++it; 101 | ``` 102 | 103 | As for std::unordered_map, the order of the elements that are not erased is preserved. 104 | 105 | - Since items are not grouped into buckets, Bucket APIs have been adapted: `max_bucket_count` is equivalent to `max_size`, and `bucket_count` returns the sparsetable size, which is normally at least twice the number of items inserted into the hash_map. 106 | 107 | - Values inserted into sparsepp have to either be `copyable and movable`, or just `movable`. See example movable.cc. 108 | 109 | ## Memory allocator on Windows (when building with Visual Studio) 110 | 111 | When building with the Microsoft compiler, we provide a custom allocator because the default one (from the Visual C++ runtime) fragments memory when reallocating. 112 | 113 | This is desirable *only* when creating large sparsepp hash maps. If you create lots of small hash_maps, memory usage may increase instead of decreasing as expected. The reason is that, for each instance of a hash_map, the custom memory allocator creates a new memory space to allocate from, which is typically 4K, so it may be a big waste if just a few items are allocated. 114 | 115 | In order to use the custom spp allocator, define the following preprocessor variable before including ``: 116 | 117 | `#define SPP_USE_SPP_ALLOC 1` 118 | 119 | ## Integer keys, and other hash function considerations. 120 | 121 | 1. For basic integer types, sparsepp provides a default hash function which does some mixing of the bits of the keys (see [Integer Hashing](http://burtleburtle.net/bob/hash/integer.html)). This prevents a pathological case where inserted keys are sequential (1, 2, 3, 4, ...), and the lookup on non-present keys becomes very slow. 122 | 123 | Of course, the user of sparsepp may provide its own hash function, as shown below: 124 | 125 | ```c++ 126 | #include 127 | 128 | struct Hash64 { 129 | size_t operator()(uint64_t k) const { return (k ^ 14695981039346656037ULL) * 1099511628211ULL; } 130 | }; 131 | 132 | struct Hash32 { 133 | size_t operator()(uint32_t k) const { return (k ^ 2166136261U) * 16777619UL; } 134 | }; 135 | 136 | int main() 137 | { 138 | spp::sparse_hash_map map; 139 | ... 140 | } 141 | 142 | ``` 143 | 144 | 2. When the user provides its own hash function, for example when inserting custom classes into a hash map, sometimes the resulting hash keys have similar low order bits and cause many collisions, decreasing the efficiency of the hash map. To address this use case, sparsepp provides an optional 'mixing' of the hash key (see [Integer Hash Function](https://gist.github.com/badboy/6267743) which can be enabled by defining the proprocessor macro: SPP_MIX_HASH. 145 | 146 | ## Example 2 - providing a hash function for a user-defined class 147 | 148 | In order to use a sparse_hash_set or sparse_hash_map, a hash function should be provided. Even though a the hash function can be provided via the HashFcn template parameter, we recommend injecting a specialization of `std::hash` for the class into the "std" namespace. For example: 149 | 150 | ```c++ 151 | #include 152 | #include 153 | #include 154 | #include 155 | 156 | using std::string; 157 | 158 | struct Person 159 | { 160 | bool operator==(const Person &o) const 161 | { return _first == o._first && _last == o._last; } 162 | 163 | string _first; 164 | string _last; 165 | }; 166 | 167 | namespace std 168 | { 169 | // inject specialization of std::hash for Person into namespace std 170 | // ---------------------------------------------------------------- 171 | template<> 172 | struct hash 173 | { 174 | std::size_t operator()(Person const &p) const 175 | { 176 | std::size_t seed = 0; 177 | spp::hash_combine(seed, p._first); 178 | spp::hash_combine(seed, p._last); 179 | return seed; 180 | } 181 | }; 182 | } 183 | 184 | int main() 185 | { 186 | // As we have defined a specialization of std::hash() for Person, 187 | // we can now create sparse_hash_set or sparse_hash_map of Persons 188 | // ---------------------------------------------------------------- 189 | spp::sparse_hash_set persons = { { "John", "Galt" }, 190 | { "Jane", "Doe" } }; 191 | for (auto& p: persons) 192 | std::cout << p._first << ' ' << p._last << '\n'; 193 | } 194 | ``` 195 | 196 | The `std::hash` specialization for `Person` combines the hash values for both first and last name using the convenient spp::hash_combine function, and returns the combined hash value. 197 | 198 | spp::hash_combine is provided by the header `sparsepp/spp.h`. However, class definitions often appear in header files, and it is desirable to limit the size of headers included in such header files, so we provide the very small header `sparsepp/spp_utils.h` for that purpose: 199 | 200 | ```c++ 201 | #include 202 | #include 203 | 204 | using std::string; 205 | 206 | struct Person 207 | { 208 | bool operator==(const Person &o) const 209 | { 210 | return _first == o._first && _last == o._last && _age == o._age; 211 | } 212 | 213 | string _first; 214 | string _last; 215 | int _age; 216 | }; 217 | 218 | namespace std 219 | { 220 | // inject specialization of std::hash for Person into namespace std 221 | // ---------------------------------------------------------------- 222 | template<> 223 | struct hash 224 | { 225 | std::size_t operator()(Person const &p) const 226 | { 227 | std::size_t seed = 0; 228 | spp::hash_combine(seed, p._first); 229 | spp::hash_combine(seed, p._last); 230 | spp::hash_combine(seed, p._age); 231 | return seed; 232 | } 233 | }; 234 | } 235 | ``` 236 | 237 | ## Example 3 - serialization 238 | 239 | sparse_hash_set and sparse_hash_map can easily be serialized/unserialized to a file or network connection. 240 | This support is implemented in the following APIs: 241 | 242 | ```c++ 243 | template 244 | bool serialize(Serializer serializer, OUTPUT *stream); 245 | 246 | template 247 | bool unserialize(Serializer serializer, INPUT *stream); 248 | ``` 249 | 250 | The following example demonstrates how a simple sparse_hash_map can be written to a file, and then read back. The serializer we use read and writes to a file using the stdio APIs, but it would be equally simple to write a serialized using the stream APIS: 251 | 252 | ```c++ 253 | #include 254 | 255 | #include 256 | 257 | using spp::sparse_hash_map; 258 | using namespace std; 259 | 260 | class FileSerializer 261 | { 262 | public: 263 | // serialize basic types to FILE 264 | // ----------------------------- 265 | template 266 | bool operator()(FILE *fp, const T& value) 267 | { 268 | return fwrite((const void *)&value, sizeof(value), 1, fp) == 1; 269 | } 270 | 271 | template 272 | bool operator()(FILE *fp, T* value) 273 | { 274 | return fread((void *)value, sizeof(*value), 1, fp) == 1; 275 | } 276 | 277 | // serialize std::string to FILE 278 | // ----------------------------- 279 | bool operator()(FILE *fp, const string& value) 280 | { 281 | const size_t size = value.size(); 282 | return (*this)(fp, size) && fwrite(value.c_str(), size, 1, fp) == 1; 283 | } 284 | 285 | bool operator()(FILE *fp, string* value) 286 | { 287 | size_t size; 288 | if (!(*this)(fp, &size)) 289 | return false; 290 | char* buf = new char[size]; 291 | if (fread(buf, size, 1, fp) != 1) 292 | { 293 | delete [] buf; 294 | return false; 295 | } 296 | new (value) string(buf, (size_t)size); 297 | delete[] buf; 298 | return true; 299 | } 300 | 301 | // serialize std::pair to FILE - needed for maps 302 | // --------------------------------------------------------- 303 | template 304 | bool operator()(FILE *fp, const std::pair& value) 305 | { 306 | return (*this)(fp, value.first) && (*this)(fp, value.second); 307 | } 308 | 309 | template 310 | bool operator()(FILE *fp, std::pair *value) 311 | { 312 | return (*this)(fp, (A *)&value->first) && (*this)(fp, &value->second); 313 | } 314 | }; 315 | 316 | int main(int argc, char* argv[]) 317 | { 318 | sparse_hash_map age{ { "John", 12 }, {"Jane", 13 }, { "Fred", 8 } }; 319 | 320 | // serialize age hash_map to "ages.dmp" file 321 | FILE *out = fopen("ages.dmp", "wb"); 322 | age.serialize(FileSerializer(), out); 323 | fclose(out); 324 | 325 | sparse_hash_map age_read; 326 | 327 | // read from "ages.dmp" file into age_read hash_map 328 | FILE *input = fopen("ages.dmp", "rb"); 329 | age_read.unserialize(FileSerializer(), input); 330 | fclose(input); 331 | 332 | // print out contents of age_read to verify correct serialization 333 | for (auto& v : age_read) 334 | printf("age_read: %s -> %d\n", v.first.c_str(), v.second); 335 | } 336 | ``` 337 | 338 | ## Thread safety 339 | 340 | Sparsepp follows the thread safety rules of the Standard C++ library. In Particular: 341 | 342 | - A single sparsepp hash table is thread safe for reading from multiple threads. For example, given a hash table A, it is safe to read A from thread 1 and from thread 2 simultaneously. 343 | 344 | - If a single hash table is being written to by one thread, then all reads and writes to that hash table on the same or other threads must be protected. For example, given a hash table A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A. 345 | 346 | - It is safe to read and write to one instance of a type even if another thread is reading or writing to a different instance of the same type. For example, given hash tables A and B of the same type, it is safe if A is being written in thread 1 and B is being read in thread 2. 347 | -------------------------------------------------------------------------------- /bench.md: -------------------------------------------------------------------------------- 1 | # Improving on Google's excellent Sparsehash 2 | 3 | [tl;dr] 4 | 5 | 1. Looking for a great hash map 6 | 2. Google Sparsehash: brilliant idea, sparse version a bit slow and dated 7 | 3. Introducing Sparsepp: fast, memory efficient, C++11, single header 8 | 9 | 10 | ### Hashtables, sparse and dense, maps and btrees - Memory usage 11 | 12 | First, let's compare two separate versions of std::unordered_map, from Boost and g++ (the test was done using Boost version 1.55 and g++ version 4.8.4 on Ubuntu 14.02, running on a VM with 5.8GB of total memory space (under VirtualBox), 5.7GB free before benchmarks. For all tests that follow, hash entries were inserted in an initially empty, default sized container, without calling resize to preallocate the necessary memory. The code for the benchmarks is listed at the end of this article. 13 | 14 | The graph below shows the memory usage when inserting `std::pair` into the unordered_map. 15 | 16 | ![unordered_map memory usage](https://github.com/greg7mdp2/img/blob/master/sparsepp/umap_mem.PNG?raw=true) 17 | 18 | With this test, we see that Boost's implementation uses significantly more memory that the g++ version, and indeed it is unable to insert 100 Million entries into the map without running out of memory. Since the pairs we insert into the map are 16 bytes each, the minimum expected memory usage would be 1.6 GB. We see on the graph that g++ needs just a hair over 4 GB. 19 | 20 | Now, let's add to the comparison Google's offerings: the [Sparsehash](https://github.com/sparsehash/sparsehash) and [cpp-btree](https://code.google.com/archive/p/cpp-btree/) libraries. 21 | 22 | The [Sparsehash](https://github.com/sparsehash/sparsehash) library is a header-only library, open-sourced by Google in 2005. It offers two separate hash map implementations with very different performance characteristics. sparse_hash_map is designed to use as little memory as possible, at the expense of speed if necessary. dense_hash_map is extremely fast, but gobbles memory. 23 | 24 | The [cpp-btree](https://code.google.com/archive/p/cpp-btree/) library was open-sourced by Google in 2013. Although not a hash map, but a map storing ordered elements, I thought it would be interesting to include it because it claims low memory usage, and good performance thanks to cache friendliness. 25 | 26 | 27 | ![Google memory usage](https://github.com/greg7mdp2/img/blob/master/sparsepp/goog_mem.PNG?raw=true) 28 | 29 | So what do we see here? 30 | 31 | Google's dense_hash_map (blue curve) was doing well below 60 Million entries, using an amount of memory somewhat in between the Boost and g++ unordered_map implementations, but ran out of memory when trying to insert 70 Million entries. 32 | 33 | This is easily understood because Google's dense_hash_map stores all the entries in a large contiguous array, and resizes the array by doubling its size when the array is 50% full. 34 | 35 | For 40M to 60M elements, the dense_hash_map used 2GB. Indeed, 60M entries, each 16 byte in size, would occupy 960Mb. So 70M entries would require over 50% of 2GB, causing a resize to 4Gb. And when the resize occurs, a total of 6GB is allocated, as the entries have to be transferred from the 2GB array to the 4GB array. 36 | 37 | So we see that the dense_hash_map has pretty dramatic spikes in memory usage when resizing, equal to six times the space required for the actual entries. For big data cases, and unless the final size of the container can be accurately predicted and the container sized appropriately, the memory demands of dense_hash_map when resizing may remove it from consideration for many applications. 38 | 39 | On the contrary, both Google's sparse_hash_map and btree_map (from cpp-btree) have excellent memory usage characteristics. The sparse_hash_map, as promised, has a very small overhead (it uses about 1.9GB, just 18% higher than the theorical minimum 1.6GB). The btree_map uses a little over 2GB, still excellent. 40 | 41 | Interestingly, the memory usage of both sparse_hash_map and btree_map increases regularly without significant spikes when resizing, which allows them to grow gracefully and use all the memory available. 42 | 43 | The two std::unordered_map implementations do have memory usage spikes when resizing, but less drastic than Google's dense_hash_map. This is because std::unordered_map implementations typically implement the hash map as an array of buckets (all entries with the same hash value go into the same bucket), and buckets store entries into something equivalent to a std::forward_list. So, when resizing, while the bucket array has to be reallocated, the actual map entries can be moved to their new bucket without requiring extra memory. 44 | 45 | 46 | ### What about performance? 47 | 48 | Memory usage is one thing, but we also need efficient containers allowing fast insertion and lookup. We ran series of three benchmarks, still using the same `std::pair` value_type. While randomized, the sequence of keys was the same for each container tested. 49 | 50 | 1. Random Insert: we measured the time needed to insert N entries into an initially empty, default sized container, without calling resize to preallocate the necessary memory. Keys were inserted in random order, i.e. the integer keys were not sorted. 51 | API used: `pair insert(const value_type& val);` 52 | 53 | 54 | ![insert](https://github.com/greg7mdp2/img/blob/master/sparsepp/goog_insert.PNG?raw=true) 55 | 56 | 57 | 2. Random Lookup: we measured the time needed to retrieve N entries known to be present in the array, plus N entries with only a 10% probablility to be present. 58 | API used: `iterator find(const key_type& k);` 59 | 60 | 61 | ![lookup](https://github.com/greg7mdp2/img/blob/master/sparsepp/goog_lookup.PNG?raw=true) 62 | 63 | 64 | 3. Delete: we measured the time needed to delete the N entries known to be present in the array. Entries had been inserted in random order, and are deleted in a different random order. 65 | API used: `size_type erase(const key_type& k);` 66 | 67 | ![delete](https://github.com/greg7mdp2/img/blob/master/sparsepp/goog_delete.PNG?raw=true) 68 | 69 | 70 | What can we conclude from these tests? Here are my observations: 71 | 72 | - Until it runs out of memory at 60M entries, Google's dense_hash_map is consistently the fastest, often by a quite significant margin. 73 | 74 | - Both Boost and g++ unordered_maps have very similar performance, which I would qualify as average amoung the alternatives tested. 75 | 76 | - Google's btree_map (from cpp-btree) does not perform very well on these tests (where the fact that it maintains the ordering of entries is not used). While it is competitive with the sparse_hash_map for insertion, the lookup time is typically at least 3 time higher than the slowest hash map, and deletion is slower as well. This is not unexpected as the btree complexity on insert, lookup and delete is O(log n). 77 | 78 | - Google's sparse_hash_map is very competitive, as fast as the std::unordered_maps on lookup, faster at deletion, but slower on insert. Considering its excellent memory usage characteristics, I would say it is the best compromise. 79 | 80 | So, if we are looking for a non-ordered associative container on linux[^1], I think the two Google offerings are most attractive: 81 | 82 | - Google's dense_hash_map: **extremely fast, very high memory requirement** (unless the maximum numbers of entries is known in advance, and is not just a little bit greater than a power of two). 83 | 84 | - Google's sparse_hash_map: **very memory efficient, fast lookup and deletion**, however slower that std::unordered_map on insertion. 85 | 86 | 87 | ### Introducing [Sparsepp](https://github.com/greg7mdp/sparsepp) 88 | 89 | At this point, I started wondering about the large speed difference between the sparse and dense hash_maps by Google. Could that performance gap be reduced somewhat? After all, both use [open adressing](https://en.wikipedia.org/wiki/Open_addressing) with internal [quadratic probing](https://en.wikipedia.org/wiki/Quadratic_probing). 90 | 91 | I was also intrigued by the remarkable sparse_hash_map memory efficiency, and wanted to fully understand its implementation, which is based on a [sparsetable](http://htmlpreview.github.io/?https://github.com/sparsehash/sparsehash/blob/master/doc/implementation.html). 92 | 93 | As I read the code and followed it under the debugger, I started having ideas on how to speed-up the sparse_hash_map, without significantly increasing the memory usage. That little game was addictive, and I found myself trying various ideas: some which provided significant benefits, and some that didn't pan out. 94 | 95 | Regardless, after a few months of work on evenings and week-ends, I am proud to present [Sparsepp](https://github.com/greg7mdp/sparsepp), a heavily modified version of Google's sparse_hash_map which offers significant performance improvements, while maintaining a a very low memory profile. 96 | 97 | The graphs below show the relative performance (purple line) of the [Sparsepp](https://github.com/greg7mdp/sparsepp) sparse_hash_map compared to the other implementations: 98 | 99 | `Note: "Sparse++" in the graphs legend is actually "Sparsepp".` 100 | 101 | 1. Random Insert: [Sparsepp](https://github.com/greg7mdp/sparsepp), while still slower than the dense_hash_map, is significantly faster than the original sparse_hash_map and the btree_map, and as fast as the two std::unordered_map implementations. 102 | 103 | ![insert](https://github.com/greg7mdp2/img/blob/master/sparsepp/spp_insert.PNG?raw=true) 104 | 105 | 2. Random Lookup (find): [Sparsepp](https://github.com/greg7mdp/sparsepp) is faster than all other alternatives, except for dense_hash_map. 106 | 107 | ![lookup](https://github.com/greg7mdp2/img/blob/master/sparsepp/spp_lookup.PNG?raw=true) 108 | 109 | 3. Delete (erase): [Sparsepp](https://github.com/greg7mdp/sparsepp) is again doing very well, outperformed only by dense_hash_map. We should note that unlike the original sparse_hash_map, [Sparsepp](https://github.com/greg7mdp/sparsepp)'s sparse_hash_map does release the memory on erase, instead of just overwriting the memory with the deleted key value. Indeed, the non-standard APIs set_deleted_key() and set_empty_key(), while still present for compatibility with the original sparse_hash_map, are no longer necessary or useful. 110 | 111 | ![delete](https://github.com/greg7mdp2/img/blob/master/sparsepp/spp_delete.PNG?raw=true) 112 | 113 | 114 | Looks good, but what is the cost of using [Sparsepp](https://github.com/greg7mdp/sparsepp) versus the original sparse_hash_map in memory usage: 115 | 116 | ![delete](https://github.com/greg7mdp2/img/blob/master/sparsepp/spp_mem.PNG?raw=true) 117 | 118 | Not bad! While [Sparsepp](https://github.com/greg7mdp/sparsepp) memory usage is a little bit higher than the original sparse_hash_map, it is still memory friendly, and there are no memory spikes when the map resizes. We can see that when moving from 60M entries to 70M entries, both Google's dense and [Sparsepp](https://github.com/greg7mdp/sparsepp) hash_maps needed a resize to accomodate the 70M elements. The resize proved fatal for the dense_hash_map, who could not allocate the 6GB needed for the resize + copy, while the [Sparsepp](https://github.com/greg7mdp/sparsepp) sparse_hash_map had no problem. 119 | 120 | In order to validate that the sparse hash tables can indeed grow to accomodate many more entries than regular hash tables, we decided to run a test that would gounsert items until all tables run out of memory, the result of which is presented in the two graphs below: 121 | 122 | ![SPP_ALLOC_SZ_0](https://github.com/greg7mdp2/img/blob/master/sparsepp/insert_large_0.PNG?raw=true) 123 | 124 | The table below display the maximum number of entries that could be added to each map before it ran out of memory. As a reminder, the VM had 5.7GB free before each test, and each entry is 16 bytes. 125 | 126 | Max entries | Google's dense_hash | Boost unordered | g++ unordered | Google's btree_map | Sparsepp | Google's sparse_hash 127 | ------------ | -------------- | --------------- | -------------- | --------- | -------- | --------------- 128 | in millions | 60 M | 80 M | 120 M | 220 M | 220 M | 240 M 129 | 130 | 131 | Both sparse hash implementations, as well as Google's btree_map are significantly more memory efficient than the classic unordered_maps. They are also significantly slower that [Sparsepp](https://github.com/greg7mdp/sparsepp). 132 | 133 | If we are willing to sacrifice a little bit of insertion performance for improved memory efficiency, it is easily done with a simple change in [Sparsepp](https://github.com/greg7mdp/sparsepp) header file `sparsepp.h`. Just change: 134 | 135 | `#define SPP_ALLOC_SZ 0` 136 | 137 | to 138 | 139 | `#define SPP_ALLOC_SZ 1` 140 | 141 | With this change, we get the graphs below: 142 | 143 | ![SPP_ALLOC_SZ_1](https://github.com/greg7mdp2/img/blob/master/sparsepp/insert_large_1.PNG?raw=true) 144 | 145 | Now the memory usage of [Sparsepp](https://github.com/greg7mdp/sparsepp) is reduced to just a little bit more than Google's sparse_hash_map, and both sparse map implementations are able to insert 240 Million entries, but choke at 260 Million. [Sparsepp](https://github.com/greg7mdp/sparsepp) is a little bit slower on insert, but still significantly faster than Google's sparse_hash_map. Lookup performance (not graphed) is unchanged. 146 | 147 | To conclude, we feel that [Sparsepp](https://github.com/greg7mdp/sparsepp) provides an unusual combination of performance and memory economy, and will be a useful addition to every developer toolkit. 148 | 149 | Here are some other features of [Sparsepp](https://github.com/greg7mdp/sparsepp) you may find attractive: 150 | 151 | * Single header implementation: the full [Sparsepp](https://github.com/greg7mdp/sparsepp) resides in the single header file sparsepp.h. Just copy this file in your project, #include it, and you are good to go. 152 | 153 | * C++11 compatible: move semantics, cbegin()/cend(), stateful allocators 154 | 155 | 156 | ### Benchmarks code 157 | 158 | ```c++ 159 | template 160 | void _fill(vector &v) 161 | { 162 | srand(1); // for a fair/deterministic comparison 163 | for (size_t i = 0, sz = v.size(); i < sz; ++i) 164 | v[i] = (T)(i * 10 + rand() % 10); 165 | } 166 | 167 | template 168 | void _shuffle(vector &v) 169 | { 170 | for (size_t n = v.size(); n >= 2; --n) 171 | std::swap(v[n - 1], v[static_cast(rand()) % n]); 172 | } 173 | 174 | template 175 | double _fill_random(vector &v, HT &hash) 176 | { 177 | _fill(v); 178 | _shuffle(v); 179 | 180 | double start_time = get_time(); 181 | 182 | for (size_t i = 0, sz = v.size(); i < sz; ++i) 183 | hash.insert(typename HT::value_type(v[i], 0)); 184 | return start_time; 185 | } 186 | 187 | template 188 | double _lookup(vector &v, HT &hash, size_t &num_present) 189 | { 190 | _fill_random(v, hash); 191 | 192 | num_present = 0; 193 | size_t max_val = v.size() * 10; 194 | double start_time = get_time(); 195 | 196 | for (size_t i = 0, sz = v.size(); i < sz; ++i) 197 | { 198 | num_present += (size_t)(hash.find(v[i]) != hash.end()); 199 | num_present += (size_t)(hash.find((T)(rand() % max_val)) != hash.end()); 200 | } 201 | return start_time; 202 | } 203 | 204 | template 205 | double _delete(vector &v, HT &hash) 206 | { 207 | _fill_random(v, hash); 208 | _shuffle(v); // don't delete in insertion order 209 | 210 | double start_time = get_time(); 211 | 212 | for(size_t i = 0, sz = v.size(); i < sz; ++i) 213 | hash.erase(v[i]); 214 | return start_time; 215 | } 216 | ``` 217 | 218 | [^1]: Google's hash maps were most likely developed on linux/g++. When built on Windows with Visual Studio (2015), the lookup of items is very slow, to the point that the sparse hashtable is not much faster than Google's btree_map (from the cpp-btree library). The reason for this poor performance is that Google's implementation uses by default the hash functions provided by the compiler, and those provided by the Visual C++ compiler turn out to be very inefficient. If you are using Google's sparse_hash_map on Windows, you can look forward to an even greater performance increase when switching to [Sparsepp](https://github.com/greg7mdp/sparsepp). 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from conan import ConanFile, tools 5 | from conan.tools.files import get,copy 6 | import os 7 | 8 | class SparseppConan(ConanFile): 9 | name = "sparsepp" 10 | version = "1.22" 11 | description = "A fast, memory efficient hash map for C++" 12 | license = "https://github.com/greg7mdp/sparsepp/blob/master/LICENSE" 13 | 14 | exports_sources = "sparsepp/*" 15 | no_copy_source = True 16 | exports = ["LICENSE"] 17 | package_type = "header-library" 18 | 19 | def package(self): 20 | copy(self, "*", 21 | self.source_folder, 22 | os.path.join(self.package_folder, "include")) 23 | 24 | def package_id(self): 25 | self.info.clear() 26 | 27 | def package_info(self): 28 | self.cpp_info.includedirs = [os.path.join(self.package_folder, "include")] -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /examples/emplace.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace patch 12 | { 13 | template std::string to_string(const T& n) 14 | { 15 | std::ostringstream stm; 16 | stm << n; 17 | return stm.str(); 18 | } 19 | } 20 | 21 | #if defined(SPP_NO_CXX11_RVALUE_REFERENCES) 22 | #warning "problem: we expect spp will detect we have rvalue support" 23 | #endif 24 | 25 | template 26 | using milliseconds = std::chrono::duration; 27 | 28 | class custom_type 29 | { 30 | std::string one = "one"; 31 | std::string two = "two"; 32 | std::uint32_t three = 3; 33 | std::uint64_t four = 4; 34 | std::uint64_t five = 5; 35 | public: 36 | custom_type() = default; 37 | // Make object movable and non-copyable 38 | custom_type(custom_type &&) = default; 39 | custom_type& operator=(custom_type &&) = default; 40 | // should be automatically deleted per http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 41 | //custom_type(custom_type const&) = delete; 42 | //custom_type& operator=(custom_type const&) = delete; 43 | }; 44 | 45 | void test(std::size_t iterations, std::size_t container_size) 46 | { 47 | std::clog << "bench: iterations: " << iterations << " / container_size: " << container_size << "\n"; 48 | { 49 | std::size_t count = 0; 50 | auto t1 = std::chrono::high_resolution_clock::now(); 51 | for (std::size_t i=0; i m; 54 | m.reserve(container_size); 55 | for (std::size_t j=0; j(t2 - t1).count(); 61 | if (count != iterations*container_size) 62 | std::clog << " invalid count: " << count << "\n"; 63 | std::clog << " std::unordered_map: " << std::fixed << int(elapsed) << " ms\n"; 64 | } 65 | 66 | { 67 | std::size_t count = 0; 68 | auto t1 = std::chrono::high_resolution_clock::now(); 69 | for (std::size_t i=0; i m; 72 | for (std::size_t j=0; j(t2 - t1).count(); 78 | if (count != iterations*container_size) 79 | std::clog << " invalid count: " << count << "\n"; 80 | std::clog << " std::map: " << std::fixed << int(elapsed) << " ms\n"; 81 | } 82 | 83 | { 84 | std::size_t count = 0; 85 | auto t1 = std::chrono::high_resolution_clock::now(); 86 | for (std::size_t i=0; i> m; 89 | m.reserve(container_size); 90 | for (std::size_t j=0; j(t2 - t1).count(); 96 | if (count != iterations*container_size) 97 | std::clog << " invalid count: " << count << "\n"; 98 | std::clog << " std::vector: " << std::fixed << int(elapsed) << " ms\n"; 99 | } 100 | 101 | { 102 | std::size_t count = 0; 103 | auto t1 = std::chrono::high_resolution_clock::now(); 104 | for (std::size_t i=0; i m; 107 | m.reserve(container_size); 108 | for (std::size_t j=0; j(t2 - t1).count(); 114 | if (count != iterations*container_size) 115 | std::clog << " invalid count: " << count << "\n"; 116 | std::clog << " spp::sparse_hash_map: " << std::fixed << int(elapsed) << " ms\n"; 117 | } 118 | 119 | } 120 | 121 | int main() 122 | { 123 | std::size_t iterations = 100000; 124 | 125 | test(iterations,1); 126 | test(iterations,10); 127 | test(iterations,50); 128 | } 129 | -------------------------------------------------------------------------------- /examples/hash_std.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using std::string; 6 | 7 | struct Person 8 | { 9 | bool operator==(const Person &o) const 10 | { 11 | return _first == o._first && _last == o._last; 12 | } 13 | 14 | string _first; 15 | string _last; 16 | }; 17 | 18 | namespace std 19 | { 20 | // inject specialization of std::hash for Person into namespace std 21 | // ---------------------------------------------------------------- 22 | template<> 23 | struct hash 24 | { 25 | std::size_t operator()(Person const &p) const 26 | { 27 | std::size_t seed = 0; 28 | spp::hash_combine(seed, p._first); 29 | spp::hash_combine(seed, p._last); 30 | return seed; 31 | } 32 | }; 33 | } 34 | 35 | int main() 36 | { 37 | // As we have defined a specialization of std::hash() for Person, 38 | // we can now create sparse_hash_set or sparse_hash_map of Persons 39 | // ---------------------------------------------------------------- 40 | spp::sparse_hash_set persons = 41 | { { "John", "Galt" }, 42 | { "Jane", "Doe" } 43 | }; 44 | 45 | for (auto& p: persons) 46 | std::cout << p._first << ' ' << p._last << '\n'; 47 | } 48 | -------------------------------------------------------------------------------- /examples/insert_rvalue.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sparsepp/spp.h" 3 | #include 4 | 5 | using namespace std; 6 | 7 | template 8 | std::unique_ptr make_unique(Args&&... args) 9 | { 10 | return std::unique_ptr(new T(std::forward(args)...)); 11 | } 12 | 13 | int main() 14 | { 15 | using Key = std::unique_ptr; 16 | using Value = std::unique_ptr; 17 | 18 | #if 1 19 | using AssociativeContainer = spp::sparse_hash_map; 20 | #else 21 | using AssociativeContainer = unordered_map; 22 | #endif 23 | 24 | AssociativeContainer c; 25 | char buf[10], buf2[10]; 26 | 27 | for (int i=0; i<10; ++i) 28 | { 29 | sprintf(buf, "%d", i); 30 | sprintf(buf2, "val%d", i); 31 | auto& value = c[make_unique(buf)]; 32 | value = make_unique(buf2); 33 | } 34 | 35 | for (int i=10; i<20; ++i) 36 | { 37 | sprintf(buf, "%d", i); 38 | sprintf(buf2, "val%d", i); 39 | auto&& p = std::make_pair(make_unique(buf), make_unique(buf2)); 40 | c.insert(std::move(p)); 41 | } 42 | 43 | for (int i=20; i<30; ++i) 44 | { 45 | sprintf(buf, "%d", i); 46 | sprintf(buf2, "val%d", i); 47 | c.emplace(std::make_pair(make_unique(buf), make_unique(buf2))); 48 | } 49 | 50 | for (auto& p : c) 51 | std::cout << *p.first << " -> " << *p.second << '\n'; 52 | } 53 | -------------------------------------------------------------------------------- /examples/makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS = -O2 -std=c++11 -I.. 2 | CXXFLAGS += -Wall -pedantic -Wextra 3 | SPP_DEPS_1 = spp.h spp_utils.h spp_dlalloc.h spp_traits.h spp_config.h 4 | SPP_DEPS = $(addprefix ../sparsepp/,$(SPP_DEPS_1)) 5 | TARGETS = insert_rvalue movable emplace piecewise hash_std serialize_file serialize_stream serialize_large 6 | 7 | ifeq ($(OS),Windows_NT) 8 | LDFLAGS = -lpsapi 9 | else 10 | OS = $(shell uname -s) 11 | ifeq ($(OS),Linux) 12 | CXXFLAGS += -D_XOPEN_SOURCE=700 13 | endif 14 | ifeq ($(OS),FreeBSD) 15 | LDFLAGS = -lkvm 16 | endif 17 | endif 18 | 19 | all: $(TARGETS) 20 | 21 | clean: 22 | rm -f $(TARGETS) ages.dmp data.dat vsprojects/x64/* vsprojects/x86/* 23 | 24 | %: %.cc $(SPP_DEPS) makefile 25 | $(CXX) $(CXXFLAGS) -DNDEBUG $< -o $@ $(LDFLAGS) 26 | 27 | -------------------------------------------------------------------------------- /examples/movable.cc: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // values inserted into sparsepp have to be either copyable or movable. 3 | // this example demonstrates inserting a movable only object. 4 | // --------------------------------------------------------------------------- 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class A 11 | { 12 | public: 13 | A(const std::pair& b) 14 | { 15 | key = b.first; 16 | value = b.second; 17 | } 18 | 19 | // no copy allowed 20 | A(const A&) = delete; 21 | A& operator=(const A&) = delete; 22 | 23 | // but moving is OK 24 | A(A&&) = default; 25 | A& operator=(A&&) = default; 26 | 27 | private: 28 | std::string key; 29 | std::string value; 30 | }; 31 | 32 | int main() 33 | { 34 | spp::sparse_hash_map m; 35 | 36 | m.emplace(std::piecewise_construct, 37 | std::forward_as_tuple("c"), 38 | std::forward_as_tuple(std::make_pair("a", "bcd"))); 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /examples/piecewise.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int main() 9 | { 10 | using StringPtr = std::unique_ptr; 11 | 12 | #if 1 13 | using StringPtrContainer = spp::sparse_hash_map; 14 | #else 15 | using StringPtrContainer = std::unordered_map; 16 | #endif 17 | 18 | StringPtrContainer c; 19 | 20 | c.emplace(std::piecewise_construct, 21 | std::forward_as_tuple(new std::string{ "key" }), 22 | std::forward_as_tuple(new std::string{ "value" })); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/serialize_file.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using spp::sparse_hash_map; 6 | using namespace std; 7 | 8 | class FileSerializer 9 | { 10 | public: 11 | // serialize basic types to FILE 12 | // ----------------------------- 13 | template 14 | bool operator()(FILE *fp, const T& value) 15 | { 16 | return fwrite((const void *)&value, sizeof(value), 1, fp) == 1; 17 | } 18 | 19 | template 20 | bool operator()(FILE *fp, T* value) 21 | { 22 | return fread((void *)value, sizeof(*value), 1, fp) == 1; 23 | } 24 | 25 | // serialize std::string to FILE 26 | // ----------------------------- 27 | bool operator()(FILE *fp, const string& value) 28 | { 29 | const size_t size = value.size(); 30 | return (*this)(fp, size) && fwrite(value.c_str(), size, 1, fp) == 1; 31 | } 32 | 33 | bool operator()(FILE *fp, string* value) 34 | { 35 | size_t size; 36 | if (!(*this)(fp, &size)) 37 | return false; 38 | char* buf = new char[size]; 39 | if (fread(buf, size, 1, fp) != 1) 40 | { 41 | delete [] buf; 42 | return false; 43 | } 44 | new (value) string(buf, (size_t)size); 45 | delete[] buf; 46 | return true; 47 | } 48 | 49 | // serialize std::vector to FILE 50 | // ----------------------------------- 51 | template > 52 | bool operator()(FILE *fp, const std::vector& value) 53 | { 54 | const size_t size = value.size(); 55 | if (!(*this)(fp, size)) 56 | return false; 57 | for (size_t i=0; i> 64 | bool operator()(FILE *fp, std::vector* value) 65 | { 66 | size_t size; 67 | if (!(*this)(fp, &size)) 68 | return false; 69 | new (value) std::vector(size); 70 | for (size_t i=0; i to FILE 77 | // ----------------------------------------------------- 78 | template , 80 | class E = std::equal_to, 81 | class A = SPP_DEFAULT_ALLOCATOR > > 82 | bool operator()(FILE *fp, const spp::sparse_hash_map& value) 83 | { 84 | return const_cast &>(value).serialize(*this, fp); 85 | } 86 | 87 | template , 89 | class E = std::equal_to, 90 | class A = SPP_DEFAULT_ALLOCATOR > > 91 | bool operator()(FILE *fp, spp::sparse_hash_map *value) 92 | { 93 | new (value) spp::sparse_hash_map(); 94 | return value->unserialize(*this, fp); 95 | } 96 | 97 | // serialize std::pair to FILE - needed for maps 98 | // --------------------------------------------------------- 99 | template 100 | bool operator()(FILE *fp, const std::pair& value) 101 | { 102 | return (*this)(fp, value.first) && (*this)(fp, value.second); 103 | } 104 | 105 | template 106 | bool operator()(FILE *fp, std::pair *value) 107 | { 108 | return (*this)(fp, (A *)&value->first) && (*this)(fp, &value->second); 109 | } 110 | }; 111 | 112 | int main(int, char* []) 113 | { 114 | sparse_hash_map age{ { "John", 12 }, {"Jane", 13 }, { "Fred", 8 } }; 115 | 116 | // serialize age hash_map to "ages.dmp" file 117 | FILE *out = fopen("ages.dmp", "wb"); 118 | age.serialize(FileSerializer(), out); 119 | fclose(out); 120 | 121 | sparse_hash_map age_read; 122 | 123 | // read from "ages.dmp" file into age_read hash_map 124 | FILE *input = fopen("ages.dmp", "rb"); 125 | age_read.unserialize(FileSerializer(), input); 126 | fclose(input); 127 | 128 | // print out contents of age_read to verify correct serialization 129 | for (auto& v : age_read) 130 | printf("age_read: %s -> %d\n", v.first.c_str(), v.second); 131 | } 132 | -------------------------------------------------------------------------------- /examples/serialize_large.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using spp::sparse_hash_map; 10 | using namespace std; 11 | 12 | class FileSerializer 13 | { 14 | public: 15 | // serialize basic types to FILE 16 | // ----------------------------- 17 | template 18 | bool operator()(FILE *fp, const T& value) 19 | { 20 | return fwrite((const void *)&value, sizeof(value), 1, fp) == 1; 21 | } 22 | 23 | template 24 | bool operator()(FILE *fp, T* value) 25 | { 26 | return fread((void *)value, sizeof(*value), 1, fp) == 1; 27 | } 28 | 29 | // serialize std::string to FILE 30 | // ----------------------------- 31 | bool operator()(FILE *fp, const string& value) 32 | { 33 | const size_t size = value.size(); 34 | return (*this)(fp, size) && fwrite(value.c_str(), size, 1, fp) == 1; 35 | } 36 | 37 | bool operator()(FILE *fp, string* value) 38 | { 39 | size_t size; 40 | if (!(*this)(fp, &size)) 41 | return false; 42 | char* buf = new char[size]; 43 | if (fread(buf, size, 1, fp) != 1) 44 | { 45 | delete [] buf; 46 | return false; 47 | } 48 | new (value) string(buf, (size_t)size); 49 | delete[] buf; 50 | return true; 51 | } 52 | 53 | // serialize std::pair to FILE - needed for maps 54 | // --------------------------------------------------------- 55 | template 56 | bool operator()(FILE *fp, const std::pair& value) 57 | { 58 | return (*this)(fp, value.first) && (*this)(fp, value.second); 59 | } 60 | 61 | template 62 | bool operator()(FILE *fp, std::pair *value) 63 | { 64 | return (*this)(fp, (A *)&value->first) && (*this)(fp, &value->second); 65 | } 66 | }; 67 | 68 | float _to_gb(uint64_t m) { return (float)((double)m / (1024 * 1024 * 1024)); } 69 | 70 | int main(int, char* []) 71 | { 72 | sparse_hash_map age; 73 | 74 | for (size_t i=0; i<10000000; ++i) 75 | { 76 | char buff[20]; 77 | sprintf(buff, "%zu", i); 78 | age.insert(std::make_pair(std::string(buff), i)); 79 | } 80 | 81 | printf("before serialize(): mem_usage %4.1f GB\n", _to_gb(spp::GetProcessMemoryUsed())); 82 | // serialize age hash_map to "ages.dmp" file 83 | FILE *out = fopen("ages.dmp", "wb"); 84 | age.serialize(FileSerializer(), out); 85 | fclose(out); 86 | 87 | printf("before clear(): mem_usage %4.1f GB\n", _to_gb(spp::GetProcessMemoryUsed())); 88 | age.clear(); 89 | printf("after clear(): mem_usage %4.1f GB\n", _to_gb(spp::GetProcessMemoryUsed())); 90 | 91 | 92 | // read from "ages.dmp" file into age_read hash_map 93 | FILE *input = fopen("ages.dmp", "rb"); 94 | age.unserialize(FileSerializer(), input); 95 | fclose(input); 96 | printf("after unserialize(): mem_usage %4.1f GB\n", _to_gb(spp::GetProcessMemoryUsed())); 97 | } 98 | -------------------------------------------------------------------------------- /examples/serialize_stream.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | using spp::sparse_hash_map; 7 | 8 | using namespace std; 9 | 10 | struct StringToIntSerializer 11 | { 12 | bool operator()(std::ofstream* stream, const std::pair& value) const 13 | { 14 | size_t sizeSecond = sizeof(value.second); 15 | size_t sizeFirst = value.first.size(); 16 | stream->write((char*)&sizeFirst, sizeof(sizeFirst)); 17 | stream->write(value.first.c_str(), sizeFirst); 18 | stream->write((char*)&value.second, sizeSecond); 19 | return true; 20 | } 21 | 22 | bool operator()(std::ifstream* istream, std::pair* value) const 23 | { 24 | // Read key 25 | size_t size = 0; 26 | istream->read((char*)&size, sizeof(size)); 27 | char * first = new char[size]; 28 | istream->read(first, size); 29 | new (const_cast(&value->first)) string(first, size); 30 | 31 | // Read value 32 | istream->read((char *)&value->second, sizeof(value->second)); 33 | return true; 34 | } 35 | }; 36 | 37 | int main(int , char* []) 38 | { 39 | sparse_hash_map users; 40 | 41 | users["John"] = 12345; 42 | users["Bob"] = 553; 43 | users["Alice"] = 82200; 44 | 45 | // Write users to file "data.dat" 46 | // ------------------------------ 47 | std::ofstream* stream = new std::ofstream("data.dat", 48 | std::ios::out | std::ios::trunc | std::ios::binary); 49 | users.serialize(StringToIntSerializer(), stream); 50 | stream->close(); 51 | delete stream; 52 | 53 | // Read from file "data.dat" into users2 54 | // ------------------------------------- 55 | sparse_hash_map users2; 56 | std::ifstream* istream = new std::ifstream("data.dat"); 57 | users2.unserialize(StringToIntSerializer(), istream); 58 | istream->close(); 59 | delete istream; 60 | 61 | for (sparse_hash_map::iterator it = users2.begin(); it != users2.end(); ++it) 62 | printf("users2: %s -> %d\n", it->first.c_str(), it->second); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /examples/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | auto bar() { 7 | return std::make_tuple(3, 4.5, "Some string"); 8 | } 9 | 10 | int main() 11 | { 12 | auto[c, d, e] = bar(); 13 | std::cout << "c: " << c << ", d: " << d << ", e: " << e << "\n"; 14 | 15 | spp::sparse_hash_set test1; 16 | spp::sparse_hash_set test2 = std::move(test1); 17 | test1.clear(); 18 | test1.erase(0); 19 | } 20 | -------------------------------------------------------------------------------- /examples/vsprojects/serialize_stream.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {19BC4240-15ED-4C76-BC57-34BB70FE163B} 29 | Win32Proj 30 | 8.1 31 | serialize_stream 32 | 33 | 34 | 35 | Application 36 | v140 37 | MultiByte 38 | 39 | 40 | Application 41 | MultiByte 42 | v140 43 | 44 | 45 | Application 46 | v140 47 | MultiByte 48 | 49 | 50 | Application 51 | v140 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | <_ProjectFileVersion>14.0.23107.0 72 | 73 | 74 | None 75 | 76 | 77 | $(SolutionDir)$(Configuration)\ 78 | $(Configuration)\ 79 | true 80 | 81 | 82 | true 83 | 84 | 85 | $(SolutionDir)$(Configuration)\ 86 | $(Configuration)\ 87 | false 88 | 89 | 90 | false 91 | 92 | 93 | 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 96 | true 97 | EnableFastChecks 98 | MultiThreadedDebug 99 | 100 | Level3 101 | ProgramDatabase 102 | ../.. 103 | 104 | 105 | $(OutDir)spp_alloc_test.exe 106 | true 107 | $(OutDir)spp_alloc_test.pdb 108 | Console 109 | MachineX86 110 | 111 | 112 | 113 | 114 | Disabled 115 | WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 116 | EnableFastChecks 117 | MultiThreadedDebug 118 | 119 | 120 | Level3 121 | ProgramDatabase 122 | ../.. 123 | 124 | 125 | $(OutDir)spp_alloc_test.exe 126 | true 127 | $(OutDir)spp_alloc_test.pdb 128 | Console 129 | 130 | 131 | 132 | 133 | WIN32;NDEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 134 | MultiThreaded 135 | 136 | Level3 137 | ProgramDatabase 138 | ../.. 139 | 140 | 141 | $(OutDir)spp_alloc_test.exe 142 | true 143 | Console 144 | true 145 | true 146 | MachineX86 147 | true 148 | 149 | 150 | 151 | 152 | WIN32;NDEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 153 | MultiThreaded 154 | 155 | 156 | Level3 157 | ProgramDatabase 158 | ../.. 159 | 160 | 161 | $(OutDir)spp_alloc_test.exe 162 | true 163 | Console 164 | true 165 | true 166 | true 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /examples/vsprojects/serialize_stream.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {ba5fa1b8-1783-4b3b-9a41-31d363b52841} 6 | 7 | 8 | 9 | 10 | Header Files 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/vsprojects/spp_examples.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "serialize_stream", "serialize_stream.vcxproj", "{19BC4240-15ED-4C76-BC57-34BB70FE163B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x64.ActiveCfg = Debug|x64 17 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x64.Build.0 = Debug|x64 18 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x86.ActiveCfg = Debug|Win32 19 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x86.Build.0 = Debug|Win32 20 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x64.ActiveCfg = Release|x64 21 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x64.Build.0 = Release|x64 22 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x86.ActiveCfg = Release|Win32 23 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /sparsepp/spp_config.h: -------------------------------------------------------------------------------- 1 | #if !defined(spp_config_h_guard) 2 | #define spp_config_h_guard 3 | 4 | // -------------------------------------------------- 5 | // Sparsepp config macros 6 | // some can be overriden on the command line 7 | // -------------------------------------------------- 8 | #ifndef SPP_NAMESPACE 9 | #define SPP_NAMESPACE spp 10 | #endif 11 | 12 | #ifndef spp_ 13 | #define spp_ SPP_NAMESPACE 14 | #endif 15 | 16 | #ifndef SPP_DEFAULT_ALLOCATOR 17 | #if (defined(SPP_USE_SPP_ALLOC) && SPP_USE_SPP_ALLOC) && defined(_MSC_VER) 18 | // ----------------------------------------------------------------------------- 19 | // When building with the Microsoft compiler, we use a custom allocator because 20 | // the default one fragments memory when reallocating. This is desirable only 21 | // when creating large sparsepp hash maps. If you create lots of small hash_maps, 22 | // define the following before including spp.h: 23 | // #define SPP_DEFAULT_ALLOCATOR spp::libc_allocator 24 | // ----------------------------------------------------------------------------- 25 | #define SPP_DEFAULT_ALLOCATOR spp_::spp_allocator 26 | #define SPP_INCLUDE_SPP_ALLOC 27 | #else 28 | #define SPP_DEFAULT_ALLOCATOR spp_::libc_allocator 29 | #endif 30 | #endif 31 | 32 | #ifndef SPP_GROUP_SIZE 33 | // must be 32 or 64 34 | #define SPP_GROUP_SIZE 32 35 | #endif 36 | 37 | #ifndef SPP_ALLOC_SZ 38 | // must be power of 2 (0 = agressive alloc, 1 = smallest memory usage, 2 = good compromise) 39 | #define SPP_ALLOC_SZ 0 40 | #endif 41 | 42 | #ifndef SPP_STORE_NUM_ITEMS 43 | // 1 uses a little bit more memory, but faster!! 44 | #define SPP_STORE_NUM_ITEMS 1 45 | #endif 46 | 47 | 48 | // --------------------------------------------------------------------------- 49 | // Compiler detection code (SPP_ proprocessor macros) derived from Boost 50 | // libraries. Therefore Boost software licence reproduced below. 51 | // --------------------------------------------------------------------------- 52 | // Boost Software License - Version 1.0 - August 17th, 2003 53 | // 54 | // Permission is hereby granted, free of charge, to any person or organization 55 | // obtaining a copy of the software and accompanying documentation covered by 56 | // this license (the "Software") to use, reproduce, display, distribute, 57 | // execute, and transmit the Software, and to prepare derivative works of the 58 | // Software, and to permit third-parties to whom the Software is furnished to 59 | // do so, all subject to the following: 60 | // 61 | // The copyright notices in the Software and this entire statement, including 62 | // the above license grant, this restriction and the following disclaimer, 63 | // must be included in all copies of the Software, in whole or in part, and 64 | // all derivative works of the Software, unless such copies or derivative 65 | // works are solely in the form of machine-executable object code generated by 66 | // a source language processor. 67 | // 68 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 69 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 70 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 71 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 72 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 73 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 74 | // DEALINGS IN THE SOFTWARE. 75 | // --------------------------------------------------------------------------- 76 | 77 | // Boost like configuration 78 | // ------------------------ 79 | #if defined __clang__ 80 | 81 | #if defined(i386) 82 | #include 83 | inline void spp_cpuid(int info[4], int InfoType) { 84 | __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); 85 | } 86 | #endif 87 | 88 | #define SPP_POPCNT __builtin_popcount 89 | #define SPP_POPCNT64 __builtin_popcountll 90 | 91 | #define SPP_HAS_CSTDINT 92 | 93 | #ifndef __has_extension 94 | #define __has_extension __has_feature 95 | #endif 96 | 97 | #if !__has_feature(cxx_exceptions) && !defined(SPP_NO_EXCEPTIONS) 98 | #define SPP_NO_EXCEPTIONS 99 | #endif 100 | 101 | #if !__has_feature(cxx_rtti) && !defined(SPP_NO_RTTI) 102 | #define SPP_NO_RTTI 103 | #endif 104 | 105 | #if !__has_feature(cxx_rtti) && !defined(SPP_NO_TYPEID) 106 | #define SPP_NO_TYPEID 107 | #endif 108 | 109 | #if defined(__int64) && !defined(__GNUC__) 110 | #define SPP_HAS_MS_INT64 111 | #endif 112 | 113 | #define SPP_HAS_NRVO 114 | 115 | // Branch prediction hints 116 | #if defined(__has_builtin) 117 | #if __has_builtin(__builtin_expect) 118 | #define SPP_LIKELY(x) __builtin_expect(x, 1) 119 | #define SPP_UNLIKELY(x) __builtin_expect(x, 0) 120 | #endif 121 | #endif 122 | 123 | // Clang supports "long long" in all compilation modes. 124 | #define SPP_HAS_LONG_LONG 125 | 126 | #if !__has_feature(cxx_constexpr) 127 | #define SPP_NO_CXX11_CONSTEXPR 128 | #endif 129 | 130 | #if !__has_feature(cxx_decltype) 131 | #define SPP_NO_CXX11_DECLTYPE 132 | #endif 133 | 134 | #if !__has_feature(cxx_decltype_incomplete_return_types) 135 | #define SPP_NO_CXX11_DECLTYPE_N3276 136 | #endif 137 | 138 | #if !__has_feature(cxx_defaulted_functions) 139 | #define SPP_NO_CXX11_DEFAULTED_FUNCTIONS 140 | #endif 141 | 142 | #if !__has_feature(cxx_deleted_functions) 143 | #define SPP_NO_CXX11_DELETED_FUNCTIONS 144 | #endif 145 | 146 | #if !__has_feature(cxx_explicit_conversions) 147 | #define SPP_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS 148 | #endif 149 | 150 | #if !__has_feature(cxx_default_function_template_args) 151 | #define SPP_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS 152 | #endif 153 | 154 | #if !__has_feature(cxx_generalized_initializers) 155 | #define SPP_NO_CXX11_HDR_INITIALIZER_LIST 156 | #endif 157 | 158 | #if !__has_feature(cxx_lambdas) 159 | #define SPP_NO_CXX11_LAMBDAS 160 | #endif 161 | 162 | #if !__has_feature(cxx_local_type_template_args) 163 | #define SPP_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS 164 | #endif 165 | 166 | #if !__has_feature(cxx_raw_string_literals) 167 | #define SPP_NO_CXX11_RAW_LITERALS 168 | #endif 169 | 170 | #if !__has_feature(cxx_reference_qualified_functions) 171 | #define SPP_NO_CXX11_REF_QUALIFIERS 172 | #endif 173 | 174 | #if !__has_feature(cxx_generalized_initializers) 175 | #define SPP_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX 176 | #endif 177 | 178 | #if !__has_feature(cxx_rvalue_references) 179 | #define SPP_NO_CXX11_RVALUE_REFERENCES 180 | #endif 181 | 182 | #if !__has_feature(cxx_static_assert) 183 | #define SPP_NO_CXX11_STATIC_ASSERT 184 | #endif 185 | 186 | #if !__has_feature(cxx_alias_templates) 187 | #define SPP_NO_CXX11_TEMPLATE_ALIASES 188 | #endif 189 | 190 | #if !__has_feature(cxx_variadic_templates) 191 | #define SPP_NO_CXX11_VARIADIC_TEMPLATES 192 | #endif 193 | 194 | #if !__has_feature(cxx_user_literals) 195 | #define SPP_NO_CXX11_USER_DEFINED_LITERALS 196 | #endif 197 | 198 | #if !__has_feature(cxx_alignas) 199 | #define SPP_NO_CXX11_ALIGNAS 200 | #endif 201 | 202 | #if !__has_feature(cxx_trailing_return) 203 | #define SPP_NO_CXX11_TRAILING_RESULT_TYPES 204 | #endif 205 | 206 | #if !__has_feature(cxx_inline_namespaces) 207 | #define SPP_NO_CXX11_INLINE_NAMESPACES 208 | #endif 209 | 210 | #if !__has_feature(cxx_override_control) 211 | #define SPP_NO_CXX11_FINAL 212 | #endif 213 | 214 | #if !(__has_feature(__cxx_binary_literals__) || __has_extension(__cxx_binary_literals__)) 215 | #define SPP_NO_CXX14_BINARY_LITERALS 216 | #endif 217 | 218 | #if !__has_feature(__cxx_decltype_auto__) 219 | #define SPP_NO_CXX14_DECLTYPE_AUTO 220 | #endif 221 | 222 | #if !__has_feature(__cxx_init_captures__) 223 | #define SPP_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES 224 | #endif 225 | 226 | #if !__has_feature(__cxx_generic_lambdas__) 227 | #define SPP_NO_CXX14_GENERIC_LAMBDAS 228 | #endif 229 | 230 | 231 | #if !__has_feature(__cxx_generic_lambdas__) || !__has_feature(__cxx_relaxed_constexpr__) 232 | #define SPP_NO_CXX14_CONSTEXPR 233 | #endif 234 | 235 | #if !__has_feature(__cxx_return_type_deduction__) 236 | #define SPP_NO_CXX14_RETURN_TYPE_DEDUCTION 237 | #endif 238 | 239 | #if !__has_feature(__cxx_variable_templates__) 240 | #define SPP_NO_CXX14_VARIABLE_TEMPLATES 241 | #endif 242 | 243 | #if __cplusplus < 201400 244 | #define SPP_NO_CXX14_DIGIT_SEPARATORS 245 | #endif 246 | 247 | #if defined(__has_builtin) && __has_builtin(__builtin_unreachable) 248 | #define SPP_UNREACHABLE_RETURN(x) __builtin_unreachable(); 249 | #endif 250 | 251 | #define SPP_ATTRIBUTE_UNUSED __attribute__((__unused__)) 252 | 253 | #ifndef SPP_COMPILER 254 | #define SPP_COMPILER "Clang version " __clang_version__ 255 | #endif 256 | 257 | #define SPP_CLANG 1 258 | 259 | 260 | #elif defined __GNUC__ 261 | 262 | #define SPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 263 | 264 | // definition to expand macro then apply to pragma message 265 | // #define VALUE_TO_STRING(x) #x 266 | // #define VALUE(x) VALUE_TO_STRING(x) 267 | // #define VAR_NAME_VALUE(var) #var "=" VALUE(var) 268 | // #pragma message(VAR_NAME_VALUE(SPP_GCC_VERSION)) 269 | 270 | #if defined(i386) 271 | #include 272 | inline void spp_cpuid(int info[4], int InfoType) { 273 | __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); 274 | } 275 | #endif 276 | 277 | // __POPCNT__ defined when the compiled with popcount support 278 | // (-mpopcnt compiler option is given for example) 279 | #ifdef __POPCNT__ 280 | // slower unless compiled iwith -mpopcnt 281 | #define SPP_POPCNT __builtin_popcount 282 | #define SPP_POPCNT64 __builtin_popcountll 283 | #endif 284 | 285 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) 286 | #define SPP_GCC_CXX11 287 | #endif 288 | 289 | #if __GNUC__ == 3 290 | #if defined (__PATHSCALE__) 291 | #define SPP_NO_TWO_PHASE_NAME_LOOKUP 292 | #define SPP_NO_IS_ABSTRACT 293 | #endif 294 | 295 | #if __GNUC_MINOR__ < 4 296 | #define SPP_NO_IS_ABSTRACT 297 | #endif 298 | 299 | #define SPP_NO_CXX11_EXTERN_TEMPLATE 300 | #endif 301 | 302 | #if __GNUC__ < 4 303 | // 304 | // All problems to gcc-3.x and earlier here: 305 | // 306 | #define SPP_NO_TWO_PHASE_NAME_LOOKUP 307 | #ifdef __OPEN64__ 308 | #define SPP_NO_IS_ABSTRACT 309 | #endif 310 | #endif 311 | 312 | // GCC prior to 3.4 had #pragma once too but it didn't work well with filesystem links 313 | #if SPP_GCC_VERSION >= 30400 314 | #define SPP_HAS_PRAGMA_ONCE 315 | #endif 316 | 317 | #if SPP_GCC_VERSION < 40400 318 | // Previous versions of GCC did not completely implement value-initialization: 319 | // GCC Bug 30111, "Value-initialization of POD base class doesn't initialize 320 | // members", reported by Jonathan Wakely in 2006, 321 | // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 (fixed for GCC 4.4) 322 | // GCC Bug 33916, "Default constructor fails to initialize array members", 323 | // reported by Michael Elizabeth Chastain in 2007, 324 | // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 (fixed for GCC 4.2.4) 325 | // See also: http://www.boost.org/libs/utility/value_init.htm #compiler_issues 326 | #define SPP_NO_COMPLETE_VALUE_INITIALIZATION 327 | #endif 328 | 329 | #if !defined(__EXCEPTIONS) && !defined(SPP_NO_EXCEPTIONS) 330 | #define SPP_NO_EXCEPTIONS 331 | #endif 332 | 333 | // 334 | // Threading support: Turn this on unconditionally here (except for 335 | // those platforms where we can know for sure). It will get turned off again 336 | // later if no threading API is detected. 337 | // 338 | #if !defined(__MINGW32__) && !defined(linux) && !defined(__linux) && !defined(__linux__) 339 | #define SPP_HAS_THREADS 340 | #endif 341 | 342 | // 343 | // gcc has "long long" 344 | // Except on Darwin with standard compliance enabled (-pedantic) 345 | // Apple gcc helpfully defines this macro we can query 346 | // 347 | #if !defined(__DARWIN_NO_LONG_LONG) 348 | #define SPP_HAS_LONG_LONG 349 | #endif 350 | 351 | // 352 | // gcc implements the named return value optimization since version 3.1 353 | // 354 | #define SPP_HAS_NRVO 355 | 356 | // Branch prediction hints 357 | #define SPP_LIKELY(x) __builtin_expect(x, 1) 358 | #define SPP_UNLIKELY(x) __builtin_expect(x, 0) 359 | 360 | // 361 | // Dynamic shared object (DSO) and dynamic-link library (DLL) support 362 | // 363 | #if __GNUC__ >= 4 364 | #if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && !defined(__CYGWIN__) 365 | // All Win32 development environments, including 64-bit Windows and MinGW, define 366 | // _WIN32 or one of its variant spellings. Note that Cygwin is a POSIX environment, 367 | // so does not define _WIN32 or its variants. 368 | #define SPP_HAS_DECLSPEC 369 | #define SPP_SYMBOL_EXPORT __attribute__((__dllexport__)) 370 | #define SPP_SYMBOL_IMPORT __attribute__((__dllimport__)) 371 | #else 372 | #define SPP_SYMBOL_EXPORT __attribute__((__visibility__("default"))) 373 | #define SPP_SYMBOL_IMPORT 374 | #endif 375 | 376 | #define SPP_SYMBOL_VISIBLE __attribute__((__visibility__("default"))) 377 | #else 378 | // config/platform/win32.hpp will define SPP_SYMBOL_EXPORT, etc., unless already defined 379 | #define SPP_SYMBOL_EXPORT 380 | #endif 381 | 382 | // 383 | // RTTI and typeinfo detection is possible post gcc-4.3: 384 | // 385 | #if SPP_GCC_VERSION > 40300 386 | #ifndef __GXX_RTTI 387 | #ifndef SPP_NO_TYPEID 388 | #define SPP_NO_TYPEID 389 | #endif 390 | #ifndef SPP_NO_RTTI 391 | #define SPP_NO_RTTI 392 | #endif 393 | #endif 394 | #endif 395 | 396 | // 397 | // Recent GCC versions have __int128 when in 64-bit mode. 398 | // 399 | // We disable this if the compiler is really nvcc with C++03 as it 400 | // doesn't actually support __int128 as of CUDA_VERSION=7500 401 | // even though it defines __SIZEOF_INT128__. 402 | // See https://svn.boost.org/trac/boost/ticket/8048 403 | // https://svn.boost.org/trac/boost/ticket/11852 404 | // Only re-enable this for nvcc if you're absolutely sure 405 | // of the circumstances under which it's supported: 406 | // 407 | #if defined(__CUDACC__) 408 | #if defined(SPP_GCC_CXX11) 409 | #define SPP_NVCC_CXX11 410 | #else 411 | #define SPP_NVCC_CXX03 412 | #endif 413 | #endif 414 | 415 | #if defined(__SIZEOF_INT128__) && !defined(SPP_NVCC_CXX03) 416 | #define SPP_HAS_INT128 417 | #endif 418 | // 419 | // Recent GCC versions have a __float128 native type, we need to 420 | // include a std lib header to detect this - not ideal, but we'll 421 | // be including later anyway when we select the std lib. 422 | // 423 | // Nevertheless, as of CUDA 7.5, using __float128 with the host 424 | // compiler in pre-C++11 mode is still not supported. 425 | // See https://svn.boost.org/trac/boost/ticket/11852 426 | // 427 | #ifdef __cplusplus 428 | #include 429 | #else 430 | #include 431 | #endif 432 | 433 | #if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) && !defined(SPP_NVCC_CXX03) 434 | #define SPP_HAS_FLOAT128 435 | #endif 436 | 437 | // C++0x features in 4.3.n and later 438 | // 439 | #if (SPP_GCC_VERSION >= 40300) && defined(SPP_GCC_CXX11) 440 | // C++0x features are only enabled when -std=c++0x or -std=gnu++0x are 441 | // passed on the command line, which in turn defines 442 | // __GXX_EXPERIMENTAL_CXX0X__. 443 | #define SPP_HAS_DECLTYPE 444 | #define SPP_HAS_RVALUE_REFS 445 | #define SPP_HAS_STATIC_ASSERT 446 | #define SPP_HAS_VARIADIC_TMPL 447 | #define SPP_HAS_CSTDINT 448 | #else 449 | #define SPP_NO_CXX11_DECLTYPE 450 | #define SPP_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS 451 | #define SPP_NO_CXX11_RVALUE_REFERENCES 452 | #define SPP_NO_CXX11_STATIC_ASSERT 453 | #endif 454 | 455 | // C++0x features in 4.4.n and later 456 | // 457 | #if (SPP_GCC_VERSION < 40400) || !defined(SPP_GCC_CXX11) 458 | #define SPP_NO_CXX11_AUTO_DECLARATIONS 459 | #define SPP_NO_CXX11_AUTO_MULTIDECLARATIONS 460 | #define SPP_NO_CXX11_CHAR16_T 461 | #define SPP_NO_CXX11_CHAR32_T 462 | #define SPP_NO_CXX11_HDR_INITIALIZER_LIST 463 | #define SPP_NO_CXX11_DEFAULTED_FUNCTIONS 464 | #define SPP_NO_CXX11_DELETED_FUNCTIONS 465 | #define SPP_NO_CXX11_TRAILING_RESULT_TYPES 466 | #define SPP_NO_CXX11_INLINE_NAMESPACES 467 | #define SPP_NO_CXX11_VARIADIC_TEMPLATES 468 | #endif 469 | 470 | #if SPP_GCC_VERSION < 40500 471 | #define SPP_NO_SFINAE_EXPR 472 | #endif 473 | 474 | // GCC 4.5 forbids declaration of defaulted functions in private or protected sections 475 | #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 5) || !defined(SPP_GCC_CXX11) 476 | #define SPP_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS 477 | #endif 478 | 479 | // C++0x features in 4.5.0 and later 480 | // 481 | #if (SPP_GCC_VERSION < 40500) || !defined(SPP_GCC_CXX11) 482 | #define SPP_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS 483 | #define SPP_NO_CXX11_LAMBDAS 484 | #define SPP_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS 485 | #define SPP_NO_CXX11_RAW_LITERALS 486 | #endif 487 | 488 | // C++0x features in 4.6.n and later 489 | // 490 | #if (SPP_GCC_VERSION < 40600) || !defined(SPP_GCC_CXX11) 491 | #define SPP_NO_CXX11_CONSTEXPR 492 | #define SPP_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX 493 | #endif 494 | 495 | // C++0x features in 4.7.n and later 496 | // 497 | #if (SPP_GCC_VERSION < 40700) || !defined(SPP_GCC_CXX11) 498 | #define SPP_NO_CXX11_FINAL 499 | #define SPP_NO_CXX11_TEMPLATE_ALIASES 500 | #define SPP_NO_CXX11_USER_DEFINED_LITERALS 501 | #define SPP_NO_CXX11_FIXED_LENGTH_VARIADIC_TEMPLATE_EXPANSION_PACKS 502 | #endif 503 | 504 | // C++0x features in 4.8.n and later 505 | // 506 | #if (SPP_GCC_VERSION < 40800) || !defined(SPP_GCC_CXX11) 507 | #define SPP_NO_CXX11_ALIGNAS 508 | #endif 509 | 510 | // C++0x features in 4.8.1 and later 511 | // 512 | #if (SPP_GCC_VERSION < 40801) || !defined(SPP_GCC_CXX11) 513 | #define SPP_NO_CXX11_DECLTYPE_N3276 514 | #define SPP_NO_CXX11_REF_QUALIFIERS 515 | #define SPP_NO_CXX14_BINARY_LITERALS 516 | #endif 517 | 518 | // C++14 features in 4.9.0 and later 519 | // 520 | #if (SPP_GCC_VERSION < 40900) || (__cplusplus < 201300) 521 | #define SPP_NO_CXX14_RETURN_TYPE_DEDUCTION 522 | #define SPP_NO_CXX14_GENERIC_LAMBDAS 523 | #define SPP_NO_CXX14_DIGIT_SEPARATORS 524 | #define SPP_NO_CXX14_DECLTYPE_AUTO 525 | #if !((SPP_GCC_VERSION >= 40801) && (SPP_GCC_VERSION < 40900) && defined(SPP_GCC_CXX11)) 526 | #define SPP_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES 527 | #endif 528 | #endif 529 | 530 | 531 | // C++ 14: 532 | #if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) 533 | #define SPP_NO_CXX14_CONSTEXPR 534 | #endif 535 | #if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) 536 | #define SPP_NO_CXX14_VARIABLE_TEMPLATES 537 | #endif 538 | 539 | // 540 | // Unused attribute: 541 | #if __GNUC__ >= 4 542 | #define SPP_ATTRIBUTE_UNUSED __attribute__((__unused__)) 543 | #endif 544 | // 545 | // __builtin_unreachable: 546 | #if SPP_GCC_VERSION >= 40800 547 | #define SPP_UNREACHABLE_RETURN(x) __builtin_unreachable(); 548 | #endif 549 | 550 | #ifndef SPP_COMPILER 551 | #define SPP_COMPILER "GNU C++ version " __VERSION__ 552 | #endif 553 | 554 | // ConceptGCC compiler: 555 | // http://www.generic-programming.org/software/ConceptGCC/ 556 | #ifdef __GXX_CONCEPTS__ 557 | #define SPP_HAS_CONCEPTS 558 | #define SPP_COMPILER "ConceptGCC version " __VERSION__ 559 | #endif 560 | 561 | #elif defined _MSC_VER 562 | 563 | #include // for __popcnt() 564 | 565 | #define SPP_POPCNT_CHECK // slower when defined, but we have to check! 566 | #define spp_cpuid(info, x) __cpuid(info, x) 567 | 568 | #define SPP_POPCNT __popcnt 569 | #if (SPP_GROUP_SIZE == 64 && INTPTR_MAX == INT64_MAX) 570 | #define SPP_POPCNT64 __popcnt64 571 | #endif 572 | 573 | // Attempt to suppress VC6 warnings about the length of decorated names (obsolete): 574 | #pragma warning( disable : 4503 ) // warning: decorated name length exceeded 575 | 576 | #define SPP_HAS_PRAGMA_ONCE 577 | #define SPP_HAS_CSTDINT 578 | 579 | // 580 | // versions check: 581 | // we don't support Visual C++ prior to version 7.1: 582 | #if _MSC_VER < 1310 583 | #error "Antique compiler not supported" 584 | #endif 585 | 586 | #if _MSC_FULL_VER < 180020827 587 | #define SPP_NO_FENV_H 588 | #endif 589 | 590 | #if _MSC_VER < 1400 591 | // although a conforming signature for swprint exists in VC7.1 592 | // it appears not to actually work: 593 | #define SPP_NO_SWPRINTF 594 | 595 | // Our extern template tests also fail for this compiler: 596 | #define SPP_NO_CXX11_EXTERN_TEMPLATE 597 | 598 | // Variadic macros do not exist for VC7.1 and lower 599 | #define SPP_NO_CXX11_VARIADIC_MACROS 600 | #endif 601 | 602 | #if _MSC_VER < 1500 // 140X == VC++ 8.0 603 | #undef SPP_HAS_CSTDINT 604 | #define SPP_NO_MEMBER_TEMPLATE_FRIENDS 605 | #endif 606 | 607 | #if _MSC_VER < 1600 // 150X == VC++ 9.0 608 | // A bug in VC9: 609 | #define SPP_NO_ADL_BARRIER 610 | #endif 611 | 612 | 613 | // MSVC (including the latest checked version) has not yet completely 614 | // implemented value-initialization, as is reported: 615 | // "VC++ does not value-initialize members of derived classes without 616 | // user-declared constructor", reported in 2009 by Sylvester Hesp: 617 | // https: //connect.microsoft.com/VisualStudio/feedback/details/484295 618 | // "Presence of copy constructor breaks member class initialization", 619 | // reported in 2009 by Alex Vakulenko: 620 | // https: //connect.microsoft.com/VisualStudio/feedback/details/499606 621 | // "Value-initialization in new-expression", reported in 2005 by 622 | // Pavel Kuznetsov (MetaCommunications Engineering): 623 | // https: //connect.microsoft.com/VisualStudio/feedback/details/100744 624 | // See also: http: //www.boost.org/libs/utility/value_init.htm #compiler_issues 625 | // (Niels Dekker, LKEB, May 2010) 626 | #define SPP_NO_COMPLETE_VALUE_INITIALIZATION 627 | 628 | #ifndef _NATIVE_WCHAR_T_DEFINED 629 | #define SPP_NO_INTRINSIC_WCHAR_T 630 | #endif 631 | 632 | // 633 | // check for exception handling support: 634 | #if !defined(_CPPUNWIND) && !defined(SPP_NO_EXCEPTIONS) 635 | #define SPP_NO_EXCEPTIONS 636 | #endif 637 | 638 | // 639 | // __int64 support: 640 | // 641 | #define SPP_HAS_MS_INT64 642 | #if defined(_MSC_EXTENSIONS) || (_MSC_VER >= 1400) 643 | #define SPP_HAS_LONG_LONG 644 | #else 645 | #define SPP_NO_LONG_LONG 646 | #endif 647 | 648 | #if (_MSC_VER >= 1400) && !defined(_DEBUG) 649 | #define SPP_HAS_NRVO 650 | #endif 651 | 652 | #if _MSC_VER >= 1500 // 150X == VC++ 9.0 653 | #define SPP_HAS_PRAGMA_DETECT_MISMATCH 654 | #endif 655 | 656 | // 657 | // disable Win32 API's if compiler extensions are 658 | // turned off: 659 | // 660 | #if !defined(_MSC_EXTENSIONS) && !defined(SPP_DISABLE_WIN32) 661 | #define SPP_DISABLE_WIN32 662 | #endif 663 | 664 | #if !defined(_CPPRTTI) && !defined(SPP_NO_RTTI) 665 | #define SPP_NO_RTTI 666 | #endif 667 | 668 | // 669 | // TR1 features: 670 | // 671 | #if _MSC_VER >= 1700 672 | // #define SPP_HAS_TR1_HASH // don't know if this is true yet. 673 | // #define SPP_HAS_TR1_TYPE_TRAITS // don't know if this is true yet. 674 | #define SPP_HAS_TR1_UNORDERED_MAP 675 | #define SPP_HAS_TR1_UNORDERED_SET 676 | #endif 677 | 678 | // 679 | // C++0x features 680 | // 681 | // See above for SPP_NO_LONG_LONG 682 | 683 | // C++ features supported by VC++ 10 (aka 2010) 684 | // 685 | #if _MSC_VER < 1600 686 | #define SPP_NO_CXX11_AUTO_DECLARATIONS 687 | #define SPP_NO_CXX11_AUTO_MULTIDECLARATIONS 688 | #define SPP_NO_CXX11_LAMBDAS 689 | #define SPP_NO_CXX11_RVALUE_REFERENCES 690 | #define SPP_NO_CXX11_STATIC_ASSERT 691 | #define SPP_NO_CXX11_DECLTYPE 692 | #endif // _MSC_VER < 1600 693 | 694 | #if _MSC_VER >= 1600 695 | #define SPP_HAS_STDINT_H 696 | #endif 697 | 698 | // C++11 features supported by VC++ 11 (aka 2012) 699 | // 700 | #if _MSC_VER < 1700 701 | #define SPP_NO_CXX11_FINAL 702 | #endif // _MSC_VER < 1700 703 | 704 | // C++11 features supported by VC++ 12 (aka 2013). 705 | // 706 | #if _MSC_FULL_VER < 180020827 707 | #define SPP_NO_CXX11_DEFAULTED_FUNCTIONS 708 | #define SPP_NO_CXX11_DELETED_FUNCTIONS 709 | #define SPP_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS 710 | #define SPP_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS 711 | #define SPP_NO_CXX11_RAW_LITERALS 712 | #define SPP_NO_CXX11_TEMPLATE_ALIASES 713 | #define SPP_NO_CXX11_TRAILING_RESULT_TYPES 714 | #define SPP_NO_CXX11_VARIADIC_TEMPLATES 715 | #define SPP_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX 716 | #define SPP_NO_CXX11_DECLTYPE_N3276 717 | #endif 718 | 719 | // C++11 features supported by VC++ 14 (aka 2014) CTP1 720 | #if (_MSC_FULL_VER < 190021730) 721 | #define SPP_NO_CXX11_REF_QUALIFIERS 722 | #define SPP_NO_CXX11_USER_DEFINED_LITERALS 723 | #define SPP_NO_CXX11_ALIGNAS 724 | #define SPP_NO_CXX11_INLINE_NAMESPACES 725 | #define SPP_NO_CXX14_DECLTYPE_AUTO 726 | #define SPP_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES 727 | #define SPP_NO_CXX14_RETURN_TYPE_DEDUCTION 728 | #define SPP_NO_CXX11_HDR_INITIALIZER_LIST 729 | #endif 730 | 731 | // C++11 features not supported by any versions 732 | #define SPP_NO_CXX11_CHAR16_T 733 | #define SPP_NO_CXX11_CHAR32_T 734 | #define SPP_NO_CXX11_CONSTEXPR 735 | #define SPP_NO_SFINAE_EXPR 736 | #define SPP_NO_TWO_PHASE_NAME_LOOKUP 737 | 738 | // C++ 14: 739 | #if !defined(__cpp_binary_literals) || (__cpp_binary_literals < 201304) 740 | #define SPP_NO_CXX14_BINARY_LITERALS 741 | #endif 742 | 743 | #if !defined(__cpp_constexpr) || (__cpp_constexpr < 201304) 744 | #define SPP_NO_CXX14_CONSTEXPR 745 | #endif 746 | 747 | #if (__cplusplus < 201304) // There's no SD6 check for this.... 748 | #define SPP_NO_CXX14_DIGIT_SEPARATORS 749 | #endif 750 | 751 | #if !defined(__cpp_generic_lambdas) || (__cpp_generic_lambdas < 201304) 752 | #define SPP_NO_CXX14_GENERIC_LAMBDAS 753 | #endif 754 | 755 | #if !defined(__cpp_variable_templates) || (__cpp_variable_templates < 201304) 756 | #define SPP_NO_CXX14_VARIABLE_TEMPLATES 757 | #endif 758 | 759 | #endif 760 | 761 | // from boost/config/suffix.hpp 762 | // ---------------------------- 763 | #ifndef SPP_ATTRIBUTE_UNUSED 764 | #define SPP_ATTRIBUTE_UNUSED 765 | #endif 766 | 767 | /* 768 | Try to persuade compilers to inline. 769 | */ 770 | #ifndef SPP_FORCEINLINE 771 | #if defined(__GNUC__) 772 | #define SPP_FORCEINLINE __inline __attribute__ ((always_inline)) 773 | #elif defined(_MSC_VER) 774 | #define SPP_FORCEINLINE __forceinline 775 | #else 776 | #define SPP_FORCEINLINE inline 777 | #endif 778 | #endif 779 | 780 | 781 | #endif // spp_config_h_guard 782 | -------------------------------------------------------------------------------- /sparsepp/spp_memory.h: -------------------------------------------------------------------------------- 1 | #if !defined(spp_memory_h_guard) 2 | #define spp_memory_h_guard 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(_WIN32) || defined( __CYGWIN__) 9 | #define SPP_WIN 10 | #endif 11 | 12 | #ifdef SPP_WIN 13 | #include 14 | #include 15 | #undef min 16 | #undef max 17 | #elif defined(__linux__) 18 | #include 19 | #include 20 | #elif defined(__FreeBSD__) 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #endif 28 | 29 | namespace spp 30 | { 31 | uint64_t GetSystemMemory() 32 | { 33 | #ifdef SPP_WIN 34 | MEMORYSTATUSEX memInfo; 35 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 36 | GlobalMemoryStatusEx(&memInfo); 37 | return static_cast(memInfo.ullTotalPageFile); 38 | #elif defined(__linux__) 39 | struct sysinfo memInfo; 40 | sysinfo (&memInfo); 41 | auto totalVirtualMem = memInfo.totalram; 42 | 43 | totalVirtualMem += memInfo.totalswap; 44 | totalVirtualMem *= memInfo.mem_unit; 45 | return static_cast(totalVirtualMem); 46 | #elif defined(__FreeBSD__) 47 | kvm_t *kd; 48 | u_int pageCnt; 49 | size_t pageCntLen = sizeof(pageCnt); 50 | u_int pageSize; 51 | struct kvm_swap kswap; 52 | uint64_t totalVirtualMem; 53 | 54 | pageSize = static_cast(getpagesize()); 55 | 56 | sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); 57 | totalVirtualMem = pageCnt * pageSize; 58 | 59 | kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); 60 | kvm_getswapinfo(kd, &kswap, 1, 0); 61 | kvm_close(kd); 62 | totalVirtualMem += kswap.ksw_total * pageSize; 63 | 64 | return totalVirtualMem; 65 | #else 66 | return 0; 67 | #endif 68 | } 69 | 70 | uint64_t GetTotalMemoryUsed() 71 | { 72 | #ifdef SPP_WIN 73 | MEMORYSTATUSEX memInfo; 74 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 75 | GlobalMemoryStatusEx(&memInfo); 76 | return static_cast(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile); 77 | #elif defined(__linux__) 78 | struct sysinfo memInfo; 79 | sysinfo(&memInfo); 80 | auto virtualMemUsed = memInfo.totalram - memInfo.freeram; 81 | 82 | virtualMemUsed += memInfo.totalswap - memInfo.freeswap; 83 | virtualMemUsed *= memInfo.mem_unit; 84 | 85 | return static_cast(virtualMemUsed); 86 | #elif defined(__FreeBSD__) 87 | kvm_t *kd; 88 | u_int pageSize; 89 | u_int pageCnt, freeCnt; 90 | size_t pageCntLen = sizeof(pageCnt); 91 | size_t freeCntLen = sizeof(freeCnt); 92 | struct kvm_swap kswap; 93 | uint64_t virtualMemUsed; 94 | 95 | pageSize = static_cast(getpagesize()); 96 | 97 | sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); 98 | sysctlbyname("vm.stats.vm.v_free_count", &freeCnt, &freeCntLen, NULL, 0); 99 | virtualMemUsed = (pageCnt - freeCnt) * pageSize; 100 | 101 | kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); 102 | kvm_getswapinfo(kd, &kswap, 1, 0); 103 | kvm_close(kd); 104 | virtualMemUsed += kswap.ksw_used * pageSize; 105 | 106 | return virtualMemUsed; 107 | #else 108 | return 0; 109 | #endif 110 | } 111 | 112 | uint64_t GetProcessMemoryUsed() 113 | { 114 | #ifdef SPP_WIN 115 | PROCESS_MEMORY_COUNTERS_EX pmc; 116 | GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); 117 | return static_cast(pmc.PrivateUsage); 118 | #elif defined(__linux__) 119 | auto parseLine = 120 | [](char* line)->int 121 | { 122 | auto i = strlen(line); 123 | 124 | while(*line < '0' || *line > '9') 125 | { 126 | line++; 127 | } 128 | 129 | line[i-3] = '\0'; 130 | i = atoi(line); 131 | return i; 132 | }; 133 | 134 | auto file = fopen("/proc/self/status", "r"); 135 | auto result = -1; 136 | char line[128]; 137 | 138 | while(fgets(line, 128, file) != nullptr) 139 | { 140 | if(strncmp(line, "VmSize:", 7) == 0) 141 | { 142 | result = parseLine(line); 143 | break; 144 | } 145 | } 146 | 147 | fclose(file); 148 | return static_cast(result) * 1024; 149 | #elif defined(__FreeBSD__) 150 | struct kinfo_proc info; 151 | size_t infoLen = sizeof(info); 152 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; 153 | 154 | sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &infoLen, NULL, 0); 155 | return static_cast(info.ki_rssize * getpagesize()); 156 | #else 157 | return 0; 158 | #endif 159 | } 160 | 161 | uint64_t GetPhysicalMemory() 162 | { 163 | #ifdef SPP_WIN 164 | MEMORYSTATUSEX memInfo; 165 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 166 | GlobalMemoryStatusEx(&memInfo); 167 | return static_cast(memInfo.ullTotalPhys); 168 | #elif defined(__linux__) 169 | struct sysinfo memInfo; 170 | sysinfo(&memInfo); 171 | 172 | auto totalPhysMem = memInfo.totalram; 173 | 174 | totalPhysMem *= memInfo.mem_unit; 175 | return static_cast(totalPhysMem); 176 | #elif defined(__FreeBSD__) 177 | u_long physMem; 178 | size_t physMemLen = sizeof(physMem); 179 | int mib[] = { CTL_HW, HW_PHYSMEM }; 180 | 181 | sysctl(mib, sizeof(mib) / sizeof(*mib), &physMem, &physMemLen, NULL, 0); 182 | return physMem; 183 | #else 184 | return 0; 185 | #endif 186 | } 187 | 188 | } 189 | 190 | #endif // spp_memory_h_guard 191 | -------------------------------------------------------------------------------- /sparsepp/spp_smartptr.h: -------------------------------------------------------------------------------- 1 | #if !defined(spp_smartptr_h_guard) 2 | #define spp_smartptr_h_guard 3 | 4 | 5 | /* ----------------------------------------------------------------------------------------------- 6 | * quick version of intrusive_ptr 7 | * ----------------------------------------------------------------------------------------------- 8 | */ 9 | 10 | #include 11 | #include "spp_config.h" 12 | 13 | // ------------------------------------------------------------------------ 14 | class spp_rc 15 | { 16 | public: 17 | spp_rc() : _cnt(0) {} 18 | spp_rc(const spp_rc &) : _cnt(0) {} 19 | void increment() const { ++_cnt; } 20 | void decrement() const { assert(_cnt); if (--_cnt == 0) delete this; } 21 | unsigned count() const { return _cnt; } 22 | 23 | protected: 24 | virtual ~spp_rc() {} 25 | 26 | private: 27 | mutable unsigned _cnt; 28 | }; 29 | 30 | // ------------------------------------------------------------------------ 31 | template 32 | class spp_sptr 33 | { 34 | public: 35 | spp_sptr() : _p(0) {} 36 | spp_sptr(T *p) : _p(p) { if (_p) _p->increment(); } 37 | spp_sptr(const spp_sptr &o) : _p(o._p) { if (_p) _p->increment(); } 38 | #ifndef SPP_NO_CXX11_RVALUE_REFERENCES 39 | spp_sptr(spp_sptr &&o) : _p(o._p) { o._p = (T *)0; } 40 | spp_sptr& operator=(spp_sptr &&o) { this->swap(o); return *this; } 41 | #endif 42 | ~spp_sptr() { if (_p) _p->decrement(); } 43 | spp_sptr& operator=(const spp_sptr &o) { reset(o._p); return *this; } 44 | T* get() const { return _p; } 45 | void swap(spp_sptr &o) { T *tmp = _p; _p = o._p; o._p = tmp; } 46 | void reset(const T *p = 0) 47 | { 48 | if (p == _p) 49 | return; 50 | if (_p) _p->decrement(); 51 | _p = (T *)p; 52 | if (_p) _p->increment(); 53 | } 54 | T* operator->() const { return const_cast(_p); } 55 | bool operator!() const { return _p == 0; } 56 | 57 | private: 58 | T *_p; 59 | }; 60 | 61 | // ------------------------------------------------------------------------ 62 | namespace std 63 | { 64 | template 65 | inline void swap(spp_sptr &a, spp_sptr &b) 66 | { 67 | a.swap(b); 68 | } 69 | } 70 | 71 | #endif // spp_smartptr_h_guard 72 | -------------------------------------------------------------------------------- /sparsepp/spp_stdint.h: -------------------------------------------------------------------------------- 1 | #if !defined(spp_stdint_h_guard) 2 | #define spp_stdint_h_guard 3 | 4 | #include "spp_config.h" 5 | 6 | #if defined(SPP_HAS_CSTDINT) && (__cplusplus >= 201103) 7 | #include 8 | #else 9 | #if defined(__FreeBSD__) || defined(__IBMCPP__) || defined(_AIX) 10 | #include 11 | #else 12 | #include 13 | #endif 14 | #endif 15 | 16 | #endif // spp_stdint_h_guard 17 | -------------------------------------------------------------------------------- /sparsepp/spp_timer.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2016 Mariano Gonzalez 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 | #ifndef spp_timer_h_guard 24 | #define spp_timer_h_guard 25 | 26 | #include 27 | 28 | namespace spp 29 | { 30 | template 31 | class Timer 32 | { 33 | public: 34 | Timer() { reset(); } 35 | void reset() { _start = _snap = clock::now(); } 36 | void snap() { _snap = clock::now(); } 37 | 38 | float get_total() const { return get_diff(_start, clock::now()); } 39 | float get_delta() const { return get_diff(_snap, clock::now()); } 40 | 41 | private: 42 | using clock = std::chrono::high_resolution_clock; 43 | using point = std::chrono::time_point; 44 | 45 | template 46 | static T get_diff(const point& start, const point& end) 47 | { 48 | using duration_t = std::chrono::duration; 49 | 50 | return std::chrono::duration_cast(end - start).count(); 51 | } 52 | 53 | point _start; 54 | point _snap; 55 | }; 56 | } 57 | 58 | #endif // spp_timer_h_guard 59 | -------------------------------------------------------------------------------- /sparsepp/spp_traits.h: -------------------------------------------------------------------------------- 1 | #if !defined(spp_traits_h_guard) 2 | #define spp_traits_h_guard 3 | 4 | #include "spp_config.h" 5 | 6 | template class HashObject; // for Google's benchmark, not in spp namespace! 7 | 8 | namespace spp_ 9 | { 10 | 11 | // --------------------------------------------------------------------------- 12 | // type_traits we need 13 | // --------------------------------------------------------------------------- 14 | template 15 | struct integral_constant { static const T value = v; }; 16 | 17 | template const T integral_constant::value; 18 | 19 | typedef integral_constant true_type; 20 | typedef integral_constant false_type; 21 | 22 | typedef integral_constant zero_type; 23 | typedef integral_constant one_type; 24 | typedef integral_constant two_type; 25 | typedef integral_constant three_type; 26 | 27 | template struct is_same : public false_type { }; 28 | template struct is_same : public true_type { }; 29 | 30 | template struct remove_const { typedef T type; }; 31 | template struct remove_const { typedef T type; }; 32 | 33 | template struct remove_volatile { typedef T type; }; 34 | template struct remove_volatile { typedef T type; }; 35 | 36 | template struct remove_cv 37 | { 38 | typedef typename remove_const::type>::type type; 39 | }; 40 | 41 | // ---------------- is_integral ---------------------------------------- 42 | template struct is_integral; 43 | template struct is_integral : false_type { }; 44 | template<> struct is_integral : true_type { }; 45 | template<> struct is_integral : true_type { }; 46 | template<> struct is_integral : true_type { }; 47 | template<> struct is_integral : true_type { }; 48 | template<> struct is_integral : true_type { }; 49 | template<> struct is_integral : true_type { }; 50 | template<> struct is_integral : true_type { }; 51 | template<> struct is_integral : true_type { }; 52 | template<> struct is_integral : true_type { }; 53 | template<> struct is_integral : true_type { }; 54 | #ifdef SPP_HAS_LONG_LONG 55 | template<> struct is_integral : true_type { }; 56 | template<> struct is_integral : true_type { }; 57 | #endif 58 | template struct is_integral : is_integral { }; 59 | template struct is_integral : is_integral { }; 60 | template struct is_integral : is_integral { }; 61 | 62 | // ---------------- is_floating_point ---------------------------------------- 63 | template struct is_floating_point; 64 | template struct is_floating_point : false_type { }; 65 | template<> struct is_floating_point : true_type { }; 66 | template<> struct is_floating_point : true_type { }; 67 | template<> struct is_floating_point : true_type { }; 68 | template struct is_floating_point : is_floating_point { }; 69 | template struct is_floating_point : is_floating_point { }; 70 | template struct is_floating_point : is_floating_point { }; 71 | 72 | // ---------------- is_pointer ---------------------------------------- 73 | template struct is_pointer; 74 | template struct is_pointer : false_type { }; 75 | template struct is_pointer : true_type { }; 76 | template struct is_pointer : is_pointer { }; 77 | template struct is_pointer : is_pointer { }; 78 | template struct is_pointer : is_pointer { }; 79 | 80 | // ---------------- is_reference ---------------------------------------- 81 | template struct is_reference; 82 | template struct is_reference : false_type {}; 83 | template struct is_reference : true_type {}; 84 | 85 | // ---------------- is_relocatable ---------------------------------------- 86 | // relocatable values can be moved around in memory using memcpy and remain 87 | // correct. Most types are relocatable, an example of a type who is not would 88 | // be a struct which contains a pointer to a buffer inside itself - this is the 89 | // case for std::string in gcc 5. 90 | // ------------------------------------------------------------------------ 91 | template struct is_relocatable; 92 | template struct is_relocatable : 93 | integral_constant::value || 94 | is_floating_point::value || 95 | is_pointer::value 96 | )> 97 | { }; 98 | 99 | template struct is_relocatable > : true_type { }; 100 | 101 | template struct is_relocatable : is_relocatable { }; 102 | template struct is_relocatable : is_relocatable { }; 103 | template struct is_relocatable : is_relocatable { }; 104 | template struct is_relocatable : is_relocatable { }; 105 | template struct is_relocatable > : 106 | integral_constant::value && is_relocatable::value)> 107 | { }; 108 | 109 | // A template helper used to select A or B based on a condition. 110 | // ------------------------------------------------------------ 111 | template 112 | struct if_ 113 | { 114 | typedef A type; 115 | }; 116 | 117 | template 118 | struct if_ 119 | { 120 | typedef B type; 121 | }; 122 | 123 | } // spp_ namespace 124 | 125 | #endif // spp_traits_h_guard 126 | -------------------------------------------------------------------------------- /sparsepp/spp_utils.h: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | // Copyright (c) 2016, Steven Gregory Popovitch - greg7mdp@gmail.com 3 | // All rights reserved. 4 | // 5 | // Code derived derived from Boost libraries. 6 | // Boost software licence reproduced below. 7 | // 8 | // Redistribution and use in source and binary forms, with or without 9 | // modification, are permitted provided that the following conditions are 10 | // met: 11 | // 12 | // * Redistributions of source code must retain the above copyright 13 | // notice, this list of conditions and the following disclaimer. 14 | // * Redistributions in binary form must reproduce the above 15 | // copyright notice, this list of conditions and the following disclaimer 16 | // in the documentation and/or other materials provided with the 17 | // distribution. 18 | // * The name of Steven Gregory Popovitch may not be used to 19 | // endorse or promote products derived from this software without 20 | // specific prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | // ---------------------------------------------------------------------- 34 | 35 | // --------------------------------------------------------------------------- 36 | // Boost Software License - Version 1.0 - August 17th, 2003 37 | // 38 | // Permission is hereby granted, free of charge, to any person or organization 39 | // obtaining a copy of the software and accompanying documentation covered by 40 | // this license (the "Software") to use, reproduce, display, distribute, 41 | // execute, and transmit the Software, and to prepare derivative works of the 42 | // Software, and to permit third-parties to whom the Software is furnished to 43 | // do so, all subject to the following: 44 | // 45 | // The copyright notices in the Software and this entire statement, including 46 | // the above license grant, this restriction and the following disclaimer, 47 | // must be included in all copies of the Software, in whole or in part, and 48 | // all derivative works of the Software, unless such copies or derivative 49 | // works are solely in the form of machine-executable object code generated by 50 | // a source language processor. 51 | // 52 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 53 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 54 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 55 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 56 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 57 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 58 | // DEALINGS IN THE SOFTWARE. 59 | // --------------------------------------------------------------------------- 60 | 61 | // ---------------------------------------------------------------------- 62 | // H A S H F U N C T I O N S 63 | // ---------------------------- 64 | // 65 | // Implements spp::spp_hash() and spp::hash_combine() 66 | // ---------------------------------------------------------------------- 67 | 68 | #if !defined(spp_utils_h_guard_) 69 | #define spp_utils_h_guard_ 70 | 71 | #if defined(_MSC_VER) 72 | #if (_MSC_VER >= 1600 ) // vs2010 (1900 is vs2015) 73 | #include 74 | #define SPP_HASH_CLASS std::hash 75 | #else 76 | #include 77 | #define SPP_HASH_CLASS stdext::hash_compare 78 | #endif 79 | #if (_MSC_FULL_VER < 190021730) 80 | #define SPP_NO_CXX11_NOEXCEPT 81 | #endif 82 | #elif defined __clang__ 83 | #if __has_feature(cxx_noexcept) || defined(SPP_CXX11) // define SPP_CXX11 if your compiler has 84 | #include 85 | #define SPP_HASH_CLASS std::hash 86 | #else 87 | #include 88 | #define SPP_HASH_CLASS std::tr1::hash 89 | #endif 90 | 91 | #if !__has_feature(cxx_noexcept) 92 | #define SPP_NO_CXX11_NOEXCEPT 93 | #endif 94 | #elif defined(__GNUC__) 95 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) 96 | #include 97 | #define SPP_HASH_CLASS std::hash 98 | 99 | #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40600 100 | #define SPP_NO_CXX11_NOEXCEPT 101 | #endif 102 | #else 103 | #include 104 | #define SPP_HASH_CLASS std::tr1::hash 105 | #define SPP_NO_CXX11_NOEXCEPT 106 | #endif 107 | #else 108 | #include 109 | #define SPP_HASH_CLASS std::hash 110 | #endif 111 | 112 | #ifdef SPP_NO_CXX11_NOEXCEPT 113 | #define SPP_NOEXCEPT 114 | #else 115 | #define SPP_NOEXCEPT noexcept 116 | #endif 117 | 118 | #ifdef SPP_NO_CXX11_CONSTEXPR 119 | #define SPP_CONSTEXPR 120 | #else 121 | #define SPP_CONSTEXPR constexpr 122 | #endif 123 | 124 | #ifdef SPP_NO_CXX14_CONSTEXPR 125 | #define SPP_CXX14_CONSTEXPR 126 | #else 127 | #define SPP_CXX14_CONSTEXPR constexpr 128 | #endif 129 | 130 | #define SPP_INLINE 131 | 132 | #ifndef spp_ 133 | #define spp_ spp 134 | #endif 135 | 136 | namespace spp_ 137 | { 138 | 139 | template T spp_min(T a, T b) { return a < b ? a : b; } 140 | template T spp_max(T a, T b) { return a >= b ? a : b; } 141 | 142 | template 143 | struct spp_hash 144 | { 145 | SPP_INLINE size_t operator()(const T &__v) const SPP_NOEXCEPT 146 | { 147 | SPP_HASH_CLASS hasher; 148 | return hasher(__v); 149 | } 150 | }; 151 | 152 | template 153 | struct spp_hash 154 | { 155 | static size_t spp_log2 (size_t val) SPP_NOEXCEPT 156 | { 157 | size_t res = 0; 158 | while (val > 1) 159 | { 160 | val >>= 1; 161 | res++; 162 | } 163 | return res; 164 | } 165 | 166 | SPP_INLINE size_t operator()(const T *__v) const SPP_NOEXCEPT 167 | { 168 | static const size_t shift = 3; // spp_log2(1 + sizeof(T)); // T might be incomplete! 169 | const uintptr_t i = (const uintptr_t)__v; 170 | return static_cast(i >> shift); 171 | } 172 | }; 173 | 174 | // from http://burtleburtle.net/bob/hash/integer.html 175 | // fast and efficient for power of two table sizes where we always 176 | // consider the last bits. 177 | // --------------------------------------------------------------- 178 | inline size_t spp_mix_32(uint32_t a) 179 | { 180 | a = a ^ (a >> 4); 181 | a = (a ^ 0xdeadbeef) + (a << 5); 182 | a = a ^ (a >> 11); 183 | return static_cast(a); 184 | } 185 | 186 | // More thorough scrambling as described in 187 | // https://gist.github.com/badboy/6267743 188 | // ---------------------------------------- 189 | inline size_t spp_mix_64(uint64_t a) 190 | { 191 | a = (~a) + (a << 21); // a = (a << 21) - a - 1; 192 | a = a ^ (a >> 24); 193 | a = (a + (a << 3)) + (a << 8); // a * 265 194 | a = a ^ (a >> 14); 195 | a = (a + (a << 2)) + (a << 4); // a * 21 196 | a = a ^ (a >> 28); 197 | a = a + (a << 31); 198 | return static_cast(a); 199 | } 200 | 201 | template 202 | struct spp_unary_function 203 | { 204 | typedef ArgumentType argument_type; 205 | typedef ResultType result_type; 206 | }; 207 | 208 | template <> 209 | struct spp_hash : public spp_unary_function 210 | { 211 | SPP_INLINE size_t operator()(bool __v) const SPP_NOEXCEPT 212 | { return static_cast(__v); } 213 | }; 214 | 215 | template <> 216 | struct spp_hash : public spp_unary_function 217 | { 218 | SPP_INLINE size_t operator()(char __v) const SPP_NOEXCEPT 219 | { return static_cast(__v); } 220 | }; 221 | 222 | template <> 223 | struct spp_hash : public spp_unary_function 224 | { 225 | SPP_INLINE size_t operator()(signed char __v) const SPP_NOEXCEPT 226 | { return static_cast(__v); } 227 | }; 228 | 229 | template <> 230 | struct spp_hash : public spp_unary_function 231 | { 232 | SPP_INLINE size_t operator()(unsigned char __v) const SPP_NOEXCEPT 233 | { return static_cast(__v); } 234 | }; 235 | 236 | template <> 237 | struct spp_hash : public spp_unary_function 238 | { 239 | SPP_INLINE size_t operator()(wchar_t __v) const SPP_NOEXCEPT 240 | { return static_cast(__v); } 241 | }; 242 | 243 | template <> 244 | struct spp_hash : public spp_unary_function 245 | { 246 | SPP_INLINE size_t operator()(int16_t __v) const SPP_NOEXCEPT 247 | { return spp_mix_32(static_cast(__v)); } 248 | }; 249 | 250 | template <> 251 | struct spp_hash : public spp_unary_function 252 | { 253 | SPP_INLINE size_t operator()(uint16_t __v) const SPP_NOEXCEPT 254 | { return spp_mix_32(static_cast(__v)); } 255 | }; 256 | 257 | template <> 258 | struct spp_hash : public spp_unary_function 259 | { 260 | SPP_INLINE size_t operator()(int32_t __v) const SPP_NOEXCEPT 261 | { return spp_mix_32(static_cast(__v)); } 262 | }; 263 | 264 | template <> 265 | struct spp_hash : public spp_unary_function 266 | { 267 | SPP_INLINE size_t operator()(uint32_t __v) const SPP_NOEXCEPT 268 | { return spp_mix_32(static_cast(__v)); } 269 | }; 270 | 271 | template <> 272 | struct spp_hash : public spp_unary_function 273 | { 274 | SPP_INLINE size_t operator()(int64_t __v) const SPP_NOEXCEPT 275 | { return spp_mix_64(static_cast(__v)); } 276 | }; 277 | 278 | template <> 279 | struct spp_hash : public spp_unary_function 280 | { 281 | SPP_INLINE size_t operator()(uint64_t __v) const SPP_NOEXCEPT 282 | { return spp_mix_64(static_cast(__v)); } 283 | }; 284 | 285 | template <> 286 | struct spp_hash : public spp_unary_function 287 | { 288 | SPP_INLINE size_t operator()(float __v) const SPP_NOEXCEPT 289 | { 290 | // -0.0 and 0.0 should return same hash 291 | uint32_t *as_int = reinterpret_cast(&__v); 292 | return (__v == 0) ? static_cast(0) : spp_mix_32(*as_int); 293 | } 294 | }; 295 | 296 | template <> 297 | struct spp_hash : public spp_unary_function 298 | { 299 | SPP_INLINE size_t operator()(double __v) const SPP_NOEXCEPT 300 | { 301 | // -0.0 and 0.0 should return same hash 302 | uint64_t *as_int = reinterpret_cast(&__v); 303 | return (__v == 0) ? static_cast(0) : spp_mix_64(*as_int); 304 | } 305 | }; 306 | 307 | template struct Combiner 308 | { 309 | inline void operator()(T& seed, T value); 310 | }; 311 | 312 | template struct Combiner 313 | { 314 | inline void operator()(T& seed, T value) 315 | { 316 | seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); 317 | } 318 | }; 319 | 320 | template struct Combiner 321 | { 322 | inline void operator()(T& seed, T value) 323 | { 324 | seed ^= value + T(0xc6a4a7935bd1e995) + (seed << 6) + (seed >> 2); 325 | } 326 | }; 327 | 328 | template 329 | inline void hash_combine(std::size_t& seed, T const& v) 330 | { 331 | spp_::spp_hash hasher; 332 | Combiner combiner; 333 | 334 | combiner(seed, hasher(v)); 335 | } 336 | 337 | static inline uint32_t s_spp_popcount_default(uint32_t i) SPP_NOEXCEPT 338 | { 339 | i = i - ((i >> 1) & 0x55555555); 340 | i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 341 | return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; 342 | } 343 | 344 | static inline uint32_t s_spp_popcount_default(uint64_t x) SPP_NOEXCEPT 345 | { 346 | const uint64_t m1 = uint64_t(0x5555555555555555); // binary: 0101... 347 | const uint64_t m2 = uint64_t(0x3333333333333333); // binary: 00110011.. 348 | const uint64_t m4 = uint64_t(0x0f0f0f0f0f0f0f0f); // binary: 4 zeros, 4 ones ... 349 | const uint64_t h01 = uint64_t(0x0101010101010101); // the sum of 256 to the power of 0,1,2,3... 350 | 351 | x -= (x >> 1) & m1; // put count of each 2 bits into those 2 bits 352 | x = (x & m2) + ((x >> 2) & m2); // put count of each 4 bits into those 4 bits 353 | x = (x + (x >> 4)) & m4; // put count of each 8 bits into those 8 bits 354 | return (x * h01)>>56; // returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24)+... 355 | } 356 | 357 | #ifdef __APPLE__ 358 | static inline uint32_t count_trailing_zeroes(size_t v) SPP_NOEXCEPT 359 | { 360 | size_t x = (v & -v) - 1; 361 | // sadly sizeof() required to build on macos 362 | return sizeof(size_t) == 8 ? s_spp_popcount_default((uint64_t)x) : s_spp_popcount_default((uint32_t)x); 363 | } 364 | 365 | static inline uint32_t s_popcount(size_t v) SPP_NOEXCEPT 366 | { 367 | // sadly sizeof() required to build on macos 368 | return sizeof(size_t) == 8 ? s_spp_popcount_default((uint64_t)v) : s_spp_popcount_default((uint32_t)v); 369 | } 370 | #else 371 | static inline uint32_t count_trailing_zeroes(size_t v) SPP_NOEXCEPT 372 | { 373 | return s_spp_popcount_default((v & -(intptr_t)v) - 1); 374 | } 375 | 376 | static inline uint32_t s_popcount(size_t v) SPP_NOEXCEPT 377 | { 378 | return s_spp_popcount_default(v); 379 | } 380 | #endif 381 | 382 | // ----------------------------------------------------------- 383 | // ----------------------------------------------------------- 384 | template 385 | class libc_allocator 386 | { 387 | public: 388 | typedef T value_type; 389 | typedef T* pointer; 390 | typedef ptrdiff_t difference_type; 391 | typedef const T* const_pointer; 392 | typedef size_t size_type; 393 | 394 | libc_allocator() {} 395 | libc_allocator(const libc_allocator&) {} 396 | 397 | template 398 | libc_allocator(const libc_allocator &) {} 399 | 400 | libc_allocator& operator=(const libc_allocator &) { return *this; } 401 | 402 | template 403 | libc_allocator& operator=(const libc_allocator &) { return *this; } 404 | 405 | #ifndef SPP_NO_CXX11_RVALUE_REFERENCES 406 | libc_allocator(libc_allocator &&) {} 407 | libc_allocator& operator=(libc_allocator &&) { return *this; } 408 | #endif 409 | 410 | pointer allocate(size_t n, const_pointer /* unused */= 0) 411 | { 412 | pointer res = static_cast(malloc(n * sizeof(T))); 413 | if (!res) 414 | throw std::bad_alloc(); 415 | return res; 416 | } 417 | 418 | void deallocate(pointer p, size_t /* unused */) 419 | { 420 | free(p); 421 | } 422 | 423 | pointer reallocate(pointer p, size_t new_size) 424 | { 425 | pointer res = static_cast(realloc(p, new_size * sizeof(T))); 426 | if (!res) 427 | throw std::bad_alloc(); 428 | return res; 429 | } 430 | 431 | // extra API to match spp_allocator interface 432 | pointer reallocate(pointer p, size_t /* old_size */, size_t new_size) 433 | { 434 | return static_cast(realloc(p, new_size * sizeof(T))); 435 | } 436 | 437 | size_type max_size() const 438 | { 439 | return static_cast(-1) / sizeof(value_type); 440 | } 441 | 442 | void construct(pointer p, const value_type& val) 443 | { 444 | new(p) value_type(val); 445 | } 446 | 447 | void destroy(pointer p) { p->~value_type(); } 448 | 449 | template 450 | struct rebind 451 | { 452 | typedef spp_::libc_allocator other; 453 | }; 454 | 455 | }; 456 | 457 | // forward declaration 458 | // ------------------- 459 | template 460 | class spp_allocator; 461 | 462 | } 463 | 464 | template 465 | inline bool operator==(const spp_::libc_allocator &, const spp_::libc_allocator &) 466 | { 467 | return true; 468 | } 469 | 470 | template 471 | inline bool operator!=(const spp_::libc_allocator &, const spp_::libc_allocator &) 472 | { 473 | return false; 474 | } 475 | 476 | #endif // spp_utils_h_guard_ 477 | 478 | -------------------------------------------------------------------------------- /spp.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{size = {rep.table._num_buckets}}} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | rep.table._num_buckets 16 | 17 | 18 | item_ptr = grp->_group 19 | cnt = grp->_num_buckets 20 | 21 | 22 | item_ptr,na 23 | item_ptr++ 24 | cnt-- 25 | 26 | ++grp 27 | 28 | 29 | 30 | 31 | 32 | 33 | end() 34 | end() 35 | {*col_current} 36 | 37 | *col_current 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(PackageTest CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED True) 6 | 7 | set(CMAKE_TOOLCHAIN_FILE "${CMAKE_BINARY_DIR}/generators/conan_toolchain.cmake" CACHE FILEPATH "Toolchain file generated by Conan") 8 | find_package(sparsepp CONFIG REQUIRED) 9 | 10 | add_executable(conantest src/conantest.cpp) 11 | target_link_libraries(conantest sparsepp::sparsepp) -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from conan import ConanFile 4 | from conan.tools.cmake import CMake, cmake_layout 5 | from conan.tools.build import can_run 6 | 7 | 8 | class helloTestConan(ConanFile): 9 | settings = "os", "compiler", "build_type", "arch" 10 | generators = "CMakeDeps", "CMakeToolchain" 11 | 12 | def requirements(self): 13 | self.requires(self.tested_reference_str) 14 | 15 | def build(self): 16 | cmake = CMake(self) 17 | cmake.configure() 18 | cmake.build() 19 | 20 | def layout(self): 21 | cmake_layout(self) 22 | 23 | def test(self): 24 | if can_run(self): 25 | cmd = os.path.join(self.cpp.build.bindir, "conantest") 26 | self.run(cmd, env="conanrun") -------------------------------------------------------------------------------- /test_package/src/conantest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | auto bar() { 7 | return std::make_tuple(3, 4.5, "Some string"); 8 | } 9 | 10 | int main() 11 | { 12 | auto[c, d, e] = bar(); 13 | std::cout << "c: " << c << ", d: " << d << ", e: " << e << "\n"; 14 | 15 | spp::sparse_hash_set test1; 16 | spp::sparse_hash_set test2 = std::move(test1); 17 | test1.clear(); 18 | test1.erase(0); 19 | } 20 | -------------------------------------------------------------------------------- /tests/makefile: -------------------------------------------------------------------------------- 1 | CXXSTD ?= c++11 2 | CXXFLAGS = -O2 -std=$(CXXSTD) -I.. 3 | CXXFLAGS += -Wall -pedantic -Wextra 4 | SPP_DEPS_1 = spp.h spp_utils.h spp_dlalloc.h spp_traits.h spp_config.h 5 | SPP_DEPS = $(addprefix ../sparsepp/,$(SPP_DEPS_1)) 6 | TARGETS = spp_test spp_alloc_test spp_bitset_test perftest1 bench 7 | 8 | 9 | ifeq ($(OS),Windows_NT) 10 | LDFLAGS = -lpsapi 11 | else 12 | OS = $(shell uname -s) 13 | ifeq ($(OS),Linux) 14 | CXXFLAGS += -D_XOPEN_SOURCE=700 15 | endif 16 | ifeq ($(OS),FreeBSD) 17 | LDFLAGS = -lkvm 18 | endif 19 | endif 20 | 21 | def: spp_test spp_relative_include_test 22 | 23 | all: $(TARGETS) 24 | 25 | clean: 26 | rm -rf $(TARGETS) vsprojects/x64/* vsprojects/x86/* 27 | 28 | test: 29 | ./spp_test 30 | 31 | spp_test: spp_test.cc $(SPP_DEPS) makefile 32 | $(CXX) $(CXXFLAGS) -D_CRT_SECURE_NO_WARNINGS spp_test.cc -o spp_test 33 | 34 | # Test that it's possible to use spp with relative includes only - without adding -I to the compiler 35 | spp_relative_include_test: spp_relative_include_test.cc $(SPP_DEPS) makefile 36 | $(CXX) $(filter-out -I%,$(CXXFLAGS)) -D_CRT_SECURE_NO_WARNINGS spp_relative_include_test.cc -o spp_relative_include_test 37 | 38 | %: %.cc $(SPP_DEPS) makefile 39 | $(CXX) $(CXXFLAGS) -DNDEBUG $< -o $@ $(LDFLAGS) 40 | 41 | -------------------------------------------------------------------------------- /tests/perftest1.cc: -------------------------------------------------------------------------------- 1 | // compile on linux with: g++ -std=c++11 -O2 perftest1.cc -o perftest1 2 | // ----------------------------------------------------------------------- 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #define SPP 1 15 | #define DENSE 0 16 | #define SPARSE 0 17 | #define STD 0 18 | 19 | #if SPP 20 | #include 21 | #elif DENSE 22 | #include 23 | #elif SPARSE 24 | #include 25 | #elif STD 26 | #include 27 | #endif 28 | 29 | using std::make_pair; 30 | 31 | template 32 | void test(T &s, int count) 33 | { 34 | spp::Timer timer; 35 | 36 | timer.snap(); 37 | srand(0); 38 | for (int i = 0; i < count; ++i) 39 | s.insert(make_pair(rand(), i)); 40 | 41 | printf("%d random inserts in %5.2f seconds\n", count, timer.get_delta() / 1000); 42 | 43 | timer.snap(); 44 | srand(0); 45 | for (int i = 0; i < count; ++i) 46 | s.find(rand()); 47 | 48 | printf("%d random finds in %5.2f seconds\n", count, timer.get_delta() / 1000); 49 | 50 | timer.snap(); 51 | srand(1); 52 | for (int i = 0; i < count; ++i) 53 | s.find(rand()); 54 | printf("%d random not-finds in %5.2f seconds\n", count, timer.get_delta() / 1000); 55 | 56 | s.clear(); 57 | timer.snap(); 58 | srand(0); 59 | for (int i = 0; i < count; ++i) 60 | s.insert(make_pair(i, i)); 61 | printf("%d sequential inserts in %5.2f seconds\n", count, timer.get_delta() / 1000); 62 | 63 | timer.snap(); 64 | srand(0); 65 | for (int i = 0; i < count; ++i) 66 | s.find(i); 67 | 68 | printf("%d sequential finds in %5.2f seconds\n", count, timer.get_delta() / 1000); 69 | 70 | timer.snap(); 71 | srand(1); 72 | for (int i = 0; i < count; ++i) 73 | { 74 | int x = rand(); 75 | s.find(x); 76 | } 77 | printf("%d random not-finds in %5.2f seconds\n", count, timer.get_delta() / 1000); 78 | 79 | s.clear(); 80 | timer.snap(); 81 | srand(0); 82 | for (int i = 0; i < count; ++i) 83 | s.insert(make_pair(-i, -i)); 84 | 85 | printf("%d neg sequential inserts in %5.2f seconds\n", count, timer.get_delta() / 1000); 86 | 87 | timer.snap(); 88 | srand(0); 89 | for (int i = 0; i < count; ++i) 90 | s.find(-i); 91 | 92 | printf("%d neg sequential finds in %5.2f seconds\n", count, timer.get_delta() / 1000); 93 | 94 | timer.snap(); 95 | srand(1); 96 | for (int i = 0; i < count; ++i) 97 | s.find(rand()); 98 | printf("%d random not-finds in %5.2f seconds\n", count, timer.get_delta() / 1000); 99 | 100 | s.clear(); 101 | } 102 | 103 | 104 | struct Hasher64 { 105 | size_t operator()(uint64_t k) const { return (k ^ 14695981039346656037ULL) * 1099511628211ULL; } 106 | }; 107 | 108 | struct Hasher32 { 109 | size_t operator()(uint32_t k) const { return (k ^ 2166136261U) * 16777619UL; } 110 | }; 111 | 112 | struct Hasheri32 { 113 | size_t operator()(int k) const 114 | { 115 | return (k ^ 2166136261U) * 16777619UL; 116 | } 117 | }; 118 | 119 | struct Hasher_32 { 120 | size_t operator()(int k) const 121 | { 122 | uint32_t a = (uint32_t)k; 123 | #if 0 124 | a = (a ^ 61) ^ (a >> 16); 125 | a = a + (a << 3); 126 | a = a ^ (a >> 4); 127 | a = a * 0x27d4eb2d; 128 | a = a ^ (a >> 15); 129 | return a; 130 | #else 131 | a = a ^ (a >> 4); 132 | a = (a ^ 0xdeadbeef) + (a << 5); 133 | a = a ^ (a >> 11); 134 | return a; 135 | #endif 136 | } 137 | }; 138 | 139 | int main() 140 | { 141 | #if SPP 142 | spp::sparse_hash_map s; 143 | printf ("Testing spp::sparse_hash_map\n"); 144 | #elif DENSE 145 | google::dense_hash_map s; 146 | s.set_empty_key(-INT_MAX); 147 | s.set_deleted_key(-(INT_MAX - 1)); 148 | printf ("Testing google::dense_hash_map\n"); 149 | #elif SPARSE 150 | google::sparse_hash_map s; 151 | s.set_deleted_key(-INT_MAX); 152 | printf ("Testing google::sparse_hash_map\n"); 153 | #elif STD 154 | std::unordered_map s; 155 | printf ("Testing std::unordered_map\n"); 156 | #endif 157 | printf ("------------------------------\n"); 158 | test(s, 50000000); 159 | 160 | 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /tests/spp_alloc_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // enable debugging code in spp_bitset.h 9 | #define SPP_TEST 1 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | static float _to_mb(uint64_t m) { return (float)((double)m / (1024 * 1024)); } 18 | 19 | // ----------------------------------------------------------- 20 | // ----------------------------------------------------------- 21 | template 22 | class TestAlloc 23 | { 24 | public: 25 | TestAlloc(size_t num_alloc = 8000000) : 26 | _num_alloc(num_alloc) 27 | { 28 | _allocated.resize(_num_alloc, nullptr); 29 | _sizes.resize(_num_alloc, 0); 30 | _start_mem_usage = spp::GetProcessMemoryUsed(); 31 | } 32 | 33 | void run() 34 | { 35 | srand(43); // always same sequence of random numbers 36 | 37 | for (size_t i=0; i<_num_alloc; ++i) 38 | _sizes[i] = std::max(2, (rand() % 5) * 2); 39 | 40 | spp::Timer timer; 41 | 42 | // allocate small buffers 43 | // ---------------------- 44 | for (size_t i=0; i<_num_alloc; ++i) 45 | { 46 | _allocated[i] = _allocator.allocate(_sizes[i]); 47 | _set_buf(_allocated[i], _sizes[i]); 48 | } 49 | 50 | #if 1 51 | // and grow the buffers to a max size of 24 each 52 | // --------------------------------------------- 53 | for (uint32_t j=4; j<26; j += 2) 54 | { 55 | for (size_t i=0; i<_num_alloc; ++i) 56 | { 57 | // if ( _sizes[i] < j) // windows allocator friendly! 58 | if ((rand() % 4) != 3 && _sizes[i] < j) // really messes up windows allocator 59 | { 60 | _allocated[i] = _allocator.reallocate(_allocated[i], _sizes[i], j); 61 | _check_buf(_allocated[i], _sizes[i]); 62 | _set_buf(_allocated[i], j); 63 | _sizes[i] = j; 64 | } 65 | } 66 | } 67 | #endif 68 | 69 | #if 0 70 | // test erase (shrinking the buffers) 71 | // --------------------------------------------- 72 | for (uint32_t j=28; j>4; j -= 2) 73 | { 74 | for (size_t i=0; i<_num_alloc; ++i) 75 | { 76 | // if ( _sizes[i] < j) // windows allocator friendly! 77 | if ((rand() % 4) != 3 && _sizes[i] > j) // really messes up windows allocator 78 | { 79 | _allocated[i] = _allocator.reallocate(_allocated[i], _sizes[i], j); 80 | _check_buf1(_allocated[i], _sizes[i]); 81 | _set_buf(_allocated[i], j); 82 | _sizes[i] = j; 83 | } 84 | } 85 | } 86 | #endif 87 | 88 | #if 0 89 | // and grow the buffers back to a max size of 24 each 90 | // -------------------------------------------------- 91 | for (uint32_t j=4; j<26; j += 2) 92 | { 93 | for (size_t i=0; i<_num_alloc; ++i) 94 | { 95 | // if ( _sizes[i] < j) // windows allocator friendly! 96 | if ((rand() % 4) != 3 && _sizes[i] < j) // really messes up windows allocator 97 | { 98 | _allocated[i] = _allocator.reallocate(_allocated[i], _sizes[i], j); 99 | _check_buf(_allocated[i], _sizes[i]); 100 | _set_buf(_allocated[i], j); 101 | _sizes[i] = j; 102 | } 103 | } 104 | } 105 | #endif 106 | 107 | size_t total_units = 0; 108 | for (size_t i=0; i<_num_alloc; ++i) 109 | total_units += _sizes[i]; 110 | 111 | uint64_t mem_usage = spp::GetProcessMemoryUsed(); 112 | uint64_t alloc_mem_usage = mem_usage - _start_mem_usage; 113 | uint64_t expected_mem_usage = total_units * sizeof(T); 114 | 115 | // finally free the memory 116 | // ----------------------- 117 | for (size_t i=0; i<_num_alloc; ++i) 118 | { 119 | _check_buf(_allocated[i], _sizes[i]); 120 | _allocator.deallocate(_allocated[i], _sizes[i]); 121 | } 122 | 123 | uint64_t mem_usage_end = spp::GetProcessMemoryUsed(); 124 | 125 | printf("allocated %zd entities of size %zd\n", total_units, sizeof(T)); 126 | printf("done in %3.2f seconds, mem_usage %4.1f/%4.1f/%4.1f MB\n", 127 | timer.get_total() / 1000, _to_mb(_start_mem_usage), _to_mb(mem_usage), _to_mb(mem_usage_end)); 128 | printf("mem usage: %4.1f, expected mem usage: %4.1f\n", 129 | _to_mb(alloc_mem_usage), 130 | _to_mb(expected_mem_usage)); 131 | if (expected_mem_usage <= alloc_mem_usage) 132 | printf("overhead: %4.1f%%\n", 133 | (float)((double)(alloc_mem_usage - expected_mem_usage) / expected_mem_usage) * 100); 134 | else 135 | printf("bug: alloc_mem_usage <= expected_mem_usage\n"); 136 | 137 | std::vector().swap(_allocated); 138 | std::vector().swap(_sizes); 139 | 140 | printf("\nmem usage after freeing vectors: %4.1f\n", _to_mb(spp::GetProcessMemoryUsed())); 141 | } 142 | 143 | private: 144 | 145 | void _set_buf(T *buff, uint32_t sz) { *buff = (T)sz; buff[sz - 1] = (T)sz; } 146 | void _check_buf1(T *buff, uint32_t sz) 147 | { 148 | assert(*buff == (T)sz); 149 | (void)(buff + sz); // silence warning 150 | } 151 | void _check_buf(T *buff, uint32_t sz) 152 | { 153 | assert(*buff == (T)sz && buff[sz - 1] == (T)sz); 154 | (void)(buff + sz); // silence warning 155 | } 156 | 157 | size_t _num_alloc; 158 | uint64_t _start_mem_usage; 159 | std::vector _allocated; 160 | std::vector _sizes; 161 | A _allocator; 162 | }; 163 | 164 | // ----------------------------------------------------------- 165 | // ----------------------------------------------------------- 166 | template 167 | void run_test(const char *alloc_name) 168 | { 169 | printf("\n---------------- testing %s\n\n", alloc_name); 170 | 171 | printf("\nmem usage before the alloc test: %4.1f\n", 172 | _to_mb(spp::GetProcessMemoryUsed())); 173 | { 174 | TestAlloc< X, A > test_alloc; 175 | test_alloc.run(); 176 | } 177 | printf("mem usage after the alloc test: %4.1f\n", 178 | _to_mb(spp::GetProcessMemoryUsed())); 179 | 180 | printf("\n\n"); 181 | } 182 | 183 | // ----------------------------------------------------------- 184 | // ----------------------------------------------------------- 185 | int main() 186 | { 187 | typedef uint64_t X; 188 | 189 | run_test>("libc_allocator"); 190 | run_test>("spp_allocator"); 191 | } 192 | -------------------------------------------------------------------------------- /tests/spp_bitset_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // enable debugging code in spp_bitset.h 9 | #define SPP_TEST 1 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | // ----------------------------------------------------------- 18 | // ----------------------------------------------------------- 19 | template 20 | class TestBitset 21 | { 22 | public: 23 | typedef spp::spp_bitset BS; 24 | 25 | TestBitset() 26 | {} 27 | 28 | void test_set(size_t num_iter) 29 | { 30 | size_t num_errors = 0; 31 | BS bs, bs2; 32 | 33 | printf("testing set on spp_bitset<%zu> , num_iter=%6zu -> ", N, num_iter); 34 | 35 | for (size_t i=0; i, num_iter=%6zu -> ", N, num_iter); 56 | 57 | for (size_t i=0; i, num_iter=%6zu -> ", N, num_iter); 78 | 79 | for (size_t i=0; i<4 * N; ++i) 80 | { 81 | bs.set(rand() % N); 82 | if (i > 2 * N) 83 | { 84 | for (size_t j=0; j, num_iter=%6zu -> ", N, num_iter); 111 | 112 | for (size_t i=0; i 1000) 141 | { 142 | bs.set(1000); 143 | size_t longest = bs.longest_zero_sequence(); 144 | assert(longest == 1000-11 || longest == N-1001); 145 | if (!(longest == 1000-11 || longest == N-1001)) 146 | ++num_errors; 147 | } 148 | 149 | spp::Timer timer_lz; 150 | spp::Timer timer_lz_slow; 151 | float lz_time(0), lz_time_slow(0); 152 | 153 | printf("testing longest_zero_sequence() , num_iter=%6zu -> ", num_iter); 154 | srand(1); 155 | for (size_t i=0; i 1000) 191 | { 192 | bs.set(1000); 193 | size_t longest = bs.longest_zero_sequence(); 194 | assert(longest == 1000-11 || longest == N-1001); 195 | if (!(longest == 1000-11 || longest == N-1001)) 196 | ++num_errors; 197 | } 198 | 199 | spp::Timer timer_lz; 200 | spp::Timer timer_lz_slow; 201 | float lz_time(0), lz_time_slow(0); 202 | 203 | printf("testing longest_zero_sequence2() , num_iter=%6zu -> ", num_iter); 204 | srand(1); 205 | for (size_t i=0; i timer_ctz; 236 | spp::Timer timer_ctz_slow; 237 | float ctz_time(0), ctz_time_slow(0); 238 | 239 | printf("testing count_trailing_zeroes() , num_iter=%6zu -> ", num_iter); 240 | for (size_t i=0; i test_bitset_1024; 277 | test_bitset_1024.run(); 278 | 279 | TestBitset<4096> test_bitset_4096; 280 | test_bitset_4096.run(); 281 | 282 | //TestBitset<8192> test_bitset_8192; 283 | //test_bitset_8192.run(); 284 | } 285 | -------------------------------------------------------------------------------- /tests/spp_relative_include_test.cc: -------------------------------------------------------------------------------- 1 | // Test that it's possible to use spp with relative includes only - without adding -I to the compiler 2 | #include "../sparsepp/spp.h" 3 | 4 | int main() 5 | { 6 | spp::sparse_hash_map dummy; 7 | } 8 | -------------------------------------------------------------------------------- /tests/vsprojects/spp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spp_test", "spp_test.vcxproj", "{9863A521-E9DB-4775-A276-CADEF726CF11}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spp_alloc_test", "spp_alloc_test.vcxproj", "{19BC4240-15ED-4C76-BC57-34BB70FE163B}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Debug|x64.ActiveCfg = Debug|x64 19 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Debug|x64.Build.0 = Debug|x64 20 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Debug|x86.ActiveCfg = Debug|Win32 21 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Debug|x86.Build.0 = Debug|Win32 22 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Release|x64.ActiveCfg = Release|x64 23 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Release|x64.Build.0 = Release|x64 24 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Release|x86.ActiveCfg = Release|Win32 25 | {9863A521-E9DB-4775-A276-CADEF726CF11}.Release|x86.Build.0 = Release|Win32 26 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x64.ActiveCfg = Debug|x64 27 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x64.Build.0 = Debug|x64 28 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x86.ActiveCfg = Debug|Win32 29 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Debug|x86.Build.0 = Debug|Win32 30 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x64.ActiveCfg = Release|x64 31 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x64.Build.0 = Release|x64 32 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x86.ActiveCfg = Release|Win32 33 | {19BC4240-15ED-4C76-BC57-34BB70FE163B}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /tests/vsprojects/spp_alloc_test.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {19BC4240-15ED-4C76-BC57-34BB70FE163B} 34 | Win32Proj 35 | 8.1 36 | 37 | 38 | 39 | Application 40 | v140 41 | MultiByte 42 | 43 | 44 | Application 45 | MultiByte 46 | v140 47 | 48 | 49 | Application 50 | v140 51 | MultiByte 52 | 53 | 54 | Application 55 | v140 56 | MultiByte 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | <_ProjectFileVersion>14.0.23107.0 76 | 77 | 78 | None 79 | 80 | 81 | $(SolutionDir)$(Configuration)\ 82 | $(Configuration)\ 83 | true 84 | 85 | 86 | true 87 | 88 | 89 | $(SolutionDir)$(Configuration)\ 90 | $(Configuration)\ 91 | false 92 | 93 | 94 | false 95 | 96 | 97 | 98 | Disabled 99 | WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 100 | true 101 | EnableFastChecks 102 | MultiThreadedDebug 103 | 104 | Level3 105 | ProgramDatabase 106 | ../.. 107 | 108 | 109 | $(OutDir)spp_alloc_test.exe 110 | true 111 | $(OutDir)spp_alloc_test.pdb 112 | Console 113 | MachineX86 114 | 115 | 116 | 117 | 118 | Disabled 119 | WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 120 | EnableFastChecks 121 | MultiThreadedDebug 122 | 123 | 124 | Level3 125 | ProgramDatabase 126 | ../.. 127 | 128 | 129 | $(OutDir)spp_alloc_test.exe 130 | true 131 | $(OutDir)spp_alloc_test.pdb 132 | Console 133 | 134 | 135 | 136 | 137 | WIN32;NDEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 138 | MultiThreaded 139 | 140 | Level3 141 | ProgramDatabase 142 | ../.. 143 | 144 | 145 | $(OutDir)spp_alloc_test.exe 146 | true 147 | Console 148 | true 149 | true 150 | MachineX86 151 | true 152 | 153 | 154 | 155 | 156 | WIN32;NDEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 157 | MultiThreaded 158 | 159 | 160 | Level3 161 | ProgramDatabase 162 | ../.. 163 | 164 | 165 | $(OutDir)spp_alloc_test.exe 166 | true 167 | Console 168 | true 169 | true 170 | true 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /tests/vsprojects/spp_alloc_test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {c644622a-f598-4fcf-861c-199b4b988881} 6 | 7 | 8 | 9 | 10 | Header Files 11 | 12 | 13 | Header Files 14 | 15 | 16 | Header Files 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/vsprojects/spp_test.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {9863A521-E9DB-4775-A276-CADEF726CF11} 34 | Win32Proj 35 | 8.1 36 | 37 | 38 | 39 | Application 40 | v140 41 | MultiByte 42 | 43 | 44 | Application 45 | MultiByte 46 | v140 47 | 48 | 49 | Application 50 | v140 51 | MultiByte 52 | 53 | 54 | Application 55 | v140 56 | MultiByte 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | <_ProjectFileVersion>14.0.23107.0 76 | 77 | 78 | None 79 | 80 | 81 | $(SolutionDir)$(Configuration)\ 82 | $(Configuration)\ 83 | true 84 | 85 | 86 | true 87 | AllRules.ruleset 88 | 89 | 90 | $(SolutionDir)$(Configuration)\ 91 | $(Configuration)\ 92 | false 93 | 94 | 95 | false 96 | 97 | 98 | 99 | Disabled 100 | WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 101 | true 102 | EnableFastChecks 103 | MultiThreadedDebug 104 | 105 | Level3 106 | ProgramDatabase 107 | ../.. 108 | 109 | 110 | $(OutDir)spp_test.exe 111 | true 112 | $(OutDir)spp_test.pdb 113 | Console 114 | MachineX86 115 | 116 | 117 | 118 | 119 | Disabled 120 | WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 121 | EnableFastChecks 122 | MultiThreadedDebug 123 | 124 | 125 | EnableAllWarnings 126 | ProgramDatabase 127 | ../.. 128 | 129 | 130 | $(OutDir)spp_test.exe 131 | true 132 | $(OutDir)spp_test.pdb 133 | Console 134 | 135 | 136 | 137 | 138 | WIN32;NDEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 139 | MultiThreaded 140 | 141 | Level3 142 | ProgramDatabase 143 | ../.. 144 | 145 | 146 | $(OutDir)spp_test.exe 147 | true 148 | Console 149 | true 150 | true 151 | MachineX86 152 | 153 | 154 | 155 | 156 | WIN32;NDEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 157 | MultiThreaded 158 | 159 | 160 | Level3 161 | ProgramDatabase 162 | ../.. 163 | 164 | 165 | $(OutDir)spp_test.exe 166 | true 167 | Console 168 | true 169 | true 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /tests/vsprojects/spp_test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 6 | h;hpp;hxx;hm;inl;inc;xsd 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Header Files 15 | 16 | 17 | Header Files 18 | 19 | 20 | Header Files 21 | 22 | 23 | Header Files 24 | 25 | 26 | Header Files 27 | 28 | 29 | Header Files 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/vsprojects/spp_test.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------