├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── linux.yml │ ├── macos.yml │ └── windows.yml ├── .gitignore ├── CITATION.cff ├── CMakeLists.txt ├── LICENSE ├── README.md ├── benchmark ├── BENCHMARK.md ├── Makefile ├── bench.cc ├── bench.py ├── charts-template.html ├── js │ ├── examples.css │ ├── jquery.canvaswrapper.js │ ├── jquery.colorhelpers.js │ ├── jquery.flot.axislabels.js │ ├── jquery.flot.browser.js │ ├── jquery.flot.categories.js │ ├── jquery.flot.composeImages.js │ ├── jquery.flot.crosshair.js │ ├── jquery.flot.drawSeries.js │ ├── jquery.flot.errorbars.js │ ├── jquery.flot.fillbetween.js │ ├── jquery.flot.flatdata.js │ ├── jquery.flot.hover.js │ ├── jquery.flot.image.js │ ├── jquery.flot.js │ ├── jquery.flot.legend.js │ ├── jquery.flot.logaxis.js │ ├── jquery.flot.navigate.js │ ├── jquery.flot.pie.js │ ├── jquery.flot.resize.js │ ├── jquery.flot.saturated.js │ ├── jquery.flot.selection.js │ ├── jquery.flot.stack.js │ ├── jquery.flot.symbol.js │ ├── jquery.flot.threshold.js │ ├── jquery.flot.time.js │ ├── jquery.flot.touch.js │ ├── jquery.flot.touchNavigate.js │ ├── jquery.flot.uiConstants.js │ ├── jquery.js │ └── jquery.mousewheel.js ├── make_chart_data.py ├── martinus │ └── Vagrantfile └── results │ ├── output_flat_par │ ├── output_flat_par_mutex_4 │ ├── output_flat_par_mutex_5 │ ├── output_mt_par_only │ ├── output_mt_stl_flat_par │ ├── output_mt_stl_flat_par_run2 │ ├── output_stl_flat │ ├── output_stl_flat_par │ └── output_various_N ├── cmake ├── CMakeLists.txt.in ├── DetectVersion.cmake ├── DownloadGTest.cmake ├── helpers.cmake └── phmap.cmake ├── css ├── bootstrap-responsive.min.css ├── bootstrap.min.css ├── colors.css └── style.css ├── doc └── new_release.md ├── examples ├── allmaps.cc ├── basic.cc ├── bench.cc ├── btree.cc ├── btree_fwd.h ├── custom_pointer.cc ├── dump_load.cc ├── dump_nested.cc ├── emplace.cc ├── f1.cc ├── f2.cc ├── hash.cc ├── hash_bench.cc ├── hash_std.cc ├── hash_std.h ├── hash_value.cc ├── hash_value.h ├── insert_bench.cc ├── knucleotide-input.txt ├── knucleotide-input0.txt ├── knucleotide.cc ├── lazy_emplace_l.cc ├── llil.cc ├── llil4map.cc ├── llil_utils │ ├── gen-llil.pl │ ├── gen_files │ ├── run_llil4map │ └── shuffle.pl ├── matt.cc ├── mt_word_counter.cc ├── p_bench.cc ├── pmr.cc └── serialize.cc ├── html ├── Makefile ├── bench_results │ └── martinus_mod │ │ ├── InsertManyInt.html │ │ ├── Lookup.html │ │ └── index2.html ├── css │ ├── bootstrap-responsive.min.css │ ├── bootstrap.min.css │ ├── colors.css │ └── style.css ├── diagrams │ ├── closed_hashing │ ├── closed_hashing.svg │ ├── index_computation │ └── index_computation.svg ├── img │ ├── closed_hashing.png │ ├── flat_mem_usage.gif │ ├── flat_mem_usage.png │ ├── flat_par_mutex_4.PNG │ ├── flat_par_mutex_5.PNG │ ├── flat_par_mutex_5_speed.PNG │ ├── flat_par_mutex_6_speed.PNG │ ├── flat_par_speed.PNG │ ├── flat_peak.gif │ ├── flat_peak.png │ ├── hashtable_benchmarks.PNG │ ├── idx_computation_cost.PNG │ ├── index_computation.png │ ├── lock_various_sizes.PNG │ ├── mt_stl_flat_par_both.PNG │ ├── mt_stl_flat_par_both_run2.PNG │ ├── mt_stl_flat_par_mem.PNG │ ├── mt_stl_flat_par_mem_run2.PNG │ ├── mt_stl_flat_par_mem_run2_zoomed.PNG │ ├── mt_stl_flat_par_mem_zoomed.PNG │ ├── mt_stl_flat_par_speed.PNG │ ├── mt_stl_flat_par_speed_run2.PNG │ ├── no_preselection.PNG │ ├── node_mem_usage.gif │ ├── node_mem_usage.png │ ├── node_peak.gif │ ├── node_peak.png │ ├── par_align_test.png │ ├── par_mt_memory.PNG │ ├── par_mt_speed.PNG │ ├── parallel_flat_peak.gif │ ├── parallel_flat_peak.png │ ├── parallel_node_peak.gif │ ├── parallel_node_peak.png │ ├── phash.png │ ├── phmap_logo.png │ ├── spp_flat_par_both.png │ ├── stl_flat_both.PNG │ ├── stl_flat_mem.PNG │ ├── stl_flat_par_both.PNG │ ├── stl_flat_par_mem.PNG │ ├── stl_flat_par_mem_zoomed.PNG │ ├── stl_flat_par_speed.PNG │ └── stl_flat_speed.PNG ├── includes.hs ├── latex_macros ├── parallel_hashmap.html ├── parallel_hashmap.md ├── parallel_hashmap.pdf ├── template.html └── template.latex ├── index.html ├── parallel_hashmap ├── btree.h ├── meminfo.h ├── phmap.h ├── phmap_base.h ├── phmap_bits.h ├── phmap_config.h ├── phmap_dump.h ├── phmap_fwd_decl.h └── phmap_utils.h ├── phmap.natvis ├── phmap_gdb.py ├── phmap_lldb.py └── tests ├── btree_test.cc ├── btree_test.h ├── container_memory_test.cc ├── dump_load_test.cc ├── erase_if_test.cc ├── flat_hash_map_test.cc ├── flat_hash_set_test.cc ├── hash_generator_testing.h ├── hash_policy_testing.h ├── hash_policy_testing_test.cc ├── hashtable_debug.h ├── node_hash_map_test.cc ├── node_hash_policy_test.cc ├── node_hash_set_test.cc ├── parallel_flat_hash_map_mutex_test.cc ├── parallel_flat_hash_map_test.cc ├── parallel_flat_hash_set_test.cc ├── parallel_hash_map_test.cc ├── parallel_hash_set_test.cc ├── parallel_node_hash_map_test.cc ├── parallel_node_hash_set_test.cc ├── raw_hash_set_allocator_test.cc ├── raw_hash_set_test.cc ├── test_instance_tracker.h ├── tracked.h ├── unordered_map_constructor_test.h ├── unordered_map_lookup_test.h ├── unordered_map_members_test.h ├── unordered_map_modifiers_test.h ├── unordered_set_constructor_test.h ├── unordered_set_lookup_test.h ├── unordered_set_members_test.h └── unordered_set_modifiers_test.h /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.cc linguist-vendored=false 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: greg7mdp 2 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | build: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest] 14 | compiler: [g++, clang++] 15 | flags: [-std=c++11, -std=c++17] 16 | optimize: [-O2] 17 | 18 | env: 19 | CXX: ${{ matrix.compiler }} 20 | CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }} 21 | CTEST_OUTPUT_ON_FAILURE: 1 22 | CTEST_NO_TESTS_ACTION: error 23 | CTEST_PARALLEL_LEVEL: 0 24 | CMAKE_BUILD_PARALLEL_LEVEL: 4 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: CMake configure 31 | run: cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -B build 32 | 33 | - name: CMake build 34 | run: cmake --build build 35 | 36 | - name: CMake test 37 | run: ctest --test-dir build 38 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: MacOS 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | build: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [macos-latest] 14 | compiler: [g++, clang++] 15 | flags: [-std=c++11, -std=c++17] 16 | optimize: [-O2] 17 | 18 | env: 19 | CXX: ${{ matrix.compiler }} 20 | CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }} 21 | CTEST_OUTPUT_ON_FAILURE: 1 22 | CTEST_NO_TESTS_ACTION: error 23 | CTEST_PARALLEL_LEVEL: 0 24 | CMAKE_BUILD_PARALLEL_LEVEL: 4 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: CMake configure 31 | run: cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -B build 32 | 33 | - name: CMake build 34 | run: cmake --build build 35 | 36 | - name: CMake test 37 | run: ctest --test-dir build 38 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | build: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [windows-latest] 14 | flags: ["/std:c++11", "/std:c++latest"] 15 | optimize: [/O2] 16 | 17 | env: 18 | CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }} 19 | CTEST_OUTPUT_ON_FAILURE: 1 20 | CTEST_NO_TESTS_ACTION: error 21 | CTEST_PARALLEL_LEVEL: 0 22 | CMAKE_BUILD_PARALLEL_LEVEL: 4 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | 28 | - name: CMake configure 29 | run: cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -B build 30 | 31 | - name: CMake build 32 | run: cmake --build build --config Release 33 | 34 | - name: CMake test 35 | run: ctest --test-dir build -C Release 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | VagrantFile 2 | benchmark/build 3 | benchmark/output 4 | benchmark/charts.html 5 | examples/llil_utils/tmp 6 | build* 7 | .vagrant 8 | .cache 9 | .gdb_history 10 | compile_commands.json 11 | .emacs.desktop* 12 | **/.vscode 13 | TAGS 14 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: Popovitch 5 | given-names: Gregory 6 | orcid: https://orcid.org/0009-0005-7245-5930 7 | title: "The Parallel Hashmap C++ library" 8 | license: Apache-2.0 9 | version: 2.0.0 10 | date-released: 2025-01-21 11 | -------------------------------------------------------------------------------- /benchmark/BENCHMARK.md: -------------------------------------------------------------------------------- 1 | # parallel-hashmap 2 | 3 | How to run these benchmarks 4 | =========================== 5 | 6 | These bencharks were run on windows using Visual Studio 2017, in a cygwin window with the VC++ 2017 compiler env vars (add something like this in your Cygwin.bat: 7 | 8 | CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" 9 | 10 | Running them on linux would require Makefile changes. 11 | 12 | To build and run the tests, just update the path to the abseil libraries in the makefile, and run make. 13 | 14 | Your charts are now in charts.html. 15 | 16 | 17 | -------------------------------------------------------------------------------- /benchmark/Makefile: -------------------------------------------------------------------------------- 1 | CXX=CL -EHsc -DNDEBUG -Fo$@ -O2 2 | #CXX=CL -EHsc -g -debug -Zi -Fo$@ 3 | #CXX=g++ -ggdb -O2 -lm -std=c++11 -DNDEBUG 4 | 5 | ABSEIL_LIBS=absl_bad_optional_access.lib absl_bad_variant_access.lib absl_base.lib absl_demangle_internal.lib absl_hash.lib absl_int128.lib absl_internal_bad_any_cast_impl.lib absl_internal_city.lib absl_internal_civil_time.lib absl_internal_debugging_internal.lib absl_internal_graphcycles_internal.lib absl_internal_hashtablez_sampler.lib absl_internal_malloc_internal.lib absl_internal_spinlock_wait.lib absl_internal_strings_internal.lib absl_internal_throw_delegate.lib absl_internal_time_zone.lib absl_optional.lib absl_raw_hash_set.lib absl_stacktrace.lib absl_strings.lib absl_symbolize.lib absl_synchronization.lib absl_time.lib 6 | 7 | PROGS = stl_unordered_map sparsepp phmap abseil_flat abseil_parallel_flat phmap_flat 8 | BUILD_PROGS = $(addprefix build/,$(PROGS)) 9 | SIZE = 100000000 10 | ABSEIL = ../../abseil-cpp 11 | PHMAP_SRC = ../parallel_hashmap 12 | 13 | all: test 14 | 15 | builddir: 16 | @if [ ! -d build ]; then mkdir build; fi 17 | 18 | build/stl_unordered_map: bench.cc Makefile 19 | $(CXX) -DSTL_UNORDERED -I.. bench.cc -o $@ 20 | 21 | build/sparsepp: bench.cc Makefile 22 | $(CXX) -DSPARSEPP -I.. -I../../sparsepp bench.cc -o $@ 23 | 24 | build/phmap: bench.cc Makefile $(PHMAP_SRC)/phmap.h 25 | $(CXX) -DPHMAP -I.. -I$(ABSEIL) bench.cc /MD -o $@ /link /LIBPATH:$(ABSEIL)/build/lib ${ABSEIL_LIBS} 26 | 27 | build/phmap_flat: bench.cc Makefile $(PHMAP_SRC)/phmap.h 28 | $(CXX) -DPHMAP_FLAT -I.. bench.cc /MD -o $@ 29 | 30 | build/abseil_flat: bench.cc Makefile 31 | $(CXX) -DABSEIL_FLAT -I.. -I$(ABSEIL) bench.cc /MD -o $@ /link /LIBPATH:$(ABSEIL)/build/lib ${ABSEIL_LIBS} 32 | 33 | build/abseil_parallel_flat: bench.cc Makefile 34 | $(CXX) -DABSEIL_PARALLEL_FLAT -I.. -I $(ABSEIL) bench.cc /MD -o $@ /link /LIBPATH:$(ABSEIL)/build/lib ${ABSEIL_LIBS} 35 | 36 | build/emplace: emplace.cc Makefile $(PHMAP_SRC)/phmap.h 37 | $(CXX) -DABSEIL_FLAT -I.. -I$(ABSEIL) emplace.cc /MD -o $@ /link /LIBPATH:$(ABSEIL)/build/lib ${ABSEIL_LIBS} 38 | 39 | progs: $(BUILD_PROGS) 40 | 41 | test: builddir progs 42 | -rm -f output 43 | #./build/stl_unordered_map $(SIZE) random >> output 44 | #./build/sparsepp $(SIZE) random >> output 45 | ./build/abseil_flat $(SIZE) random >> output 46 | #./build/phmap_flat $(SIZE) random >> output 47 | ./build/phmap $(SIZE) random >> output 48 | ./build/abseil_parallel_flat $(SIZE) random >> output 49 | python make_chart_data.py < output 50 | 51 | test_cust: 52 | -rm -f output 53 | #./build/abseil_flat $(SIZE) random >> output 54 | #./build/sparsepp $(SIZE) random >> output 55 | ./build/abseil_parallel_flat_5 $(SIZE) random >> output 56 | ./build/abseil_parallel_flat $(SIZE) random >> output 57 | python make_chart_data.py < output 58 | 59 | chart: 60 | python make_chart_data.py < output 61 | 62 | clean: 63 | -rm -fr output build 64 | -------------------------------------------------------------------------------- /benchmark/bench.py: -------------------------------------------------------------------------------- 1 | import sys, os, subprocess, signal 2 | 3 | programs = [ 4 | 'stl_unordered_map', 5 | 'abseil_flat', 6 | 'abseil_parallel_flat' 7 | ] 8 | 9 | test_size = 1 10 | minkeys = 0 11 | maxkeys = 1000 12 | interval = 100 13 | best_out_of = 1 14 | 15 | if test_size == 2: 16 | multiplier = 1000 * 1000 17 | maxkeys = 500 18 | best_out_of = 3 19 | 20 | elif test_size == 1: 21 | multiplier = 100 * 1000 22 | interval = 200 23 | else: 24 | multiplier = 10 * 1000 25 | 26 | # and use nice/ionice 27 | # and shut down to the console 28 | # and swapoff any swap files/partitions 29 | 30 | outfile = open('output', 'w') 31 | 32 | if len(sys.argv) > 1: 33 | benchtypes = sys.argv[1:] 34 | else: 35 | benchtypes = ( 'random', 'lookup', 'delete',) 36 | #benchtypes = ( 'lookup', ) 37 | 38 | for benchtype in benchtypes: 39 | nkeys = minkeys * multiplier 40 | while nkeys <= (maxkeys * multiplier): 41 | for program in programs: 42 | fastest_attempt = 1000000 43 | fastest_attempt_data = '' 44 | 45 | for attempt in range(best_out_of): 46 | proc = subprocess.Popen(['./build/'+program, str(nkeys), benchtype], stdout=subprocess.PIPE) 47 | 48 | # wait for the program to fill up memory and spit out its "ready" message 49 | try: 50 | runtime = float(proc.stdout.readline().strip()) 51 | except: 52 | runtime = 0 53 | 54 | ps_proc = subprocess.Popen(['ps up %d | tail -n1' % proc.pid], shell=True, stdout=subprocess.PIPE) 55 | #nbytes = int(ps_proc.stdout.read().split()[4]) * 1024 56 | #ps_proc.wait() 57 | nbytes = 1000000 58 | 59 | os.kill(proc.pid, signal.SIGKILL) 60 | proc.wait() 61 | 62 | if nbytes and runtime: # otherwise it crashed 63 | line = ','.join(map(str, [benchtype, nkeys, program, nbytes, "%0.6f" % runtime])) 64 | 65 | if runtime < fastest_attempt: 66 | fastest_attempt = runtime 67 | fastest_attempt_data = line 68 | 69 | if fastest_attempt != 1000000: 70 | print >> outfile, fastest_attempt_data 71 | print fastest_attempt_data 72 | 73 | nkeys += interval * multiplier 74 | -------------------------------------------------------------------------------- /benchmark/charts-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hash Table Benchmarks 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 94 | 95 | 96 | 97 | 131 | 132 | __PLOT_DIV_SPEC_GOES_HERE__ 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /benchmark/js/examples.css: -------------------------------------------------------------------------------- 1 | * { padding: 0; margin: 0; vertical-align: top; } 2 | 3 | body { 4 | background: url(background.png) repeat-x; 5 | font: 18px "proxima-nova", Helvetica, Arial, sans-serif; 6 | line-height: 1.5; 7 | } 8 | 9 | a { color: #069; } 10 | a:hover { color: #28b; } 11 | 12 | h2 { 13 | margin-top: 15px; 14 | font: normal 32px "omnes-pro", Helvetica, Arial, sans-serif; 15 | } 16 | 17 | h3 { 18 | margin-left: 30px; 19 | font: normal 26px "omnes-pro", Helvetica, Arial, sans-serif; 20 | color: #666; 21 | } 22 | 23 | p { 24 | margin-top: 10px; 25 | } 26 | 27 | button { 28 | font-size: 18px; 29 | padding: 1px 7px; 30 | } 31 | 32 | input { 33 | font-size: 18px; 34 | } 35 | 36 | input[type=checkbox] { 37 | margin: 7px; 38 | } 39 | 40 | #header { 41 | position: relative; 42 | width: 900px; 43 | margin: auto; 44 | } 45 | 46 | #header h2 { 47 | margin-left: 10px; 48 | vertical-align: middle; 49 | font-size: 42px; 50 | font-weight: bold; 51 | text-decoration: none; 52 | color: #000; 53 | } 54 | 55 | #content { 56 | width: 880px; 57 | margin: 0 auto; 58 | padding: 10px; 59 | } 60 | 61 | #footer { 62 | margin-top: 25px; 63 | margin-bottom: 10px; 64 | text-align: center; 65 | font-size: 12px; 66 | color: #999; 67 | } 68 | 69 | .demo-container { 70 | box-sizing: border-box; 71 | width: 850px; 72 | height: 450px; 73 | padding: 20px 15px 15px 15px; 74 | margin: 15px auto 30px auto; 75 | border: 1px solid #ddd; 76 | background: #fff; 77 | background: linear-gradient(#f6f6f6 0, #fff 50px); 78 | background: -o-linear-gradient(#f6f6f6 0, #fff 50px); 79 | background: -ms-linear-gradient(#f6f6f6 0, #fff 50px); 80 | background: -moz-linear-gradient(#f6f6f6 0, #fff 50px); 81 | background: -webkit-linear-gradient(#f6f6f6 0, #fff 50px); 82 | box-shadow: 0 3px 10px rgba(0,0,0,0.15); 83 | -o-box-shadow: 0 3px 10px rgba(0,0,0,0.1); 84 | -ms-box-shadow: 0 3px 10px rgba(0,0,0,0.1); 85 | -moz-box-shadow: 0 3px 10px rgba(0,0,0,0.1); 86 | -webkit-box-shadow: 0 3px 10px rgba(0,0,0,0.1); 87 | -webkit-tap-highlight-color: rgba(0,0,0,0); 88 | -webkit-tap-highlight-color: transparent; 89 | -webkit-touch-callout: none; 90 | -webkit-user-select: none; 91 | -khtml-user-select: none; 92 | -moz-user-select: none; 93 | -ms-user-select: none; 94 | user-select: none; 95 | } 96 | 97 | .demo-placeholder { 98 | width: 100%; 99 | height: 100%; 100 | font-size: 14px; 101 | } 102 | 103 | fieldset { 104 | display: block; 105 | -webkit-margin-start: 2px; 106 | -webkit-margin-end: 2px; 107 | -webkit-padding-before: 0.35em; 108 | -webkit-padding-start: 0.75em; 109 | -webkit-padding-end: 0.75em; 110 | -webkit-padding-after: 0.625em; 111 | min-width: -webkit-min-content; 112 | border-width: 2px; 113 | border-style: groove; 114 | border-color: threedface; 115 | border-image: initial; 116 | padding: 10px; 117 | } 118 | 119 | .legend { 120 | display: block; 121 | -webkit-padding-start: 2px; 122 | -webkit-padding-end: 2px; 123 | border-width: initial; 124 | border-style: none; 125 | border-color: initial; 126 | border-image: initial; 127 | padding-left: 10px; 128 | padding-right: 10px; 129 | padding-top: 10px; 130 | padding-bottom: 10px; 131 | } 132 | 133 | .legendLayer .background { 134 | fill: rgba(255, 255, 255, 0.85); 135 | stroke: rgba(0, 0, 0, 0.85); 136 | stroke-width: 1; 137 | } 138 | 139 | input[type="radio"] { 140 | margin-top: -1px; 141 | vertical-align: middle; 142 | } 143 | 144 | .tickLabel { 145 | line-height: 1.1; 146 | } 147 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.browser.js: -------------------------------------------------------------------------------- 1 | /** ## jquery.flot.browser.js 2 | 3 | This plugin is used to make available some browser-related utility functions. 4 | 5 | ### Methods 6 | */ 7 | 8 | (function ($) { 9 | 'use strict'; 10 | 11 | var browser = { 12 | /** 13 | - getPageXY(e) 14 | 15 | Calculates the pageX and pageY using the screenX, screenY properties of the event 16 | and the scrolling of the page. This is needed because the pageX and pageY 17 | properties of the event are not correct while running tests in Edge. */ 18 | getPageXY: function (e) { 19 | // This code is inspired from https://stackoverflow.com/a/3464890 20 | var doc = document.documentElement, 21 | pageX = e.clientX + (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), 22 | pageY = e.clientY + (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); 23 | return { X: pageX, Y: pageY }; 24 | }, 25 | 26 | /** 27 | - getPixelRatio(context) 28 | 29 | This function returns the current pixel ratio defined by the product of desktop 30 | zoom and page zoom. 31 | Additional info: https://www.html5rocks.com/en/tutorials/canvas/hidpi/ 32 | */ 33 | getPixelRatio: function(context) { 34 | var devicePixelRatio = window.devicePixelRatio || 1, 35 | backingStoreRatio = 36 | context.webkitBackingStorePixelRatio || 37 | context.mozBackingStorePixelRatio || 38 | context.msBackingStorePixelRatio || 39 | context.oBackingStorePixelRatio || 40 | context.backingStorePixelRatio || 1; 41 | return devicePixelRatio / backingStoreRatio; 42 | }, 43 | 44 | /** 45 | - isSafari, isMobileSafari, isOpera, isFirefox, isIE, isEdge, isChrome, isBlink 46 | 47 | This is a collection of functions, used to check if the code is running in a 48 | particular browser or Javascript engine. 49 | */ 50 | isSafari: function() { 51 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 52 | // Safari 3.0+ "[object HTMLElementConstructor]" 53 | return /constructor/i.test(window.top.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window.top['safari'] || (typeof window.top.safari !== 'undefined' && window.top.safari.pushNotification)); 54 | }, 55 | 56 | isMobileSafari: function() { 57 | //isMobileSafari adapted from https://stackoverflow.com/questions/3007480/determine-if-user-navigated-from-mobile-safari 58 | return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/); 59 | }, 60 | 61 | isOpera: function() { 62 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 63 | //Opera 8.0+ 64 | return (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; 65 | }, 66 | 67 | isFirefox: function() { 68 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 69 | // Firefox 1.0+ 70 | return typeof InstallTrigger !== 'undefined'; 71 | }, 72 | 73 | isIE: function() { 74 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 75 | // Internet Explorer 6-11 76 | return /*@cc_on!@*/false || !!document.documentMode; 77 | }, 78 | 79 | isEdge: function() { 80 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 81 | // Edge 20+ 82 | return !browser.isIE() && !!window.StyleMedia; 83 | }, 84 | 85 | isChrome: function() { 86 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 87 | // Chrome 1+ 88 | return !!window.chrome && !!window.chrome.webstore; 89 | }, 90 | 91 | isBlink: function() { 92 | // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser 93 | return (browser.isChrome() || browser.isOpera()) && !!window.CSS; 94 | } 95 | }; 96 | 97 | $.plot.browser = browser; 98 | })(jQuery); 99 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.flatdata.js: -------------------------------------------------------------------------------- 1 | /* Support for flat 1D data series. 2 | 3 | A 1D flat data series is a data series in the form of a regular 1D array. The 4 | main reason for using a flat data series is that it performs better, consumes 5 | less memory and generates less garbage collection than the regular flot format. 6 | 7 | Example: 8 | 9 | plot.setData([[[0,0], [1,1], [2,2], [3,3]]]); // regular flot format 10 | plot.setData([{flatdata: true, data: [0, 1, 2, 3]}]); // flatdata format 11 | 12 | Set series.flatdata to true to enable this plugin. 13 | 14 | You can use series.start to specify the starting index of the series (default is 0) 15 | You can use series.step to specify the interval between consecutive indexes of the series (default is 1) 16 | */ 17 | 18 | /* global jQuery*/ 19 | 20 | (function ($) { 21 | 'use strict'; 22 | 23 | function process1DRawData(plot, series, data, datapoints) { 24 | if (series.flatdata === true) { 25 | var start = series.start || 0; 26 | var step = typeof series.step === 'number' ? series.step : 1; 27 | datapoints.pointsize = 2; 28 | for (var i = 0, j = 0; i < data.length; i++, j += 2) { 29 | datapoints.points[j] = start + (i * step); 30 | datapoints.points[j + 1] = data[i]; 31 | } 32 | if (datapoints.points !== undefined) { 33 | datapoints.points.length = data.length * 2; 34 | } else { 35 | datapoints.points = []; 36 | } 37 | } 38 | } 39 | 40 | $.plot.plugins.push({ 41 | init: function(plot) { 42 | plot.hooks.processRawData.push(process1DRawData); 43 | }, 44 | name: 'flatdata', 45 | version: '0.0.2' 46 | }); 47 | })(jQuery); 48 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.resize.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* Flot plugin for automatically redrawing plots as the placeholder resizes. 3 | 4 | Copyright (c) 2007-2014 IOLA and Ole Laursen. 5 | Licensed under the MIT license. 6 | 7 | It works by listening for changes on the placeholder div (through the jQuery 8 | resize event plugin) - if the size changes, it will redraw the plot. 9 | 10 | There are no options. If you need to disable the plugin for some plots, you 11 | can just fix the size of their placeholders. 12 | 13 | */ 14 | 15 | /* Inline dependency: 16 | * jQuery resize event - v1.1 - 3/14/2010 17 | * http://benalman.com/projects/jquery-resize-plugin/ 18 | * 19 | * Copyright (c) 2010 "Cowboy" Ben Alman 20 | * Dual licensed under the MIT and GPL licenses. 21 | * http://benalman.com/about/license/ 22 | */ 23 | (function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); 24 | 25 | /* eslint-enable */ 26 | (function ($) { 27 | var options = { }; // no options 28 | 29 | function init(plot) { 30 | function onResize() { 31 | var placeholder = plot.getPlaceholder(); 32 | 33 | // somebody might have hidden us and we can't plot 34 | // when we don't have the dimensions 35 | if (placeholder.width() === 0 || placeholder.height() === 0) return; 36 | 37 | plot.resize(); 38 | plot.setupGrid(); 39 | plot.draw(); 40 | } 41 | 42 | function bindEvents(plot, eventHolder) { 43 | plot.getPlaceholder().resize(onResize); 44 | } 45 | 46 | function shutdown(plot, eventHolder) { 47 | plot.getPlaceholder().unbind("resize", onResize); 48 | } 49 | 50 | plot.hooks.bindEvents.push(bindEvents); 51 | plot.hooks.shutdown.push(shutdown); 52 | } 53 | 54 | $.plot.plugins.push({ 55 | init: init, 56 | options: options, 57 | name: 'resize', 58 | version: '1.0' 59 | }); 60 | })(jQuery); 61 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.saturated.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 'use strict'; 3 | var saturated = { 4 | saturate: function (a) { 5 | if (a === Infinity) { 6 | return Number.MAX_VALUE; 7 | } 8 | 9 | if (a === -Infinity) { 10 | return -Number.MAX_VALUE; 11 | } 12 | 13 | return a; 14 | }, 15 | delta: function(min, max, noTicks) { 16 | return ((max - min) / noTicks) === Infinity ? (max / noTicks - min / noTicks) : (max - min) / noTicks 17 | }, 18 | multiply: function (a, b) { 19 | return saturated.saturate(a * b); 20 | }, 21 | // returns c * bInt * a. Beahves properly in the case where c is negative 22 | // and bInt * a is bigger that Number.MAX_VALUE (Infinity) 23 | multiplyAdd: function (a, bInt, c) { 24 | if (isFinite(a * bInt)) { 25 | return saturated.saturate(a * bInt + c); 26 | } else { 27 | var result = c; 28 | 29 | for (var i = 0; i < bInt; i++) { 30 | result += a; 31 | } 32 | 33 | return saturated.saturate(result); 34 | } 35 | }, 36 | // round to nearby lower multiple of base 37 | floorInBase: function(n, base) { 38 | return base * Math.floor(n / base); 39 | } 40 | }; 41 | 42 | $.plot.saturated = saturated; 43 | })(jQuery); 44 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.symbol.js: -------------------------------------------------------------------------------- 1 | /* Flot plugin that adds some extra symbols for plotting points. 2 | 3 | Copyright (c) 2007-2014 IOLA and Ole Laursen. 4 | Licensed under the MIT license. 5 | 6 | The symbols are accessed as strings through the standard symbol options: 7 | 8 | series: { 9 | points: { 10 | symbol: "square" // or "diamond", "triangle", "cross", "plus", "ellipse", "rectangle" 11 | } 12 | } 13 | 14 | */ 15 | 16 | (function ($) { 17 | // we normalize the area of each symbol so it is approximately the 18 | // same as a circle of the given radius 19 | 20 | var square = function (ctx, x, y, radius, shadow) { 21 | // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 22 | var size = radius * Math.sqrt(Math.PI) / 2; 23 | ctx.rect(x - size, y - size, size + size, size + size); 24 | }, 25 | rectangle = function (ctx, x, y, radius, shadow) { 26 | // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 27 | var size = radius * Math.sqrt(Math.PI) / 2; 28 | ctx.rect(x - size, y - size, size + size, size + size); 29 | }, 30 | diamond = function (ctx, x, y, radius, shadow) { 31 | // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) 32 | var size = radius * Math.sqrt(Math.PI / 2); 33 | ctx.moveTo(x - size, y); 34 | ctx.lineTo(x, y - size); 35 | ctx.lineTo(x + size, y); 36 | ctx.lineTo(x, y + size); 37 | ctx.lineTo(x - size, y); 38 | ctx.lineTo(x, y - size); 39 | }, 40 | triangle = function (ctx, x, y, radius, shadow) { 41 | // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) 42 | var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); 43 | var height = size * Math.sin(Math.PI / 3); 44 | ctx.moveTo(x - size / 2, y + height / 2); 45 | ctx.lineTo(x + size / 2, y + height / 2); 46 | if (!shadow) { 47 | ctx.lineTo(x, y - height / 2); 48 | ctx.lineTo(x - size / 2, y + height / 2); 49 | ctx.lineTo(x + size / 2, y + height / 2); 50 | } 51 | }, 52 | cross = function (ctx, x, y, radius, shadow) { 53 | // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 54 | var size = radius * Math.sqrt(Math.PI) / 2; 55 | ctx.moveTo(x - size, y - size); 56 | ctx.lineTo(x + size, y + size); 57 | ctx.moveTo(x - size, y + size); 58 | ctx.lineTo(x + size, y - size); 59 | }, 60 | ellipse = function(ctx, x, y, radius, shadow, fill) { 61 | if (!shadow) { 62 | ctx.moveTo(x + radius, y); 63 | ctx.arc(x, y, radius, 0, Math.PI * 2, false); 64 | } 65 | }, 66 | plus = function (ctx, x, y, radius, shadow) { 67 | var size = radius * Math.sqrt(Math.PI / 2); 68 | ctx.moveTo(x - size, y); 69 | ctx.lineTo(x + size, y); 70 | ctx.moveTo(x, y + size); 71 | ctx.lineTo(x, y - size); 72 | }, 73 | handlers = { 74 | square: square, 75 | rectangle: rectangle, 76 | diamond: diamond, 77 | triangle: triangle, 78 | cross: cross, 79 | ellipse: ellipse, 80 | plus: plus 81 | }; 82 | 83 | square.fill = true; 84 | rectangle.fill = true; 85 | diamond.fill = true; 86 | triangle.fill = true; 87 | ellipse.fill = true; 88 | 89 | function init(plot) { 90 | plot.drawSymbol = handlers; 91 | } 92 | 93 | $.plot.plugins.push({ 94 | init: init, 95 | name: 'symbols', 96 | version: '1.0' 97 | }); 98 | })(jQuery); 99 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.threshold.js: -------------------------------------------------------------------------------- 1 | /* Flot plugin for thresholding data. 2 | 3 | Copyright (c) 2007-2014 IOLA and Ole Laursen. 4 | Licensed under the MIT license. 5 | 6 | The plugin supports these options: 7 | 8 | series: { 9 | threshold: { 10 | below: number 11 | color: colorspec 12 | } 13 | } 14 | 15 | It can also be applied to a single series, like this: 16 | 17 | $.plot( $("#placeholder"), [{ 18 | data: [ ... ], 19 | threshold: { ... } 20 | }]) 21 | 22 | An array can be passed for multiple thresholding, like this: 23 | 24 | threshold: [{ 25 | below: number1 26 | color: color1 27 | },{ 28 | below: number2 29 | color: color2 30 | }] 31 | 32 | These multiple threshold objects can be passed in any order since they are 33 | sorted by the processing function. 34 | 35 | The data points below "below" are drawn with the specified color. This makes 36 | it easy to mark points below 0, e.g. for budget data. 37 | 38 | Internally, the plugin works by splitting the data into two series, above and 39 | below the threshold. The extra series below the threshold will have its label 40 | cleared and the special "originSeries" attribute set to the original series. 41 | You may need to check for this in hover events. 42 | 43 | */ 44 | 45 | (function ($) { 46 | var options = { 47 | series: { threshold: null } // or { below: number, color: color spec} 48 | }; 49 | 50 | function init(plot) { 51 | function thresholdData(plot, s, datapoints, below, color) { 52 | var ps = datapoints.pointsize, i, x, y, p, prevp, 53 | thresholded = $.extend({}, s); // note: shallow copy 54 | 55 | thresholded.datapoints = { points: [], pointsize: ps, format: datapoints.format }; 56 | thresholded.label = null; 57 | thresholded.color = color; 58 | thresholded.threshold = null; 59 | thresholded.originSeries = s; 60 | thresholded.data = []; 61 | 62 | var origpoints = datapoints.points, 63 | addCrossingPoints = s.lines.show; 64 | 65 | var threspoints = []; 66 | var newpoints = []; 67 | var m; 68 | 69 | for (i = 0; i < origpoints.length; i += ps) { 70 | x = origpoints[i]; 71 | y = origpoints[i + 1]; 72 | 73 | prevp = p; 74 | if (y < below) p = threspoints; 75 | else p = newpoints; 76 | 77 | if (addCrossingPoints && prevp !== p && 78 | x !== null && i > 0 && 79 | origpoints[i - ps] != null) { 80 | var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); 81 | prevp.push(interx); 82 | prevp.push(below); 83 | for (m = 2; m < ps; ++m) { 84 | prevp.push(origpoints[i + m]); 85 | } 86 | 87 | p.push(null); // start new segment 88 | p.push(null); 89 | for (m = 2; m < ps; ++m) { 90 | p.push(origpoints[i + m]); 91 | } 92 | 93 | p.push(interx); 94 | p.push(below); 95 | for (m = 2; m < ps; ++m) { 96 | p.push(origpoints[i + m]); 97 | } 98 | } 99 | 100 | p.push(x); 101 | p.push(y); 102 | for (m = 2; m < ps; ++m) { 103 | p.push(origpoints[i + m]); 104 | } 105 | } 106 | 107 | datapoints.points = newpoints; 108 | thresholded.datapoints.points = threspoints; 109 | 110 | if (thresholded.datapoints.points.length > 0) { 111 | var origIndex = $.inArray(s, plot.getData()); 112 | // Insert newly-generated series right after original one (to prevent it from becoming top-most) 113 | plot.getData().splice(origIndex + 1, 0, thresholded); 114 | } 115 | 116 | // FIXME: there are probably some edge cases left in bars 117 | } 118 | 119 | function processThresholds(plot, s, datapoints) { 120 | if (!s.threshold) return; 121 | if (s.threshold instanceof Array) { 122 | s.threshold.sort(function(a, b) { 123 | return a.below - b.below; 124 | }); 125 | 126 | $(s.threshold).each(function(i, th) { 127 | thresholdData(plot, s, datapoints, th.below, th.color); 128 | }); 129 | } else { 130 | thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); 131 | } 132 | } 133 | 134 | plot.hooks.processDatapoints.push(processThresholds); 135 | } 136 | 137 | $.plot.plugins.push({ 138 | init: init, 139 | options: options, 140 | name: 'threshold', 141 | version: '1.2' 142 | }); 143 | })(jQuery); 144 | -------------------------------------------------------------------------------- /benchmark/js/jquery.flot.uiConstants.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 'use strict'; 3 | $.plot.uiConstants = { 4 | SNAPPING_CONSTANT: 20, 5 | PANHINT_LENGTH_CONSTANT: 10, 6 | MINOR_TICKS_COUNT_CONSTANT: 4, 7 | TICK_LENGTH_CONSTANT: 10, 8 | ZOOM_DISTANCE_MARGIN: 25 9 | }; 10 | })(jQuery); 11 | -------------------------------------------------------------------------------- /benchmark/js/jquery.mousewheel.js: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/jquery/jquery-mousewheel/blob/a06ef4e1a127795606642c55e22d4f2945edc061/jquery.mousewheel.js 2 | 3 | /*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net) 4 | * Licensed under the MIT License (LICENSE.txt). 5 | * 6 | * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. 7 | * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. 8 | * Thanks to: Seamus Leahy for adding deltaX and deltaY 9 | * 10 | * Version: 3.0.6 11 | * 12 | * Requires: 1.2.2+ 13 | */ 14 | 15 | (function($) { 16 | 17 | var types = ['DOMMouseScroll', 'mousewheel']; 18 | 19 | if ($.event.fixHooks) { 20 | for ( var i=types.length; i; ) { 21 | $.event.fixHooks[ types[--i] ] = $.event.mouseHooks; 22 | } 23 | } 24 | 25 | $.event.special.mousewheel = { 26 | setup: function() { 27 | if ( this.addEventListener ) { 28 | for ( var i=types.length; i; ) { 29 | this.addEventListener( types[--i], handler, false ); 30 | } 31 | } else { 32 | this.onmousewheel = handler; 33 | } 34 | }, 35 | 36 | teardown: function() { 37 | if ( this.removeEventListener ) { 38 | for ( var i=types.length; i; ) { 39 | this.removeEventListener( types[--i], handler, false ); 40 | } 41 | } else { 42 | this.onmousewheel = null; 43 | } 44 | } 45 | }; 46 | 47 | $.fn.extend({ 48 | mousewheel: function(fn) { 49 | return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); 50 | }, 51 | 52 | unmousewheel: function(fn) { 53 | return this.unbind("mousewheel", fn); 54 | } 55 | }); 56 | 57 | 58 | function handler(event) { 59 | var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0; 60 | event = $.event.fix(orgEvent); 61 | event.type = "mousewheel"; 62 | 63 | // Old school scrollwheel delta 64 | if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta/120; } 65 | if ( orgEvent.detail ) { delta = -orgEvent.detail/3; } 66 | 67 | // New school multidimensional scroll (touchpads) deltas 68 | deltaY = delta; 69 | 70 | // Gecko 71 | if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) { 72 | deltaY = 0; 73 | deltaX = -1*delta; 74 | } 75 | 76 | // Webkit 77 | if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; } 78 | if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; } 79 | 80 | // Add event and delta to the front of the arguments 81 | args.unshift(event, delta, deltaX, deltaY); 82 | 83 | return ($.event.dispatch || $.event.handle).apply(this, args); 84 | } 85 | 86 | })(jQuery); 87 | -------------------------------------------------------------------------------- /benchmark/martinus/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "ubuntu/xenial64" 16 | # sudo apt-get update 17 | # sudo apt-get install -y build-essential 18 | # sudo apt-get install -y cmake 19 | # sudo apt-get install pkg-config zip g++ zlib1g-dev unzip python 20 | # sudo apt-get install -y libboost1.55-dev 21 | # wget https://github.com/bazelbuild/bazel/releases/download/0.22.0/bazel-0.22.0-installer-linux-x86_64.sh 22 | # chmod +x ./bazel-0.22.0-installer-linux-x86_64.sh 23 | # ./bazel-0.22.0-installer-linux-x86_64.sh 24 | # ./bazel-bin/hashtable_benchmarks --benchmark_format=json > benchmark-results.json 25 | 26 | 27 | # Disable automatic box update checking. If you disable this, then 28 | # boxes will only be checked for updates when the user runs 29 | # `vagrant box outdated`. This is not recommended. 30 | # config.vm.box_check_update = false 31 | 32 | # Create a forwarded port mapping which allows access to a specific port 33 | # within the machine from a port on the host machine. In the example below, 34 | # accessing "localhost:8080" will access port 80 on the guest machine. 35 | # config.vm.network "forwarded_port", guest: 80, host: 8080 36 | 37 | # Create a private network, which allows host-only access to the machine 38 | # using a specific IP. 39 | # config.vm.network "private_network", ip: "192.168.33.10" 40 | 41 | # Create a public network, which generally matched to bridged network. 42 | # Bridged networks make the machine appear as another physical device on 43 | # your network. 44 | # config.vm.network "public_network" 45 | 46 | # Share an additional folder to the guest VM. The first argument is 47 | # the path on the host to the actual folder. The second argument is 48 | # the path on the guest to mount the folder. And the optional third 49 | # argument is a set of non-required options. 50 | # config.vm.synced_folder "../data", "/vagrant_data" 51 | 52 | # Provider-specific configuration so you can fine-tune various 53 | # backing providers for Vagrant. These expose provider-specific options. 54 | # Example for VirtualBox: 55 | # 56 | config.vm.provider "virtualbox" do |vb| 57 | vb.customize ["modifyvm", :id, "--cableconnected1", "on"] 58 | 59 | # Display the VirtualBox GUI when booting the machine 60 | vb.gui = false 61 | 62 | # Customize the amount of memory on the VM: 63 | vb.memory = "16384" 64 | end 65 | # 66 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 67 | # such as FTP and Heroku are also available. See the documentation at 68 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 69 | # config.push.define "atlas" do |push| 70 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 71 | # end 72 | 73 | # Enable provisioning with a shell script. Additional provisioners such as 74 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 75 | # documentation for more information about their specific syntax and use. 76 | config.vm.provision "shell", inline: <<-SHELL 77 | sudo apt-get update 78 | sudo apt-get install -y build-essential 79 | sudo apt-get install -y cmake 80 | sudo apt-get install -y ninja-build 81 | sudo apt-get install -y pkg-config zip zlib1g-dev unzip python python-pip 82 | # for folly 83 | sudo apt-get install -y libboost-all-dev libevent-dev libdouble-conversion-dev \ 84 | libgoogle-glog-dev libgflags-dev libiberty-dev liblz4-dev liblzma-dev \ 85 | libsnappy-dev zlib1g-dev binutils-dev libjemalloc-dev libssl-dev pkg-config 86 | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 87 | sudo apt-get install -y python3 python3-pip 88 | sudo apt-get update 89 | sudo apt-get install -y gcc-8 g++-8 90 | python3 -m pip install --upgrade pip 91 | python3 -m pip install perf 92 | cd /vagrant 93 | git clone --recurse-submodules https://github.com/greg7mdp/map_benchmark.git 94 | ./map_benchmark/tools/build.sh 95 | sudo apt install -y ruby-full 96 | SHELL 97 | end 98 | -------------------------------------------------------------------------------- /cmake/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG main 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) -------------------------------------------------------------------------------- /cmake/DetectVersion.cmake: -------------------------------------------------------------------------------- 1 | 2 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/parallel_hashmap/phmap_config.h" _PHMAP_H_CONTENTS) 3 | string(REGEX REPLACE ".*#define PHMAP_VERSION_MAJOR ([0-9]+).*" "\\1" DETECTED_PHMAP_VERSION_MAJOR "${_PHMAP_H_CONTENTS}") 4 | string(REGEX REPLACE ".*#define PHMAP_VERSION_MINOR ([0-9]+).*" "\\1" DETECTED_PHMAP_VERSION_MINOR "${_PHMAP_H_CONTENTS}") 5 | string(REGEX REPLACE ".*#define PHMAP_VERSION_PATCH ([0-9]+).*" "\\1" DETECTED_PHMAP_VERSION_PATCH "${_PHMAP_H_CONTENTS}") 6 | set(DETECTED_PHMAP_VERSION "${DETECTED_PHMAP_VERSION_MAJOR}.${DETECTED_PHMAP_VERSION_MINOR}.${DETECTED_PHMAP_VERSION_PATCH}") 7 | 8 | message(STATUS "Detected PHMAP Version - ${DETECTED_PHMAP_VERSION}") 9 | -------------------------------------------------------------------------------- /cmake/DownloadGTest.cmake: -------------------------------------------------------------------------------- 1 | # Downloads and unpacks googletest at configure time. Based on the instructions 2 | # at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project 3 | 4 | # Download the latest googletest from Github master 5 | configure_file( 6 | ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in 7 | ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt 8 | ) 9 | 10 | set(PHMAP_SAVE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) 11 | set(PHMAP_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 12 | 13 | # Configure and build the downloaded googletest source 14 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 15 | RESULT_VARIABLE result 16 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 17 | 18 | if(result) 19 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 20 | endif() 21 | 22 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 23 | RESULT_VARIABLE result 24 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) 25 | 26 | if(result) 27 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 28 | endif() 29 | 30 | set(CMAKE_CXX_FLAGS ${PHMAP_SAVE_CMAKE_CXX_FLAGS}) 31 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PHMAP_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 32 | 33 | # Prevent overriding the parent project's compiler/linker settings on Windows 34 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 35 | 36 | # Add googletest directly to our build. This defines the gtest and gtest_main 37 | # targets. 38 | add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src 39 | ${CMAKE_BINARY_DIR}/googletest-build 40 | EXCLUDE_FROM_ALL) 41 | -------------------------------------------------------------------------------- /cmake/helpers.cmake: -------------------------------------------------------------------------------- 1 | #set_property(GLOBAL PROPERTY USE_FOLDERS ON) 2 | set(PHMAP_IDE_FOLDER phmap) 3 | 4 | # ------------------------------------------------------------- 5 | # phmap_cc_test(NAME awesome_test 6 | # SRCS "awesome_test.cc" 7 | # DEPS phmap::awesome gmock gtest_main) 8 | # ------------------------------------------------------------- 9 | function(phmap_cc_test) 10 | cmake_parse_arguments(PHMAP_CC_TEST 11 | "" 12 | "NAME" 13 | "SRCS;COPTS;CWOPTS;CLOPTS;DEFINES;LINKOPTS;DEPS" 14 | ${ARGN} 15 | ) 16 | 17 | set(_NAME "test_${PHMAP_CC_TEST_NAME}") 18 | add_executable(${_NAME} ${PHMAP_CC_TEST_SRCS}) 19 | target_include_directories(${_NAME} 20 | PUBLIC ${PHMAP_COMMON_INCLUDE_DIRS} 21 | PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} 22 | ) 23 | target_compile_definitions(${_NAME} 24 | PUBLIC ${PHMAP_CC_TEST_DEFINES} 25 | ) 26 | if(MSVC) 27 | target_compile_options(${_NAME} 28 | PRIVATE ${PHMAP_CC_TEST_CWOPTS} /W4 /Zc:__cplusplus /std:c++latest 29 | ) 30 | else() 31 | target_compile_options(${_NAME} 32 | PRIVATE ${PHMAP_CC_TEST_CLOPTS} 33 | ) 34 | endif() 35 | target_compile_options(${_NAME} 36 | PRIVATE ${PHMAP_CC_TEST_COPTS} 37 | ) 38 | target_link_libraries(${_NAME} 39 | PUBLIC ${PHMAP_CC_TEST_DEPS} 40 | PRIVATE ${PHMAP_CC_TEST_LINKOPTS} 41 | ) 42 | # Add all Abseil targets to a a folder in the IDE for organization. 43 | set_property(TARGET ${_NAME} PROPERTY FOLDER ${PHMAP_IDE_FOLDER}/test) 44 | 45 | set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${PHMAP_CXX_STANDARD}) 46 | set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) 47 | 48 | add_test(NAME ${_NAME} COMMAND ${_NAME}) 49 | endfunction() 50 | 51 | # ------------------------------------------------------------- 52 | function(check_target my_target) 53 | if(NOT TARGET ${my_target}) 54 | message(FATAL_ERROR " PHMAP: compiling phmap tests requires a ${my_target} CMake target in your project, 55 | see CMake/README.md for more details") 56 | endif(NOT TARGET ${my_target}) 57 | endfunction() 58 | 59 | 60 | -------------------------------------------------------------------------------- /cmake/phmap.cmake: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------- 2 | # Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp) 17 | # with modifications. 18 | # 19 | # Copyright 2017 The Abseil Authors. 20 | # 21 | # Licensed under the Apache License, Version 2.0 (the "License"); 22 | # you may not use this file except in compliance with the License. 23 | # You may obtain a copy of the License at 24 | # 25 | # https://www.apache.org/licenses/LICENSE-2.0 26 | # 27 | # Unless required by applicable law or agreed to in writing, software 28 | # distributed under the License is distributed on an "AS IS" BASIS, 29 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | # See the License for the specific language governing permissions and 31 | # limitations under the License. 32 | # --------------------------------------------------------------------------- 33 | 34 | 35 | include(CMakeParseArguments) 36 | 37 | function(check_target my_target) 38 | if(NOT TARGET ${my_target}) 39 | message(FATAL_ERROR " ABSL: compiling absl requires a ${my_target} CMake target in your project, 40 | see CMake/README.md for more details") 41 | endif(NOT TARGET ${my_target}) 42 | endfunction() 43 | -------------------------------------------------------------------------------- /css/colors.css: -------------------------------------------------------------------------------- 1 | /*** 2 | 3 | colors.css v2.0.0 4 | http://clrs.cc 5 | @mrmrs 6 | MIT License 7 | 8 | ***/ 9 | /* 10 | 11 | SKINS 12 | - Backgrounds 13 | - Colors 14 | - Border colors 15 | - SVG fills 16 | - SVG Strokes 17 | 18 | */ 19 | /* Backgrounds */ 20 | .bg-navy { 21 | background-color: #001F3F; } 22 | 23 | .bg-blue { 24 | background-color: #0074D9; } 25 | 26 | .bg-aqua { 27 | background-color: #7FDBFF; } 28 | 29 | .bg-teal { 30 | background-color: #39CCCC; } 31 | 32 | .bg-olive { 33 | background-color: #3D9970; } 34 | 35 | .bg-green { 36 | background-color: #2ECC40; } 37 | 38 | .bg-lime { 39 | background-color: #01FF70; } 40 | 41 | .bg-yellow { 42 | background-color: #FFDC00; } 43 | 44 | .bg-orange { 45 | background-color: #FF851B; } 46 | 47 | .bg-red { 48 | background-color: #FF4136; } 49 | 50 | .bg-fuchsia { 51 | background-color: #F012BE; } 52 | 53 | .bg-purple { 54 | background-color: #B10DC9; } 55 | 56 | .bg-maroon { 57 | background-color: #85144B; } 58 | 59 | .bg-white { 60 | background-color: #fff; } 61 | 62 | .bg-gray { 63 | background-color: #aaa; } 64 | 65 | .bg-silver { 66 | background-color: #ddd; } 67 | 68 | .bg-black { 69 | background-color: #111; } 70 | 71 | /* Colors */ 72 | .navy { 73 | color: #001F3F; } 74 | 75 | .blue { 76 | color: #0074D9; } 77 | 78 | .aqua { 79 | color: #7FDBFF; } 80 | 81 | .teal { 82 | color: #39CCCC; } 83 | 84 | .olive { 85 | color: #3D9970; } 86 | 87 | .green { 88 | color: #2ECC40; } 89 | 90 | .lime { 91 | color: #01FF70; } 92 | 93 | .yellow { 94 | color: #FFDC00; } 95 | 96 | .orange { 97 | color: #FF851B; } 98 | 99 | .red { 100 | color: #FF4136; } 101 | 102 | .fuchsia { 103 | color: #F012BE; } 104 | 105 | .purple { 106 | color: #B10DC9; } 107 | 108 | .maroon { 109 | color: #85144B; } 110 | 111 | .white { 112 | color: #fff; } 113 | 114 | .silver { 115 | color: #ddd; } 116 | 117 | .gray { 118 | color: #aaa; } 119 | 120 | .black { 121 | color: #111; } 122 | 123 | /* Border colors 124 | 125 | Use with another border utility that sets border-width and style 126 | i.e .border { border-width: 1px; border-style: solid; } 127 | */ 128 | .border--navy { 129 | border-color: #001F3F; } 130 | 131 | .border--blue { 132 | border-color: #0074D9; } 133 | 134 | .border--aqua { 135 | border-color: #7FDBFF; } 136 | 137 | .border--teal { 138 | border-color: #39CCCC; } 139 | 140 | .border--olive { 141 | border-color: #3D9970; } 142 | 143 | .border--green { 144 | border-color: #2ECC40; } 145 | 146 | .border--lime { 147 | border-color: #01FF70; } 148 | 149 | .border--yellow { 150 | border-color: #FFDC00; } 151 | 152 | .border--orange { 153 | border-color: #FF851B; } 154 | 155 | .border--red { 156 | border-color: #FF4136; } 157 | 158 | .border--fuchsia { 159 | border-color: #F012BE; } 160 | 161 | .border--purple { 162 | border-color: #B10DC9; } 163 | 164 | .border--maroon { 165 | border-color: #85144B; } 166 | 167 | .border--white { 168 | border-color: #fff; } 169 | 170 | .border--gray { 171 | border-color: #aaa; } 172 | 173 | .border--silver { 174 | border-color: #ddd; } 175 | 176 | .border--black { 177 | border-color: #111; } 178 | 179 | /* Fills for SVG */ 180 | .fill-navy { 181 | fill: #001F3F; } 182 | 183 | .fill-blue { 184 | fill: #0074D9; } 185 | 186 | .fill-aqua { 187 | fill: #7FDBFF; } 188 | 189 | .fill-teal { 190 | fill: #39CCCC; } 191 | 192 | .fill-olive { 193 | fill: #3D9970; } 194 | 195 | .fill-green { 196 | fill: #2ECC40; } 197 | 198 | .fill-lime { 199 | fill: #01FF70; } 200 | 201 | .fill-yellow { 202 | fill: #FFDC00; } 203 | 204 | .fill-orange { 205 | fill: #FF851B; } 206 | 207 | .fill-red { 208 | fill: #FF4136; } 209 | 210 | .fill-fuchsia { 211 | fill: #F012BE; } 212 | 213 | .fill-purple { 214 | fill: #B10DC9; } 215 | 216 | .fill-maroon { 217 | fill: #85144B; } 218 | 219 | .fill-white { 220 | fill: #fff; } 221 | 222 | .fill-gray { 223 | fill: #aaa; } 224 | 225 | .fill-silver { 226 | fill: #ddd; } 227 | 228 | .fill-black { 229 | fill: #111; } 230 | 231 | /* Strokes for SVG */ 232 | .stroke-navy { 233 | stroke: #001F3F; } 234 | 235 | .stroke-blue { 236 | stroke: #0074D9; } 237 | 238 | .stroke-aqua { 239 | stroke: #7FDBFF; } 240 | 241 | .stroke-teal { 242 | stroke: #39CCCC; } 243 | 244 | .stroke-olive { 245 | stroke: #3D9970; } 246 | 247 | .stroke-green { 248 | stroke: #2ECC40; } 249 | 250 | .stroke-lime { 251 | stroke: #01FF70; } 252 | 253 | .stroke-yellow { 254 | stroke: #FFDC00; } 255 | 256 | .stroke-orange { 257 | stroke: #FF851B; } 258 | 259 | .stroke-red { 260 | stroke: #FF4136; } 261 | 262 | .stroke-fuchsia { 263 | stroke: #F012BE; } 264 | 265 | .stroke-purple { 266 | stroke: #B10DC9; } 267 | 268 | .stroke-maroon { 269 | stroke: #85144B; } 270 | 271 | .stroke-white { 272 | stroke: #fff; } 273 | 274 | .stroke-gray { 275 | stroke: #aaa; } 276 | 277 | .stroke-silver { 278 | stroke: #ddd; } 279 | 280 | .stroke-black { 281 | stroke: #111; } 282 | 283 | /* PRETTIER LINKS */ 284 | a { 285 | text-decoration: none; 286 | -webkit-transition: color .3s ease-in-out; 287 | transition: color .3s ease-in-out; } 288 | 289 | a:link { 290 | -webkit-transition: color .3s ease-in-out; 291 | transition: color .3s ease-in-out; } 292 | 293 | a:visited { } 294 | 295 | a:hover { 296 | color: #001F3F; 297 | -webkit-transition: color .3s ease-in-out; 298 | transition: color .3s ease-in-out; } 299 | 300 | a:active { 301 | -webkit-transition: color .3s ease-in-out; 302 | transition: color .3s ease-in-out; } 303 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /* main stylesheet */ 2 | 3 | @import url(http://fonts.googleapis.com/css?family=Signika); 4 | 5 | html { 6 | overflow-y: scroll; 7 | } 8 | 9 | body { 10 | font-size: 15px; 11 | font-family: HelveticaNeue, 'Helvetica Neue', Helvetica, Arial, sans-serif; 12 | color: #332; 13 | } 14 | 15 | h1, h2, h3, h4, h5 { 16 | color: #332; 17 | font-family: HelveticaNeue, 'Helvetica Neue', Helvetica, Arial, sans-serif; 18 | font-weight: 400; 19 | font-size: 1.4em; 20 | line-height: 1.1; 21 | margin-top: 30px; 22 | } 23 | 24 | pre code { 25 | font: 14px/19px Inconsolata, Monaco,"Lucida Console",Terminal,"Courier New",Courier; 26 | } 27 | 28 | .figure { 29 | text-align: center; 30 | } 31 | 32 | .small .figure img { 33 | height: 200px; 34 | } 35 | 36 | .pagetitle .figure { 37 | text-align: left !important; 38 | } 39 | 40 | .pagetitle .figure img { 41 | height: 36px; 42 | } 43 | 44 | table{ 45 | background:#fff; 46 | border:1px solid #ccc; 47 | border-width:2px; 48 | border-collapse:collapse; 49 | margin:5px 0 10px; 50 | 51 | margin-top: 20px; 52 | margin-bottom: 20px; 53 | } 54 | 55 | th, td{ 56 | border:1px solid #ccc; 57 | padding:3px 10px; 58 | text-align:left; 59 | vertical-align:top; 60 | } 61 | 62 | tr.even td{ 63 | background:#f7f7f7; 64 | } 65 | 66 | th{ 67 | background:#edeff0; 68 | } 69 | 70 | td code { 71 | border: 0px; 72 | } 73 | 74 | img { 75 | max-width: 100%; 76 | height: auto; 77 | } 78 | 79 | hr { 80 | border: 0px; 81 | height: 0; 82 | border-bottom: 1px solid #ccc; 83 | margin-bottom: 100px; 84 | } 85 | 86 | /* Logo */ 87 | 88 | .logo { 89 | text-align: center; 90 | } 91 | 92 | .tagline { 93 | font-family: Georgia; 94 | font-size: 18px; 95 | font-style: italic; 96 | line-height: 1.45; 97 | color: #383838; 98 | } 99 | 100 | .author { 101 | } 102 | 103 | .halfbreak { 104 | padding-bottom: 100px; 105 | } 106 | 107 | .break { 108 | padding-bottom: 200px; 109 | } 110 | 111 | /* TOC Links */ 112 | 113 | a { 114 | color: #111111; 115 | text-decoration: none; 116 | } 117 | 118 | .body li a { 119 | text-decoration: underline; 120 | } 121 | 122 | /* Math */ 123 | 124 | .MathJax_Display { 125 | padding-top: 20px; 126 | padding-bottom: 20px; 127 | } 128 | 129 | /* Body Links */ 130 | 131 | p a { 132 | text-decoration: underline; 133 | } 134 | 135 | li code, p code { 136 | font-size: 12px; 137 | border: 1px solid #ccc; 138 | margin-left: 3px; 139 | margin-right: 3px; 140 | padding-left: 2px; 141 | padding-right: 2px; 142 | } 143 | 144 | /* */ 145 | 146 | .center { 147 | text-align: center; 148 | } 149 | 150 | .bigger img { 151 | width: 120%; 152 | height: 120%; 153 | } 154 | 155 | pre { 156 | font-size: 0.9em; 157 | 158 | margin-bottom: 18px; 159 | margin-top: 18px; 160 | 161 | border-left: 1px solid #ccc; 162 | 163 | } 164 | 165 | h1 { 166 | margin-top: 0px; 167 | } 168 | 169 | .annotation { 170 | font-size: 10pt; 171 | } 172 | 173 | .annotation pre { 174 | display: block; 175 | margin: 0; 176 | padding: 7px 10px; 177 | overflow-x: auto; 178 | } 179 | 180 | .annotation.span2 { 181 | /* Override bootstrap */ 182 | margin-left: 0px !important; 183 | margin-top: 18px !important; 184 | } 185 | 186 | .annotation pre code { 187 | border: 0; 188 | padding: 0; 189 | background: transparent; 190 | } 191 | 192 | blockquote { 193 | border-left: 1px solid #ccc; 194 | font-family: Georgia, serif; 195 | font-size: 14px; 196 | font-style: italic; 197 | margin: 0.25em 0; 198 | padding-left: 10px; 199 | line-height: 1.45; 200 | color: #383838; 201 | left: 20px; 202 | } 203 | 204 | 205 | blockquote cite { 206 | color: #999999; 207 | font-size: 14px; 208 | display: block; 209 | margin-top: 5px; 210 | } 211 | 212 | ul.sections { 213 | list-style: none; 214 | padding:0 0 5px 0; 215 | margin:0; 216 | } 217 | 218 | code.sourceCode { 219 | padding: 0; 220 | background: inherit; 221 | } 222 | 223 | pre.sourceCode { 224 | padding: 10px; 225 | } 226 | 227 | ul.sections > li > div { 228 | -moz-box-sizing: border-box; /* firefox */ 229 | -ms-box-sizing: border-box; /* ie */ 230 | -webkit-box-sizing: border-box; /* webkit */ 231 | -khtml-box-sizing: border-box; /* konqueror */ 232 | box-sizing: border-box; /* css3 */ 233 | } 234 | 235 | 236 | /* Make the naviation centered and larger on small screens */ 237 | /*---------------------- (> 481px) ---------------------*/ 238 | 239 | @media only screen and (max-width: 481px) { 240 | 241 | } 242 | 243 | @media only screen and (min-width: 1025px) { 244 | body { 245 | padding: 10px; 246 | } 247 | 248 | .side { 249 | position: fixed; 250 | width: 120px !important; 251 | margin-left: 0px; 252 | z-index: 1000; 253 | } 254 | 255 | .side ul ul { 256 | display: none; 257 | } 258 | 259 | .side ul ul.active { 260 | display: block; 261 | } 262 | 263 | .side .active { 264 | font-weight: bold; 265 | } 266 | 267 | .body { 268 | margin-left: 120px !important; 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /doc/new_release.md: -------------------------------------------------------------------------------- 1 | - update version in `phmap_config.h` 2 | - update version in `CITATION.cff` 3 | - update version in comment on top of `CMakeLists.txt` 4 | - update all versions # in `doc/new_release.md`. 5 | - git commit 6 | - git push 7 | - create the new release on github (tag `v2.0.0` - use semantic versioning) 8 | - download the tar.gz from github, and use `sha256sum parallel-hashmap-2.0.0.tar.gz` on linux to get the sha256 9 | 10 | ## conan 11 | 12 | - use [forked repo](https://github.com/greg7mdp/conan-center-index) 13 | - sync fork in github 14 | - git checkout conan-io:master 15 | - git checkout -b phmap_2.0.0 16 | - update: `recipes/parallel-hashmap/all/conandata.yml` and `recipes/parallel-hashmap/config.yml` 17 | - sudo pip install conan -U 18 | - cd recipes/parallel-hashmap/all 19 | - *does not work* conan create conanfile.py parallel-hashmap/2.0.0@ -pr:b=default -pr:h=default 20 | *no version??* update version in `recipes/parallel-hashmap/all/conanfile.py` 21 | - git diff 22 | - git commit -am "[parallel-hashmap] Bump version to 2.0.0" 23 | - git push origin phmap_2.0.0 24 | - create PR like [this](https://github.com/conan-io/conan-center-index/pull/13161) 25 | 26 | 27 | ## vcpkg 28 | 29 | - use [forked repo](https://github.com/greg7mdp/vcpkg) 30 | - sync fork in github 31 | - `git checkout -b phmap_2.0.0` 32 | - update ports/parallel-hashmap/portfile.cmake (the sha512) and ports/parallel-hashmap/vcpkg.json (the version #) 33 | - commit 34 | - `./bootstrap-vcpkg.sh` 35 | - `vcpkg x-add-version --all --overwrite-version` ## (or `./vcpkg.exe --no-dry-run upgrade` ) 36 | - commit 37 | - push 38 | - create PR 39 | -------------------------------------------------------------------------------- /examples/allmaps.cc: -------------------------------------------------------------------------------- 1 | // Silly program just to test the natvis file for Visual Studio 2 | // ------------------------------------------------------------ 3 | #include 4 | #include "parallel_hashmap/phmap.h" 5 | 6 | template 7 | void test_set(const F &f) 8 | { 9 | Set s; 10 | typename Set::iterator it; 11 | for (int i=0; i<100; ++i) 12 | s.insert(f(i)); 13 | 14 | it = s.begin(); 15 | ++it; 16 | 17 | it = s.end(); 18 | it = s.begin(); 19 | while(it != s.end()) 20 | ++it; 21 | it = s.begin(); 22 | } 23 | 24 | int main(int, char **) 25 | { 26 | using namespace std; 27 | 28 | auto make_int = [](int i) { return i; }; 29 | auto make_string = [](int i) { return std::to_string(i); }; 30 | 31 | auto make_2int = [](int i) { return std::make_pair(i, i); }; 32 | auto make_2string = [](int i) { return std::make_pair(std::to_string(i), std::to_string(i)); }; 33 | 34 | 35 | test_set>(make_int); 36 | test_set>(make_string); 37 | 38 | test_set>(make_int); 39 | test_set>(make_string); 40 | 41 | test_set>(make_2int); 42 | test_set>(make_2string); 43 | 44 | test_set>(make_2int); 45 | test_set>(make_2string); 46 | 47 | test_set>(make_int); 48 | test_set>(make_string); 49 | 50 | test_set>(make_int); 51 | test_set>(make_string); 52 | 53 | test_set>(make_2int); 54 | test_set>(make_2string); 55 | 56 | test_set>(make_2int); 57 | test_set>(make_2string); 58 | 59 | // example of using default parameters in order to specify the mutex type. 60 | // 61 | // Please be aware that the iterators returned (by find for example) cannot 62 | // be safely read in a multithreaded environment. Instead use if_contains(), 63 | // which passes a reference value to the callback while holding the submap lock. 64 | // Similarly, write access can be done safely using modify_if, try_emplace_l 65 | // or lazy_emplace_l. 66 | // ---------------------------------------------------------------------------- 67 | using Map = phmap::parallel_flat_hash_map, 69 | std::equal_to, 70 | std::allocator>, 71 | 4, 72 | std::mutex>; 73 | auto make_2size_t = [](size_t i) { return std::make_pair(i, i); }; 74 | test_set(make_2size_t); 75 | } 76 | -------------------------------------------------------------------------------- /examples/basic.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using phmap::flat_hash_map; 6 | 7 | int main() 8 | { 9 | // Create an unordered_map of three strings (that map to strings) 10 | flat_hash_map email = 11 | { 12 | { "tom", "tom@gmail.com"}, 13 | { "jeff", "jk@gmail.com"}, 14 | { "jim", "jimg@microsoft.com"} 15 | }; 16 | 17 | // Iterate and print keys and values 18 | for (const auto& n : email) 19 | std::cout << n.first << "'s email is: " << n.second << "\n"; 20 | 21 | // Add a new entry 22 | email["bill"] = "bg@whatever.com"; 23 | 24 | // and print it 25 | std::cout << "bill's email is: " << email["bill"] << "\n"; 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /examples/btree.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "btree_fwd.h" 3 | #include 4 | 5 | int main() 6 | { 7 | // initialise map with some values using an initializer_list 8 | phmap::btree_map map = 9 | { { "John", 35 }, 10 | { "Jane", 32 }, 11 | { "Joe", 30 }, 12 | }; 13 | 14 | // add a couple more values using operator[]() 15 | map["lucy"] = 18; 16 | map["Andre"] = 20; 17 | 18 | auto it = map.find("Joe"); 19 | map.erase(it); 20 | 21 | map.insert(std::make_pair("Alex", 16)); 22 | map.emplace("Emily", 18); // emplace uses pair template constructor 23 | 24 | for (auto& p: map) 25 | std::cout << p.first << ", " << p.second << '\n'; 26 | 27 | IntString map2; // IntString is declared in btree_fwd.h 28 | 29 | map2.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(10, 'c')); 30 | map2.try_emplace(1, 10, 'a'); // phmap::btree_map supports c++17 API 31 | 32 | for (auto& p: map2) 33 | std::cout << p.first << ", " << p.second << '\n'; 34 | 35 | // create a btree_set of tuples 36 | using X = std::tuple; 37 | phmap::btree_set set; 38 | 39 | for (int i=0; i<10; ++i) 40 | set.insert(X((float)i, std::to_string(i))); 41 | set.emplace(15.0f, "15"); 42 | 43 | set.erase(X(1.0f, "1")); 44 | 45 | for (auto& e: set) 46 | std::cout << std::get<0>(e) << ", \"" << std::get<1>(e) << "\" \n"; 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /examples/btree_fwd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using IntString = phmap::btree_map; 6 | 7 | -------------------------------------------------------------------------------- /examples/custom_pointer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using mmap_file_t = boost::interprocess::managed_mapped_file; 9 | 10 | template 11 | using bi_alloc_t = boost::interprocess::allocator; 12 | 13 | template 14 | using scoped_alloc_t = std::scoped_allocator_adaptor; 15 | 16 | void simple_map() { 17 | struct LatpLon { 18 | int32_t latp; 19 | int32_t lon; 20 | }; 21 | 22 | using nodestore_pair_t = std::pair; 23 | using map_t = phmap::flat_hash_map, std::equal_to, 24 | bi_alloc_t>; 25 | 26 | auto mmap_file = 27 | boost::interprocess::managed_mapped_file(boost::interprocess::open_or_create, "map_iv.dat", 1000000); 28 | map_t* map = mmap_file.find_or_construct("node_store")(mmap_file.get_segment_manager()); 29 | 30 | for (unsigned int i = 0; i < 1000; ++i) { 31 | LatpLon p = {10, 10}; 32 | map->emplace(i, p); 33 | } 34 | 35 | std::cout << map->at(10).latp << " " << map->at(10).lon << std::endl; 36 | } 37 | 38 | void scoped_map() { 39 | using way_t = std::vector>; 40 | using waystore_pair_t = std::pair; 41 | using map_t = phmap::flat_hash_map, std::equal_to, 42 | std::scoped_allocator_adaptor>>; 43 | 44 | auto mmap_file = 45 | boost::interprocess::managed_mapped_file(boost::interprocess::open_or_create, "map_iv.dat", 1000000); 46 | map_t* map = mmap_file.find_or_construct("ways_store")(mmap_file.get_segment_manager()); 47 | 48 | for (unsigned int i = 0; i < 1000; ++i) { 49 | std::vector init = {1, 2, 3, 4}; 50 | map->emplace(std::piecewise_construct, std::forward_as_tuple(i), std::forward_as_tuple(init.begin(), init.end())); 51 | } 52 | 53 | std::cout << map->at(10).size() << std::endl; 54 | for (auto const& i : map->at(10)) 55 | std::cout << i << " "; 56 | std::cout << std::endl; 57 | } 58 | 59 | int main() 60 | { 61 | simple_map(); 62 | scoped_map(); 63 | return 0; 64 | } -------------------------------------------------------------------------------- /examples/dump_load.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void dump_load_uint64_uint32() { 5 | phmap::flat_hash_map mp1 = { {100, 99}, {300, 299} }; 6 | 7 | for (const auto& n : mp1) 8 | std::cout << n.first << "'s value is: " << n.second << "\n"; 9 | 10 | { 11 | phmap::BinaryOutputArchive ar_out("./dump.data"); 12 | mp1.phmap_dump(ar_out); 13 | } 14 | 15 | phmap::flat_hash_map mp2; 16 | { 17 | phmap::BinaryInputArchive ar_in("./dump.data"); 18 | mp2.phmap_load(ar_in); 19 | } 20 | 21 | for (const auto& n : mp2) 22 | std::cout << n.first << "'s value is: " << n.second << "\n"; 23 | } 24 | 25 | void dump_load_parallel_flat_hash_map() { 26 | phmap::parallel_flat_hash_map mp1 = { 27 | {100, 99}, {300, 299}, {101, 992} }; 28 | 29 | for (const auto& n : mp1) 30 | std::cout << "key: " << n.first << ", value: " << n.second << "\n"; 31 | 32 | { 33 | phmap::BinaryOutputArchive ar_out("./dump.data"); 34 | mp1.phmap_dump(ar_out); 35 | } 36 | 37 | phmap::parallel_flat_hash_map mp2; 38 | { 39 | phmap::BinaryInputArchive ar_in("./dump.data"); 40 | mp2.phmap_load(ar_in); 41 | } 42 | 43 | for (const auto& n : mp2) 44 | std::cout << "key: " << n.first << ", value: " << n.second << "\n"; 45 | } 46 | 47 | int main() 48 | { 49 | dump_load_uint64_uint32(); 50 | dump_load_parallel_flat_hash_map(); 51 | return 0; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /examples/dump_nested.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Example of dumping a map, containing values which are phmap maps or sets 4 | * building this requires c++17 support 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | template 12 | class MyMap : public phmap::flat_hash_map> 13 | { 14 | public: 15 | using Set = phmap::flat_hash_set; 16 | 17 | void dump(const std::string &filename) 18 | { 19 | phmap::BinaryOutputArchive ar_out (filename.c_str()); 20 | 21 | ar_out.saveBinary(this->size()); 22 | for (auto& [k, v] : *this) 23 | { 24 | ar_out.saveBinary(k); 25 | ar_out.saveBinary(v); 26 | } 27 | } 28 | 29 | void load(const std::string & filename) 30 | { 31 | phmap::BinaryInputArchive ar_in(filename.c_str()); 32 | 33 | size_t size; 34 | ar_in.loadBinary(&size); 35 | this->reserve(size); 36 | 37 | while (size--) 38 | { 39 | K k; 40 | Set v; 41 | 42 | ar_in.loadBinary(&k); 43 | ar_in.loadBinary(&v); 44 | 45 | this->insert_or_assign(std::move(k), std::move(v)); 46 | } 47 | } 48 | 49 | void insert(K k, V v) 50 | { 51 | Set &set = (*this)[k]; 52 | set.insert(v); 53 | } 54 | 55 | friend std::ostream& operator<<(std::ostream& os, const MyMap& map) 56 | { 57 | for (const auto& [k, m] : map) 58 | { 59 | os << k << ": ["; 60 | for (const auto& x : m) 61 | os << x << ", "; 62 | os << "]\n"; 63 | } 64 | return os; 65 | } 66 | }; 67 | 68 | int main() 69 | { 70 | MyMap m; 71 | m.insert(1, 5); 72 | m.insert(1, 8); 73 | m.insert(2, 3); 74 | m.insert(1, 15); 75 | m.insert(1, 27); 76 | m.insert(2, 10); 77 | m.insert(2, 13); 78 | 79 | std::cout << m << "\n"; 80 | 81 | m.dump("test_archive"); 82 | m.clear(); 83 | m.load("test_archive"); 84 | 85 | std::cout << m << "\n"; 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /examples/emplace.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | template 12 | using milliseconds = std::chrono::duration; 13 | 14 | // type containing std::string. Seems to take a long time to construct (and maybe move) 15 | // ------------------------------------------------------------------------------------ 16 | class custom_type 17 | { 18 | std::string one = "one"; 19 | std::string two = "two"; 20 | std::uint32_t three = 3; 21 | std::uint64_t four = 4; 22 | std::uint64_t five = 5; 23 | public: 24 | custom_type() = default; 25 | 26 | // Make object movable and non-copyable 27 | custom_type(custom_type &&) = default; 28 | custom_type& operator=(custom_type &&) = default; 29 | 30 | // should be automatically deleted per http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 31 | //custom_type(custom_type const&) = delete; 32 | //custom_type& operator=(custom_type const&) = delete; 33 | }; 34 | 35 | // type containing only integrals. should be faster to create. 36 | // ----------------------------------------------------------- 37 | class custom_type_2 38 | { 39 | std::uint32_t three = 3; 40 | std::uint64_t four = 4; 41 | std::uint64_t five = 5; 42 | std::uint64_t six = 6; 43 | public: 44 | custom_type_2() = default; 45 | 46 | // Make object movable and non-copyable 47 | custom_type_2(custom_type_2 &&) = default; 48 | custom_type_2& operator=(custom_type_2 &&) = default; 49 | 50 | // should be automatically deleted per http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 51 | //custom_type_2(custom_type_2 const&) = delete; 52 | //custom_type_2& operator=(custom_type_2 const&) = delete; 53 | }; 54 | 55 | // convert std::size_t to appropriate key 56 | // -------------------------------------- 57 | template 58 | struct GenKey 59 | { 60 | K operator()(std::size_t j); 61 | }; 62 | 63 | template <> 64 | struct GenKey 65 | { 66 | std::string operator()(std::size_t j) { 67 | std::ostringstream stm; 68 | stm << j; 69 | return stm.str(); 70 | } 71 | }; 72 | 73 | template <> 74 | struct GenKey 75 | { 76 | int operator()(std::size_t j) { 77 | return (int)j; 78 | } 79 | }; 80 | 81 | // emplace key + large struct 82 | // -------------------------- 83 | template struct _emplace 84 | { 85 | void operator()(Map &m, std::size_t j); 86 | }; 87 | 88 | // "void" template parameter -> use emplace 89 | template struct _emplace 90 | { 91 | void operator()(Map &m, std::size_t j) 92 | { 93 | m.emplace(GenKey()(j), V()); 94 | } 95 | }; 96 | 97 | // "int" template parameter -> use emplace_back for std::vector 98 | template struct _emplace 99 | { 100 | void operator()(Map &m, std::size_t j) 101 | { 102 | m.emplace_back(GenKey()(j), V()); 103 | } 104 | }; 105 | 106 | // The test itself 107 | // --------------- 108 | template class INSERT> 109 | void _test(std::size_t iterations, std::size_t container_size, const char *map_name) 110 | { 111 | std::size_t count = 0; 112 | auto t1 = std::chrono::high_resolution_clock::now(); 113 | INSERT insert; 114 | for (std::size_t i=0; i(t2 - t1).count(); 123 | if (count != iterations*container_size) 124 | std::clog << " invalid count: " << count << "\n"; 125 | std::clog << map_name << std::fixed << int(elapsed) << " ms\n"; 126 | } 127 | 128 | 129 | template class INSERT> 130 | void test(std::size_t iterations, std::size_t container_size) 131 | { 132 | std::clog << "bench: iterations: " << iterations << " / container_size: " << container_size << "\n"; 133 | 134 | _test, K, V, void, INSERT>(iterations, container_size, " std::map: "); 135 | _test, K, V, void, INSERT>(iterations, container_size, " std::unordered_map: "); 136 | _test, K, V, void, INSERT>(iterations, container_size, " phmap::flat_hash_map: "); 137 | _test>, K, V, int, INSERT> (iterations, container_size, " std::vector: "); 138 | std::clog << "\n"; 139 | 140 | } 141 | 142 | int main() 143 | { 144 | std::size_t iterations = 100000; 145 | 146 | // test with custom_type_2 (int key + 32 byte value). This is representative 147 | // of the hash table insertion speed. 148 | // ------------------------------------------------------------------------- 149 | std::clog << "\n\n" << "testing with " "\n"; 150 | std::clog << "---------------------------------" "\n"; 151 | test(iterations,10); 152 | test(iterations,100); 153 | test(iterations,500); 154 | 155 | // test with custom_type, which contains two std::string values, and use 156 | // a generated string key. This is not very indicative of the speed of the 157 | // hash itself, as a good chunk of the time is spent creating the keys and 158 | // values (as shown by the long times even for std::vector). 159 | // ----------------------------------------------------------------------- 160 | std::clog << "\n" << "testing with " "\n"; 161 | std::clog << "---------------------------------" "\n"; 162 | test(iterations,1); 163 | test(iterations,10); 164 | test(iterations,50); 165 | 166 | } 167 | -------------------------------------------------------------------------------- /examples/f1.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Make sure that the phmap.h header builds fine when included in two separate 3 | * source files 4 | */ 5 | #include 6 | #include 7 | 8 | using phmap::flat_hash_map; 9 | 10 | int main() 11 | { 12 | // Create an unordered_map of three strings (that map to strings) 13 | using Map = flat_hash_map; 14 | Map email = 15 | { 16 | { "tom", "tom@gmail.com"}, 17 | { "jeff", "jk@gmail.com"}, 18 | { "jim", "jimg@microsoft.com"} 19 | }; 20 | 21 | extern void f2(Map&); 22 | f2(email); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /examples/f2.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Make sure that the phmap.h header builds fine when included in two separate 3 | * source files 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | using phmap::flat_hash_map; 10 | using Map = flat_hash_map; 11 | 12 | void f2(Map& email) 13 | { 14 | // Iterate and print keys and values 15 | for (const auto& n : email) 16 | std::cout << n.first << "'s email is: " << n.second << "\n"; 17 | 18 | // Add a new entry 19 | email["bill"] = "bg@whatever.com"; 20 | 21 | // and print it 22 | std::cout << "bill's email is: " << email["bill"] << "\n"; 23 | } 24 | -------------------------------------------------------------------------------- /examples/hash.cc: -------------------------------------------------------------------------------- 1 | #include // minimal header providing phmap::HashState() 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #if PHMAP_HAVE_STD_STRING_VIEW 8 | #include 9 | #endif 10 | #include 11 | 12 | using std::string; 13 | using std::tuple; 14 | using std::pair; 15 | 16 | using groupid_t = std::array; 17 | 18 | namespace std 19 | { 20 | template<> struct hash 21 | { 22 | #if PHMAP_HAVE_STD_STRING_VIEW 23 | std::size_t operator()(groupid_t const &g) const 24 | { 25 | const std::string_view bv{reinterpret_cast(g.data()), sizeof(g)}; 26 | return std::hash()(bv); 27 | } 28 | #else 29 | std::size_t operator()(groupid_t const &g) const 30 | { 31 | return phmap::Hash()(std::tuple_cat(g)); 32 | } 33 | #endif 34 | }; 35 | } 36 | 37 | int main() 38 | { 39 | std::vector groups = { 40 | {17, 75, 82, 66}, 41 | {22, 88, 54, 42}, 42 | {11, 55, 77, 99} }; 43 | 44 | for (const auto &g : groups) 45 | std::cout << std::hash()(g) << '\n'; 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /examples/hash_bench.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #define PHMAP_ALLOCATOR_NOTHROW 1 9 | #include 10 | 11 | // this is probably the fastest high quality 64bit random number generator that exists. 12 | // Implements Small Fast Counting v4 RNG from PractRand. 13 | class sfc64 { 14 | public: 15 | using result_type = uint64_t; 16 | 17 | // no copy ctors so we don't accidentally get the same random again 18 | sfc64(sfc64 const&) = delete; 19 | sfc64& operator=(sfc64 const&) = delete; 20 | 21 | sfc64(sfc64&&) = default; 22 | sfc64& operator=(sfc64&&) = default; 23 | 24 | sfc64(std::array const& _state) 25 | : m_a(_state[0]) 26 | , m_b(_state[1]) 27 | , m_c(_state[2]) 28 | , m_counter(_state[3]) {} 29 | 30 | static constexpr uint64_t(min)() { 31 | return (std::numeric_limits::min)(); 32 | } 33 | static constexpr uint64_t(max)() { 34 | return (std::numeric_limits::max)(); 35 | } 36 | 37 | sfc64() 38 | : sfc64(UINT64_C(0x853c49e6748fea9b)) {} 39 | 40 | sfc64(uint64_t _seed) 41 | : m_a(_seed) 42 | , m_b(_seed) 43 | , m_c(_seed) 44 | , m_counter(1) { 45 | for (int i = 0; i < 12; ++i) { 46 | operator()(); 47 | } 48 | } 49 | 50 | void seed() { 51 | *this = sfc64{std::random_device{}()}; 52 | } 53 | 54 | uint64_t operator()() noexcept { 55 | auto const tmp = m_a + m_b + m_counter++; 56 | m_a = m_b ^ (m_b >> right_shift); 57 | m_b = m_c + (m_c << left_shift); 58 | m_c = rotl(m_c, rotation) + tmp; 59 | return tmp; 60 | } 61 | 62 | std::array state() const { 63 | return {{m_a, m_b, m_c, m_counter}}; 64 | } 65 | 66 | void state(std::array const& s) { 67 | m_a = s[0]; 68 | m_b = s[1]; 69 | m_c = s[2]; 70 | m_counter = s[3]; 71 | } 72 | 73 | private: 74 | template 75 | T rotl(T const x, int k) { 76 | return (x << k) | (x >> (8 * sizeof(T) - k)); 77 | } 78 | 79 | static constexpr int rotation = 24; 80 | static constexpr int right_shift = 11; 81 | static constexpr int left_shift = 3; 82 | uint64_t m_a; 83 | uint64_t m_b; 84 | uint64_t m_c; 85 | uint64_t m_counter; 86 | }; 87 | 88 | static inline std::string to_str(uint64_t x) { 89 | std::string res(4, '1'); 90 | x = (x >> 48) ^ (x >> 32) ^ (x >> 16) ^ x; // combine 64 bits > 16 lsb 91 | for (size_t i=0; i<4; ++i) { 92 | res[i] = 'a' + (x & 0xF); 93 | x >>= 4; 94 | } 95 | return res; 96 | } 97 | 98 | int main() 99 | { 100 | using Map = phmap::flat_hash_map; 101 | Map map; 102 | map.reserve((size_t)(65536 * 1.1)); // we will create a maximun of 65536 different strings 103 | 104 | sfc64 rng(123); 105 | constexpr size_t const n = 50000000; 106 | for (size_t i = 0; i < n; ++i) { 107 | auto s = to_str(rng()); 108 | map[s]++; 109 | map[s]++; 110 | map[s]++; 111 | map[s]++; 112 | map[s]++; 113 | map[s]++; 114 | map[s]++; 115 | map[s]++; 116 | map[s]++; 117 | map[s]++; 118 | } 119 | 120 | uint64_t cnt = 0; 121 | for (const auto& s : map) { 122 | if (++cnt == 6) break; 123 | std::cout << s.first << ": " << s.second << '\n'; 124 | } 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /examples/hash_std.cc: -------------------------------------------------------------------------------- 1 | #include "hash_std.h" // defines Person with std::hash specialization 2 | 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // As we have defined a specialization of std::hash() for Person, 9 | // we can now create sparse_hash_set or sparse_hash_map of Persons 10 | // ---------------------------------------------------------------- 11 | phmap::flat_hash_set persons = 12 | { { "John", "Mitchell", 35 }, 13 | { "Jane", "Smith", 32 }, 14 | { "Jane", "Smith", 30 }, 15 | }; 16 | 17 | for (auto& p: persons) 18 | std::cout << p._first << ' ' << p._last << " (" << p._age << ")" << '\n'; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /examples/hash_std.h: -------------------------------------------------------------------------------- 1 | #ifndef phmap_example_hash_std_ 2 | #define phmap_example_hash_std_ 3 | 4 | #include // minimal header providing phmap::HashState() 5 | #include 6 | using std::string; 7 | 8 | struct Person 9 | { 10 | bool operator==(const Person &o) const 11 | { 12 | return _first == o._first && _last == o._last && _age == o._age; 13 | } 14 | 15 | string _first; 16 | string _last; 17 | int _age; 18 | }; 19 | 20 | namespace std 21 | { 22 | // inject specialization of std::hash for Person into namespace std 23 | // An alternative is to provide a hash_value() friend function (see hash_value.h) 24 | // ------------------------------------------------------------------------------ 25 | template<> struct hash 26 | { 27 | std::size_t operator()(Person const &p) const 28 | { 29 | return phmap::HashState().combine(0, p._first, p._last, p._age); 30 | } 31 | }; 32 | } 33 | 34 | #endif // phmap_example_hash_std_ 35 | -------------------------------------------------------------------------------- /examples/hash_value.cc: -------------------------------------------------------------------------------- 1 | #include "hash_value.h" // defines Person with std::hash specialization 2 | 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // As we have defined a specialization of std::hash() for Person, 9 | // we can now create sparse_hash_set or sparse_hash_map of Persons 10 | // ---------------------------------------------------------------- 11 | phmap::flat_hash_set persons = 12 | { { "John", "Mitchell", 35 }, 13 | { "Jane", "Smith", 32 }, 14 | { "Jane", "Smith", 30 }, 15 | }; 16 | 17 | for (auto& p: persons) 18 | std::cout << p._first << ' ' << p._last << " (" << p._age << ")" << '\n'; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /examples/hash_value.h: -------------------------------------------------------------------------------- 1 | #ifndef phmap_example_hash_value_ 2 | #define phmap_example_hash_value_ 3 | 4 | #include // minimal header providing phmap::HashState() 5 | #include 6 | using std::string; 7 | 8 | struct Person 9 | { 10 | bool operator==(const Person &o) const 11 | { 12 | return _first == o._first && _last == o._last && _age == o._age; 13 | } 14 | 15 | // Demonstrates how to provide the hash function as a friend member function of the class 16 | // This can be used as an alternative to providing a std::hash specialization 17 | // -------------------------------------------------------------------------------------- 18 | friend size_t hash_value(const Person &p) 19 | { 20 | return phmap::HashState().combine(0, p._first, p._last, p._age); 21 | } 22 | 23 | string _first; 24 | string _last; 25 | int _age; 26 | }; 27 | 28 | #endif // phmap_example_hash_value_ 29 | -------------------------------------------------------------------------------- /examples/insert_bench.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define PHMAP_ALLOCATOR_NOTHROW 1 8 | #include 9 | 10 | // this is probably the fastest high quality 64bit random number generator that exists. 11 | // Implements Small Fast Counting v4 RNG from PractRand. 12 | class sfc64 { 13 | public: 14 | using result_type = uint64_t; 15 | 16 | // no copy ctors so we don't accidentally get the same random again 17 | sfc64(sfc64 const&) = delete; 18 | sfc64& operator=(sfc64 const&) = delete; 19 | 20 | sfc64(sfc64&&) = default; 21 | sfc64& operator=(sfc64&&) = default; 22 | 23 | sfc64(std::array const& _state) 24 | : m_a(_state[0]) 25 | , m_b(_state[1]) 26 | , m_c(_state[2]) 27 | , m_counter(_state[3]) {} 28 | 29 | static constexpr uint64_t(min)() { 30 | return (std::numeric_limits::min)(); 31 | } 32 | static constexpr uint64_t(max)() { 33 | return (std::numeric_limits::max)(); 34 | } 35 | 36 | sfc64() 37 | : sfc64(UINT64_C(0x853c49e6748fea9b)) {} 38 | 39 | sfc64(uint64_t _seed) 40 | : m_a(_seed) 41 | , m_b(_seed) 42 | , m_c(_seed) 43 | , m_counter(1) { 44 | for (int i = 0; i < 12; ++i) { 45 | operator()(); 46 | } 47 | } 48 | 49 | void seed() { 50 | *this = sfc64{std::random_device{}()}; 51 | } 52 | 53 | uint64_t operator()() noexcept { 54 | auto const tmp = m_a + m_b + m_counter++; 55 | m_a = m_b ^ (m_b >> right_shift); 56 | m_b = m_c + (m_c << left_shift); 57 | m_c = rotl(m_c, rotation) + tmp; 58 | return tmp; 59 | } 60 | 61 | // this is a bit biased, but for our use case that's not important. 62 | uint64_t operator()(uint64_t boundExcluded) noexcept { 63 | #ifdef PHMAP_HAS_UMUL128 64 | uint64_t h; 65 | (void)phmap::umul128(operator()(), boundExcluded, &h); 66 | return h; 67 | #else 68 | return 0; 69 | #endif 70 | } 71 | 72 | std::array state() const { 73 | return {{m_a, m_b, m_c, m_counter}}; 74 | } 75 | 76 | void state(std::array const& s) { 77 | m_a = s[0]; 78 | m_b = s[1]; 79 | m_c = s[2]; 80 | m_counter = s[3]; 81 | } 82 | 83 | private: 84 | template 85 | T rotl(T const x, int k) { 86 | return (x << k) | (x >> (8 * sizeof(T) - k)); 87 | } 88 | 89 | static constexpr int rotation = 24; 90 | static constexpr int right_shift = 11; 91 | static constexpr int left_shift = 3; 92 | uint64_t m_a; 93 | uint64_t m_b; 94 | uint64_t m_c; 95 | uint64_t m_counter; 96 | }; 97 | 98 | 99 | int main() 100 | { 101 | // Create an unordered_map of three strings (that map to strings) 102 | using Map = phmap::parallel_node_hash_map; 103 | static size_t const n = 50000000; 104 | sfc64 rng(123); 105 | 106 | size_t checksum = 0; 107 | 108 | if (0) 109 | { 110 | size_t const max_rng = n / 20; 111 | Map map; 112 | for (size_t i = 0; i < n; ++i) { 113 | checksum += ++map[static_cast(rng(max_rng))]; 114 | } 115 | } 116 | 117 | if (0) 118 | { 119 | size_t const max_rng = n / 4; 120 | Map map; 121 | for (size_t i = 0; i < n; ++i) { 122 | checksum += ++map[static_cast(rng(max_rng))]; 123 | } 124 | } 125 | 126 | if (1) 127 | { 128 | size_t const max_rng = n / 2; 129 | Map map; 130 | for (size_t i = 0; i < n; ++i) { 131 | checksum += ++map[static_cast(rng(max_rng))]; 132 | } 133 | } 134 | 135 | if (0) 136 | { 137 | Map map; 138 | for (size_t i = 0; i < n; ++i) { 139 | checksum += ++map[static_cast(rng())]; 140 | } 141 | } 142 | printf("%zu\n", checksum); 143 | } 144 | -------------------------------------------------------------------------------- /examples/lazy_emplace_l.cc: -------------------------------------------------------------------------------- 1 | // ------------------------ 2 | // Windows specific example 3 | // curtesy of @kanonka 4 | // ------------------------ 5 | #include 6 | #include "parallel_hashmap/phmap.h" 7 | #include 8 | #include 9 | #include 10 | 11 | using Map = phmap::parallel_flat_hash_map, 12 | phmap::priv::hash_default_eq, 13 | std::allocator>, 8, phmap::srwlock>; 14 | 15 | class Dict 16 | { 17 | Map m_stringsMap; 18 | 19 | public: 20 | int addParallel(std::string&& str, volatile long* curIdx) 21 | { 22 | int newIndex = -1; 23 | m_stringsMap.lazy_emplace_l(std::move(str), 24 | [&](Map::value_type& p) { newIndex = p.second; }, // called only when key was already present 25 | [&](const Map::constructor& ctor) // construct value_type in place when key not present 26 | { newIndex = InterlockedIncrement(curIdx); ctor(std::move(str), newIndex); }); 27 | 28 | return newIndex; 29 | } 30 | }; 31 | 32 | int main() 33 | { 34 | size_t totalSize = 6000000; 35 | std::vector values(totalSize); 36 | Dict dict; 37 | volatile long index = 0; 38 | concurrency::parallel_for(size_t(0), size_t(totalSize), 39 | [&](size_t i) { 40 | std::string s = "ab_uu_" + std::to_string(i % 1000000); 41 | values[i] = dict.addParallel(std::move(s), &index); 42 | }); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /examples/llil_utils/gen-llil.pl: -------------------------------------------------------------------------------- 1 | # gen-llil.pl 2 | # Crude program to generate a big LLiL test file to use in benchmarks 3 | # On Windows running: 4 | # perl gen-llil.pl big2.txt 200 3 - produces a test file with size = 35,152,000 bytes 5 | # (lines terminated with "\r\n") 6 | # perl gen-llil.pl big2.txt 200 3 1 - produces a test file with size = 31,636,800 bytes 7 | # (lines terminated with "\n") 8 | # On Unix, lines are terminated with "\n" and the file size is always 31,636,800 bytes 9 | 10 | use strict; 11 | use warnings; 12 | use autodie; 13 | 14 | { 15 | my $ordmin = ord('a'); 16 | my $ordmax = ord('z') + 1; 17 | 18 | # Generate a random word 19 | sub gen_random_word { 20 | my $word = shift; # word prefix 21 | my $nchar = shift; # the number of random chars to append 22 | for my $i (1 .. $nchar) { 23 | $word .= chr( $ordmin + int( rand($ordmax - $ordmin) ) ); 24 | } 25 | return $word; 26 | } 27 | } 28 | 29 | sub create_test_file { 30 | my $fname = shift; 31 | my $count = shift; 32 | my $wordlen = shift; 33 | my $fbin = shift; 34 | open( my $fh_out, '>', $fname ); 35 | $fbin and binmode($fh_out); 36 | for my $c ( 'aaa' .. 'zzz' ) { 37 | for my $i (1 .. $count) { 38 | print {$fh_out} gen_random_word( $c, $wordlen ) . "\t" . 1 . "\n"; 39 | } 40 | } 41 | } 42 | 43 | my $outfile = shift; 44 | my $count = shift; 45 | my $wordlen = shift; 46 | my $fbin = shift; # default is to use text stream (not a binary stream) 47 | defined($fbin) or $fbin = 0; 48 | $outfile or die "usage: $0 outfile count wordlen\n"; 49 | $count or die "usage: $0 outfile count wordlen\n"; 50 | print "generating test file '$outfile' with count '$count' (binmode=$fbin)\n"; 51 | create_test_file($outfile, $count, $wordlen, $fbin); 52 | print "file size=", -s $outfile, "\n"; 53 | -------------------------------------------------------------------------------- /examples/llil_utils/gen_files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script for generating input files for llil4map.cc. 3 | 4 | mkdir -p tmp 5 | cp gen-llil.pl shuffle.pl tmp 6 | cd tmp 7 | 8 | # create 26 random files 9 | for n in $(perl -le "print for 'aa'..'az'"); do 10 | perl gen-llil.pl big$n 200 3 1 11 | perl shuffle.pl big$n >1; mv 1 big$n 12 | done & 13 | 14 | # create 26 random files 15 | for n in $(perl -le "print for 'ba'..'bz'"); do 16 | perl gen-llil.pl big$n 200 3 1 17 | perl shuffle.pl big$n >2; mv 2 big$n 18 | done & 19 | 20 | # create 26 random files 21 | for n in $(perl -le "print for 'ca'..'cz'"); do 22 | perl gen-llil.pl big$n 200 3 1 23 | perl shuffle.pl big$n >3; mv 3 big$n 24 | done & 25 | 26 | # create 14 random files (total 92 files) 27 | for n in $(perl -le "print for 'da'..'dn'"); do 28 | perl gen-llil.pl big$n 200 3 1 29 | perl shuffle.pl big$n >4; mv 4 big$n 30 | done & 31 | 32 | cd .. 33 | 34 | wait -------------------------------------------------------------------------------- /examples/llil_utils/run_llil4map: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd tmp 4 | ../../ex_llil4map big* big* big* | cksum -------------------------------------------------------------------------------- /examples/llil_utils/shuffle.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use List::Util 'shuffle'; 5 | my @arr = shuffle <>; 6 | print @arr; 7 | -------------------------------------------------------------------------------- /examples/matt.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // ------------------------------------------------------------------- 14 | // ------------------------------------------------------------------- 15 | class Timer 16 | { 17 | public: 18 | Timer(std::string name) : _name(std::move(name)), _start(std::chrono::high_resolution_clock::now()) {} 19 | 20 | ~Timer() 21 | { 22 | std::chrono::duration elapsed_seconds = std::chrono::high_resolution_clock::now() - _start; 23 | printf("%s: %.3fs\n", _name.c_str(), elapsed_seconds.count()); 24 | } 25 | 26 | private: 27 | std::string _name; 28 | std::chrono::high_resolution_clock::time_point _start; 29 | }; 30 | 31 | // -------------------------------------------------------------------------- 32 | // from: https://github.com/preshing/RandomSequence 33 | // -------------------------------------------------------------------------- 34 | class RSU 35 | { 36 | private: 37 | uint32_t m_index; 38 | uint32_t m_intermediateOffset; 39 | 40 | static uint32_t permuteQPR(uint32_t x) 41 | { 42 | static const uint32_t prime = 4294967291u; 43 | if (x >= prime) 44 | return x; // The 5 integers out of range are mapped to themselves. 45 | uint32_t residue = ((unsigned long long) x * x) % prime; 46 | return (x <= prime / 2) ? residue : prime - residue; 47 | } 48 | 49 | public: 50 | RSU(uint32_t seedBase, uint32_t seedOffset) 51 | { 52 | m_index = permuteQPR(permuteQPR(seedBase) + 0x682f0161); 53 | m_intermediateOffset = permuteQPR(permuteQPR(seedOffset) + 0x46790905); 54 | } 55 | 56 | uint32_t next() 57 | { 58 | return permuteQPR((permuteQPR(m_index++) + m_intermediateOffset) ^ 0x5bf03635); 59 | } 60 | }; 61 | 62 | using Perturb = std::function &)>; 63 | 64 | // -------------------------------------------------------------------------- 65 | // -------------------------------------------------------------------------- 66 | template 67 | void test(const char *name, const Perturb &perturb1, const Perturb& /* perturb2 */) 68 | { 69 | //phmap::btree_set s; 70 | Set s; 71 | 72 | unsigned int seed = 76687; 73 | RSU rsu(seed, seed + 1); 74 | 75 | for (uint32_t i=0; i order(s.begin(), s.end()); // contains sorted, randomly generated keys (when using phmap::btree_set) 79 | // or keys in the final order of a Set (when using Set). 80 | 81 | perturb1(order); // either keep them in same order, or shuffle them 82 | 83 | #if 0 84 | order.resize(N/4); 85 | perturb2(order); 86 | #endif 87 | 88 | Timer t(name); // start timer 89 | Set c; 90 | //c.reserve(order.size()); // whether this "reserve()" is present or not makes a huge difference 91 | c.insert(order.begin(), order.end()); // time for inserting the same keys into the set 92 | // should not depend on them being sorted or not. 93 | } 94 | 95 | // -------------------------------------------------------------------------- 96 | // -------------------------------------------------------------------------- 97 | template 98 | using pset = phmap::parallel_flat_hash_set, 100 | phmap::priv::hash_default_eq, 101 | phmap::priv::Allocator, // alias for std::allocator 102 | N>; 103 | 104 | // -------------------------------------------------------------------------- 105 | // -------------------------------------------------------------------------- 106 | int main() 107 | { 108 | auto shuffle = [](std::vector &order) { 109 | std::random_device rd; 110 | std::mt19937 g(rd()); 111 | std::shuffle(order.begin(), order.end(), g); 112 | }; 113 | 114 | auto noop = [](std::vector &) {}; 115 | 116 | auto perturb2 = noop; 117 | 118 | constexpr uint32_t num_keys = 10000000; 119 | using T = uint64_t; 120 | 121 | test, num_keys>("flat_hash_set ordered ", noop, perturb2); 122 | 123 | test, num_keys>("flat_hash_set shuffled", shuffle, perturb2); 124 | 125 | test, num_keys>("parallel (16) ordered ", noop, perturb2); 126 | 127 | test, num_keys>("parallel (16) shuffled", shuffle, perturb2); 128 | 129 | test, num_keys>("parallel (64) ordered ", noop, perturb2); 130 | 131 | test, num_keys>("parallel (64) shuffled", shuffle, perturb2); 132 | 133 | test, num_keys>("parallel (256) ordered ", noop, perturb2); 134 | 135 | test, num_keys>("parallel (256) shuffled", shuffle, perturb2); 136 | } 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /examples/mt_word_counter.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | /* 14 | * count the number of occurrences of each word in a large text file using multiple threads 15 | */ 16 | 17 | int main() { 18 | // download Jane Austin "Pride and Prejudice" 19 | // ------------------------------------------ 20 | if (system("curl https://www.gutenberg.org/files/1342/1342-0.txt -o 1342-0.txt") != 0) { 21 | std::cout << "Error: could not retrieve test file https://www.gutenberg.org/files/1342/1342-0.txt\n"; 22 | return 1; 23 | } 24 | 25 | const std::string filename = "1342-0.txt"; 26 | 27 | constexpr int num_threads = 4; 28 | std::vector threads; 29 | std::array, num_threads> lines_array; 30 | 31 | { 32 | // populate 4 vectors with lines from the book 33 | std::ifstream file(filename); 34 | if (!file.is_open()) { 35 | std::cout << "Error: could not open file " << filename << std::endl; 36 | return 1; 37 | } 38 | int line_idx = 0; 39 | std::string line; 40 | while (std::getline(file, line)) { 41 | lines_array[line_idx % num_threads].push_back(std::move(line)); 42 | ++line_idx; 43 | } 44 | } 45 | 46 | using Map = phmap::parallel_flat_hash_map_m; // parallel_flat_hash_map_m has default internal mutex 47 | Map word_counts; 48 | 49 | // run 4 threads, each thread processing lines from one of the vectors 50 | // ------------------------------------------------------------------- 51 | threads.reserve(num_threads); 52 | for (int i = 0; i < num_threads; ++i) { 53 | threads.emplace_back( 54 | [&word_counts](std::vector&& lines) { 55 | for (auto& line : lines) { 56 | std::replace_if(line.begin(), line.end(), [](char c) -> bool { return !std::isalnum(c); }, ' '); 57 | std::istringstream iss(line); 58 | std::string word; 59 | while (iss >> word) { 60 | // use lazy_emplace to modify the map while the mutex is locked 61 | word_counts.lazy_emplace_l(word, 62 | [&](Map::value_type& p) { ++p.second; }, // called only when key was already present 63 | [&](const Map::constructor& ctor) // construct value_type in place when key not present 64 | { ctor(std::move(word), 1); } ); 65 | } 66 | } 67 | }, 68 | std::move(lines_array[i])); 69 | } 70 | 71 | for (auto& thread : threads) 72 | thread.join(); 73 | 74 | // print one word used at each frequency 75 | // ------------------------------------- 76 | phmap::btree_map result; 77 | for (const auto& pair : word_counts) 78 | result[pair.second] = pair.first; 79 | 80 | for (const auto& p : result) 81 | std::cout << p.first << ": " << p.second << std::endl; 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /examples/pmr.cc: -------------------------------------------------------------------------------- 1 | #if __has_include() 2 | #include 3 | namespace std 4 | { 5 | namespace pmr = experimental::pmr; 6 | } 7 | #elif __has_include() 8 | #include 9 | #elif 10 | #error is missing 11 | #endif 12 | 13 | #include 14 | 15 | struct MyStruct 16 | { 17 | template 18 | using ParallelFlatHashMap = phmap::parallel_flat_hash_map, std::equal_to, 19 | std::pmr::polymorphic_allocator>>; 20 | 21 | ParallelFlatHashMap hashMap; 22 | 23 | // No compile errors 24 | MyStruct() 25 | { 26 | } 27 | 28 | // Compile errors 29 | MyStruct(std::pmr::memory_resource* memoryResource = std::pmr::get_default_resource()) 30 | : hashMap(memoryResource) 31 | { 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /html/Makefile: -------------------------------------------------------------------------------- 1 | PANDOC = stack exec pandoc -- 2 | MATHJAX = "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" 3 | FLAGS = --standalone --toc --toc-depth=2 --mathjax=$(MATHJAX) --highlight-style pygments 4 | PNG_IMAGES = $(patsubst %.pdf,%.png,$(wildcard img/*.pdf)) 5 | IFORMAT = -f gfm 6 | FILTER = includes.exe 7 | FILTER_OPT = --filter=${FILTER} # includes.hs 8 | 9 | ############################### html 10 | STYLE = css/style.css 11 | TEMPLATE_HTML = template.html 12 | HTML_OPT = -c ${STYLE} --template ${TEMPLATE_HTML} -t html 13 | PGTITLE = --metadata pagetitle="The Parallel Hashmap" 14 | 15 | ############################### pdf 16 | TEMPLATE_TEX = template.latex 17 | TEX_OPT = --template $(TEMPLATE_TEX) --pdf-engine=xelatex 18 | 19 | ############################### epub 20 | EPUB_COVER = --epub-cover-image=img/cover-kindle.jpg 21 | 22 | 23 | SRC = parallel_hashmap.md 24 | 25 | OBJ = $(SRC:.md=.html) 26 | 27 | all: html 28 | 29 | includes.exe: includes.hs 30 | stack exec ghc -- -o $@ -no-keep-hi-files -no-keep-o-files includes.hs 31 | 32 | html: parallel_hashmap.md $(FILTER) ${TEMPLATE_HTML} ${STYLE} 33 | $(PANDOC) ${FILTER_OPT} ${IFORMAT} ${HTML_OPT} $(FLAGS) ${PGTITLE} -o ../index.html parallel_hashmap.md 34 | 35 | %.pdf: %.md $(FILTER) ${TEMPLATE_TEX} 36 | $(PANDOC) ${FILTER_OPT} ${IFORMAT} ${TEX_OPT} $(FLAGS) -o $@ $< 37 | 38 | pdf: $(FILTER) ${TEMPLATE_TEX} 39 | rm -f parallel_hashmap.pdf; $(PANDOC) ${FILTER_OPT} ${IFORMAT} ${TEX_OPT} $(FLAGS) -o parallel_hashmap.pdf title.md $(SRC) 40 | 41 | native: 42 | $(PANDOC) -s -t native $(SRC) 43 | 44 | native_filt: $(FILTER) 45 | $(PANDOC) ${FILTER_OPT} -s -t native $(SRC) 46 | 47 | clean: 48 | -rm -f *.html *.pdf cppi.epub 49 | -------------------------------------------------------------------------------- /html/bench_results/martinus_mod/index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Benchmark Results

6 | 7 | insert 100m values in map

8 | 9 | Lookup 100m ints, all present | Lookup 100m ints, few present

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /html/css/colors.css: -------------------------------------------------------------------------------- 1 | /*** 2 | 3 | colors.css v2.0.0 4 | http://clrs.cc 5 | @mrmrs 6 | MIT License 7 | 8 | ***/ 9 | /* 10 | 11 | SKINS 12 | - Backgrounds 13 | - Colors 14 | - Border colors 15 | - SVG fills 16 | - SVG Strokes 17 | 18 | */ 19 | /* Backgrounds */ 20 | .bg-navy { 21 | background-color: #001F3F; } 22 | 23 | .bg-blue { 24 | background-color: #0074D9; } 25 | 26 | .bg-aqua { 27 | background-color: #7FDBFF; } 28 | 29 | .bg-teal { 30 | background-color: #39CCCC; } 31 | 32 | .bg-olive { 33 | background-color: #3D9970; } 34 | 35 | .bg-green { 36 | background-color: #2ECC40; } 37 | 38 | .bg-lime { 39 | background-color: #01FF70; } 40 | 41 | .bg-yellow { 42 | background-color: #FFDC00; } 43 | 44 | .bg-orange { 45 | background-color: #FF851B; } 46 | 47 | .bg-red { 48 | background-color: #FF4136; } 49 | 50 | .bg-fuchsia { 51 | background-color: #F012BE; } 52 | 53 | .bg-purple { 54 | background-color: #B10DC9; } 55 | 56 | .bg-maroon { 57 | background-color: #85144B; } 58 | 59 | .bg-white { 60 | background-color: #fff; } 61 | 62 | .bg-gray { 63 | background-color: #aaa; } 64 | 65 | .bg-silver { 66 | background-color: #ddd; } 67 | 68 | .bg-black { 69 | background-color: #111; } 70 | 71 | /* Colors */ 72 | .navy { 73 | color: #001F3F; } 74 | 75 | .blue { 76 | color: #0074D9; } 77 | 78 | .aqua { 79 | color: #7FDBFF; } 80 | 81 | .teal { 82 | color: #39CCCC; } 83 | 84 | .olive { 85 | color: #3D9970; } 86 | 87 | .green { 88 | color: #2ECC40; } 89 | 90 | .lime { 91 | color: #01FF70; } 92 | 93 | .yellow { 94 | color: #FFDC00; } 95 | 96 | .orange { 97 | color: #FF851B; } 98 | 99 | .red { 100 | color: #FF4136; } 101 | 102 | .fuchsia { 103 | color: #F012BE; } 104 | 105 | .purple { 106 | color: #B10DC9; } 107 | 108 | .maroon { 109 | color: #85144B; } 110 | 111 | .white { 112 | color: #fff; } 113 | 114 | .silver { 115 | color: #ddd; } 116 | 117 | .gray { 118 | color: #aaa; } 119 | 120 | .black { 121 | color: #111; } 122 | 123 | /* Border colors 124 | 125 | Use with another border utility that sets border-width and style 126 | i.e .border { border-width: 1px; border-style: solid; } 127 | */ 128 | .border--navy { 129 | border-color: #001F3F; } 130 | 131 | .border--blue { 132 | border-color: #0074D9; } 133 | 134 | .border--aqua { 135 | border-color: #7FDBFF; } 136 | 137 | .border--teal { 138 | border-color: #39CCCC; } 139 | 140 | .border--olive { 141 | border-color: #3D9970; } 142 | 143 | .border--green { 144 | border-color: #2ECC40; } 145 | 146 | .border--lime { 147 | border-color: #01FF70; } 148 | 149 | .border--yellow { 150 | border-color: #FFDC00; } 151 | 152 | .border--orange { 153 | border-color: #FF851B; } 154 | 155 | .border--red { 156 | border-color: #FF4136; } 157 | 158 | .border--fuchsia { 159 | border-color: #F012BE; } 160 | 161 | .border--purple { 162 | border-color: #B10DC9; } 163 | 164 | .border--maroon { 165 | border-color: #85144B; } 166 | 167 | .border--white { 168 | border-color: #fff; } 169 | 170 | .border--gray { 171 | border-color: #aaa; } 172 | 173 | .border--silver { 174 | border-color: #ddd; } 175 | 176 | .border--black { 177 | border-color: #111; } 178 | 179 | /* Fills for SVG */ 180 | .fill-navy { 181 | fill: #001F3F; } 182 | 183 | .fill-blue { 184 | fill: #0074D9; } 185 | 186 | .fill-aqua { 187 | fill: #7FDBFF; } 188 | 189 | .fill-teal { 190 | fill: #39CCCC; } 191 | 192 | .fill-olive { 193 | fill: #3D9970; } 194 | 195 | .fill-green { 196 | fill: #2ECC40; } 197 | 198 | .fill-lime { 199 | fill: #01FF70; } 200 | 201 | .fill-yellow { 202 | fill: #FFDC00; } 203 | 204 | .fill-orange { 205 | fill: #FF851B; } 206 | 207 | .fill-red { 208 | fill: #FF4136; } 209 | 210 | .fill-fuchsia { 211 | fill: #F012BE; } 212 | 213 | .fill-purple { 214 | fill: #B10DC9; } 215 | 216 | .fill-maroon { 217 | fill: #85144B; } 218 | 219 | .fill-white { 220 | fill: #fff; } 221 | 222 | .fill-gray { 223 | fill: #aaa; } 224 | 225 | .fill-silver { 226 | fill: #ddd; } 227 | 228 | .fill-black { 229 | fill: #111; } 230 | 231 | /* Strokes for SVG */ 232 | .stroke-navy { 233 | stroke: #001F3F; } 234 | 235 | .stroke-blue { 236 | stroke: #0074D9; } 237 | 238 | .stroke-aqua { 239 | stroke: #7FDBFF; } 240 | 241 | .stroke-teal { 242 | stroke: #39CCCC; } 243 | 244 | .stroke-olive { 245 | stroke: #3D9970; } 246 | 247 | .stroke-green { 248 | stroke: #2ECC40; } 249 | 250 | .stroke-lime { 251 | stroke: #01FF70; } 252 | 253 | .stroke-yellow { 254 | stroke: #FFDC00; } 255 | 256 | .stroke-orange { 257 | stroke: #FF851B; } 258 | 259 | .stroke-red { 260 | stroke: #FF4136; } 261 | 262 | .stroke-fuchsia { 263 | stroke: #F012BE; } 264 | 265 | .stroke-purple { 266 | stroke: #B10DC9; } 267 | 268 | .stroke-maroon { 269 | stroke: #85144B; } 270 | 271 | .stroke-white { 272 | stroke: #fff; } 273 | 274 | .stroke-gray { 275 | stroke: #aaa; } 276 | 277 | .stroke-silver { 278 | stroke: #ddd; } 279 | 280 | .stroke-black { 281 | stroke: #111; } 282 | 283 | /* PRETTIER LINKS */ 284 | a { 285 | text-decoration: none; 286 | -webkit-transition: color .3s ease-in-out; 287 | transition: color .3s ease-in-out; } 288 | 289 | a:link { 290 | -webkit-transition: color .3s ease-in-out; 291 | transition: color .3s ease-in-out; } 292 | 293 | a:visited { } 294 | 295 | a:hover { 296 | color: #001F3F; 297 | -webkit-transition: color .3s ease-in-out; 298 | transition: color .3s ease-in-out; } 299 | 300 | a:active { 301 | -webkit-transition: color .3s ease-in-out; 302 | transition: color .3s ease-in-out; } 303 | -------------------------------------------------------------------------------- /html/css/style.css: -------------------------------------------------------------------------------- 1 | /* main stylesheet */ 2 | 3 | @import url(http://fonts.googleapis.com/css?family=Signika); 4 | 5 | html { 6 | overflow-y: scroll; 7 | } 8 | 9 | body { 10 | font-size: 15px; 11 | font-family: HelveticaNeue, 'Helvetica Neue', Helvetica, Arial, sans-serif; 12 | color: #332; 13 | } 14 | 15 | h1, h2, h3, h4, h5 { 16 | color: #332; 17 | font-family: "Signika"; 18 | font-weight: 400; 19 | font-size: 1.4em; 20 | line-height: 1.1; 21 | margin-top: 30px; 22 | } 23 | 24 | pre code { 25 | font: 14px/19px Inconsolata, Monaco,"Lucida Console",Terminal,"Courier New",Courier; 26 | } 27 | 28 | .figure { 29 | text-align: center; 30 | } 31 | 32 | .small .figure img { 33 | height: 200px; 34 | } 35 | 36 | .pagetitle .figure { 37 | text-align: left !important; 38 | } 39 | 40 | .pagetitle .figure img { 41 | height: 36px; 42 | } 43 | 44 | table{ 45 | background:#fff; 46 | border:1px solid #ccc; 47 | border-width:2px; 48 | border-collapse:collapse; 49 | margin:5px 0 10px; 50 | 51 | margin-top: 20px; 52 | margin-bottom: 20px; 53 | } 54 | 55 | th, td{ 56 | border:1px solid #ccc; 57 | padding:3px 10px; 58 | text-align:left; 59 | vertical-align:top; 60 | } 61 | 62 | tr.even td{ 63 | background:#f7f7f7; 64 | } 65 | 66 | th{ 67 | background:#edeff0; 68 | } 69 | 70 | td code { 71 | border: 0px; 72 | } 73 | 74 | img { 75 | max-width: 100%; 76 | height: auto; 77 | } 78 | 79 | hr { 80 | border: 0px; 81 | height: 0; 82 | border-bottom: 1px solid #ccc; 83 | margin-bottom: 100px; 84 | } 85 | 86 | /* Logo */ 87 | 88 | .logo { 89 | text-align: center; 90 | } 91 | 92 | .tagline { 93 | font-family: Georgia; 94 | font-size: 18px; 95 | font-style: italic; 96 | line-height: 1.45; 97 | color: #383838; 98 | } 99 | 100 | .author { 101 | } 102 | 103 | .halfbreak { 104 | padding-bottom: 100px; 105 | } 106 | 107 | .break { 108 | padding-bottom: 200px; 109 | } 110 | 111 | /* TOC Links */ 112 | 113 | a { 114 | color: #111111; 115 | text-decoration: none; 116 | } 117 | 118 | .body li a { 119 | text-decoration: underline; 120 | } 121 | 122 | /* Math */ 123 | 124 | .MathJax_Display { 125 | padding-top: 20px; 126 | padding-bottom: 20px; 127 | } 128 | 129 | /* Body Links */ 130 | 131 | p a { 132 | text-decoration: underline; 133 | } 134 | 135 | li code, p code { 136 | font-size: 12px; 137 | border: 1px solid #ccc; 138 | margin-left: 3px; 139 | margin-right: 3px; 140 | padding-left: 2px; 141 | padding-right: 2px; 142 | } 143 | 144 | /* */ 145 | 146 | .center { 147 | text-align: center; 148 | } 149 | 150 | .bigger img { 151 | width: 120%; 152 | height: 120%; 153 | } 154 | 155 | pre { 156 | font-size: 0.9em; 157 | 158 | margin-bottom: 18px; 159 | margin-top: 18px; 160 | 161 | border-left: 1px solid #ccc; 162 | 163 | } 164 | 165 | h1 { 166 | margin-top: 0px; 167 | } 168 | 169 | .annotation { 170 | font-size: 10pt; 171 | } 172 | 173 | .annotation pre { 174 | display: block; 175 | margin: 0; 176 | padding: 7px 10px; 177 | overflow-x: auto; 178 | } 179 | 180 | .annotation.span2 { 181 | /* Override bootstrap */ 182 | margin-left: 0px !important; 183 | margin-top: 18px !important; 184 | } 185 | 186 | .annotation pre code { 187 | border: 0; 188 | padding: 0; 189 | background: transparent; 190 | } 191 | 192 | blockquote { 193 | border-left: 1px solid #ccc; 194 | font-family: Georgia, serif; 195 | font-size: 14px; 196 | font-style: italic; 197 | margin: 0.25em 0; 198 | padding-left: 10px; 199 | line-height: 1.45; 200 | color: #383838; 201 | left: 20px; 202 | } 203 | 204 | 205 | blockquote cite { 206 | color: #999999; 207 | font-size: 14px; 208 | display: block; 209 | margin-top: 5px; 210 | } 211 | 212 | ul.sections { 213 | list-style: none; 214 | padding:0 0 5px 0; 215 | margin:0; 216 | } 217 | 218 | code.sourceCode { 219 | padding: 0; 220 | background: inherit; 221 | } 222 | 223 | pre.sourceCode { 224 | padding: 10px; 225 | } 226 | 227 | ul.sections > li > div { 228 | -moz-box-sizing: border-box; /* firefox */ 229 | -ms-box-sizing: border-box; /* ie */ 230 | -webkit-box-sizing: border-box; /* webkit */ 231 | -khtml-box-sizing: border-box; /* konqueror */ 232 | box-sizing: border-box; /* css3 */ 233 | } 234 | 235 | 236 | /* Make the naviation centered and larger on small screens */ 237 | /*---------------------- (> 481px) ---------------------*/ 238 | 239 | @media only screen and (max-width: 481px) { 240 | 241 | } 242 | 243 | @media only screen and (min-width: 1025px) { 244 | body { 245 | padding: 10px; 246 | } 247 | 248 | .side { 249 | position: fixed; 250 | width: 120px !important; 251 | margin-left: 0px; 252 | z-index: 1000; 253 | } 254 | 255 | .side ul ul { 256 | display: none; 257 | } 258 | 259 | .side ul ul.active { 260 | display: block; 261 | } 262 | 263 | .side .active { 264 | font-weight: bold; 265 | } 266 | 267 | .body { 268 | margin-left: 120px !important; 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /html/diagrams/closed_hashing: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | +----------------+ 5 | |"(keyi, valuei)"| "(key, value) pairs are stored directly" 6 | +----+-----------+ "into the array (no pointers)" 7 | | +--------+---------------------+ 8 | | | | | 9 | | | | | 10 | | | | | 11 | | | | | 12 | | | | | 13 | | +--------+---------------------+ 14 | +---------------> | keyi | valuei | 15 | hasher(keyi) +--------+---------------------+ 16 | | | | 17 | | | | 18 | | | | 19 | +--------+---------------------+ 20 | | | | 21 | +--------+---------------------+ 22 | | | | 23 | | | | 24 | | | | 25 | +--------+---------------------+ 26 | 27 | absl::flat_hash_map 28 | 29 | -------------------------------------------------------------------------------- /html/diagrams/closed_hashing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | (keyi, valuei) 101 | 102 | 103 | 104 | 105 | hasher(keyi) 106 | 107 | 108 | 109 | 110 | absl::flat_hash_map 111 | 112 | 113 | 114 | 115 | (key, value) pairs are stored directly 116 | 117 | 118 | 119 | 120 | into the array (no pointers) 121 | 122 | 123 | 124 | 125 | keyi 126 | 127 | 128 | 129 | 130 | valuei 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /html/diagrams/index_computation: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | +----------------+ 5 | |"(keyi, valuei)"| 6 | +------+---------+ 7 | | 8 | | hasher(keyi) "Parallel Hash Map" 9 | v "(with 8 submaps)" 10 | +--------+-------------+ +----------------+ 11 | | h=0x7d84ea13707f4657 | | submap0 | 12 | +---------+------------+ +----------------+ 13 | | | submap1 | 14 | | "(h ^ (h >> 3)) & 0x7" +----------------+ 15 | v | submap2 | 16 | +----+----+ +----------------+ 17 | |"idx = 5"| | submap3 | 18 | +----+----+ +----------------+ 19 | | | submap4 | 20 | | +----------------+ 21 | +------------------------------->| submap5 | 22 | +----------------+ 23 | | submap6 | 24 | +----------------+ 25 | | submap7 | 26 | +----------------+ 27 | 28 | "parallel_hash_map with 8 submaps, each submap is an absl::flat_hash_map" 29 | 30 | -------------------------------------------------------------------------------- /html/diagrams/index_computation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | parallel_hash_map with 8 submaps, each submap is an absl::flat_hash_map 119 | 120 | 121 | 122 | 123 | h=0x7d84ea13707f4657 124 | 125 | 126 | 127 | 128 | (keyi, valuei) 129 | 130 | 131 | 132 | 133 | idx = 5 134 | 135 | 136 | 137 | 138 | (h ^ (h >> 3)) & 0x7 139 | 140 | 141 | 142 | 143 | hasher(keyi) 144 | 145 | 146 | 147 | 148 | Parallel Hash Map 149 | 150 | 151 | 152 | 153 | (with 8 submaps) 154 | 155 | 156 | 157 | 158 | submap0 159 | 160 | 161 | 162 | 163 | submap1 164 | 165 | 166 | 167 | 168 | submap2 169 | 170 | 171 | 172 | 173 | submap3 174 | 175 | 176 | 177 | 178 | submap4 179 | 180 | 181 | 182 | 183 | submap5 184 | 185 | 186 | 187 | 188 | submap6 189 | 190 | 191 | 192 | 193 | submap7 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /html/img/closed_hashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/closed_hashing.png -------------------------------------------------------------------------------- /html/img/flat_mem_usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_mem_usage.gif -------------------------------------------------------------------------------- /html/img/flat_mem_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_mem_usage.png -------------------------------------------------------------------------------- /html/img/flat_par_mutex_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_par_mutex_4.PNG -------------------------------------------------------------------------------- /html/img/flat_par_mutex_5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_par_mutex_5.PNG -------------------------------------------------------------------------------- /html/img/flat_par_mutex_5_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_par_mutex_5_speed.PNG -------------------------------------------------------------------------------- /html/img/flat_par_mutex_6_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_par_mutex_6_speed.PNG -------------------------------------------------------------------------------- /html/img/flat_par_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_par_speed.PNG -------------------------------------------------------------------------------- /html/img/flat_peak.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_peak.gif -------------------------------------------------------------------------------- /html/img/flat_peak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/flat_peak.png -------------------------------------------------------------------------------- /html/img/hashtable_benchmarks.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/hashtable_benchmarks.PNG -------------------------------------------------------------------------------- /html/img/idx_computation_cost.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/idx_computation_cost.PNG -------------------------------------------------------------------------------- /html/img/index_computation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/index_computation.png -------------------------------------------------------------------------------- /html/img/lock_various_sizes.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/lock_various_sizes.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_both.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_both.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_both_run2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_both_run2.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_mem.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_mem.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_mem_run2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_mem_run2.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_mem_run2_zoomed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_mem_run2_zoomed.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_mem_zoomed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_mem_zoomed.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_speed.PNG -------------------------------------------------------------------------------- /html/img/mt_stl_flat_par_speed_run2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/mt_stl_flat_par_speed_run2.PNG -------------------------------------------------------------------------------- /html/img/no_preselection.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/no_preselection.PNG -------------------------------------------------------------------------------- /html/img/node_mem_usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/node_mem_usage.gif -------------------------------------------------------------------------------- /html/img/node_mem_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/node_mem_usage.png -------------------------------------------------------------------------------- /html/img/node_peak.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/node_peak.gif -------------------------------------------------------------------------------- /html/img/node_peak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/node_peak.png -------------------------------------------------------------------------------- /html/img/par_align_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/par_align_test.png -------------------------------------------------------------------------------- /html/img/par_mt_memory.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/par_mt_memory.PNG -------------------------------------------------------------------------------- /html/img/par_mt_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/par_mt_speed.PNG -------------------------------------------------------------------------------- /html/img/parallel_flat_peak.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/parallel_flat_peak.gif -------------------------------------------------------------------------------- /html/img/parallel_flat_peak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/parallel_flat_peak.png -------------------------------------------------------------------------------- /html/img/parallel_node_peak.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/parallel_node_peak.gif -------------------------------------------------------------------------------- /html/img/parallel_node_peak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/parallel_node_peak.png -------------------------------------------------------------------------------- /html/img/phash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/phash.png -------------------------------------------------------------------------------- /html/img/phmap_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/phmap_logo.png -------------------------------------------------------------------------------- /html/img/spp_flat_par_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/spp_flat_par_both.png -------------------------------------------------------------------------------- /html/img/stl_flat_both.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_both.PNG -------------------------------------------------------------------------------- /html/img/stl_flat_mem.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_mem.PNG -------------------------------------------------------------------------------- /html/img/stl_flat_par_both.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_par_both.PNG -------------------------------------------------------------------------------- /html/img/stl_flat_par_mem.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_par_mem.PNG -------------------------------------------------------------------------------- /html/img/stl_flat_par_mem_zoomed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_par_mem_zoomed.PNG -------------------------------------------------------------------------------- /html/img/stl_flat_par_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_par_speed.PNG -------------------------------------------------------------------------------- /html/img/stl_flat_speed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/img/stl_flat_speed.PNG -------------------------------------------------------------------------------- /html/includes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | import Text.Read 4 | import Control.Monad.State 5 | import Control.Monad 6 | import Text.Pandoc 7 | import Data.Monoid 8 | import Control.Applicative 9 | 10 | import Text.Pandoc.JSON 11 | import Text.Pandoc.Walk 12 | 13 | slice :: Int -> Int -> [a] -> [a] 14 | slice from to xs = take (to - from + 1) (drop from xs) 15 | 16 | doSlice :: Block -> IO Block 17 | doSlice cb@(CodeBlock (id, classes, namevals) contents) = do 18 | res <- return $ do 19 | upper <- readMaybe =<< lookup "upper" namevals 20 | lower <- readMaybe =<< lookup "lower" namevals 21 | file <- lookup "slice" namevals 22 | return (upper, lower, file) 23 | 24 | case res of 25 | Nothing -> return cb 26 | Just (upper, lower, f) -> do 27 | contents <- readFile f 28 | let lns = unlines $ slice lower upper (lines contents) 29 | return (CodeBlock (id, classes, namevals) lns) 30 | doSlice x = return x 31 | 32 | doInclude :: Block -> IO Block 33 | doInclude cb@(CodeBlock (id, classes, namevals) contents) = 34 | case lookup "include" namevals of 35 | Just f -> return . (CodeBlock (id, classes, namevals)) =<< readFile f 36 | Nothing -> return cb 37 | doInclude x = return x 38 | 39 | doHtml :: Block -> IO Block 40 | doHtml cb@(CodeBlock (id, classes, namevals) contents) = 41 | case lookup "literal" namevals of 42 | Just f -> return . (RawBlock "html") =<< readFile f 43 | Nothing -> return cb 44 | doHtml x = return x 45 | 46 | injectLatexMacros :: Maybe Format -> Pandoc -> IO Pandoc 47 | injectLatexMacros (Just fmt) p = do 48 | macros <- readFile "latex_macros" 49 | let block = 50 | case fmt of 51 | Format "html" -> 52 | Div ("",[],[("style","display:none")]) . (:[]) 53 | . Para . (:[]) . Math DisplayMath $ macros 54 | Format "latex" -> RawBlock "latex" macros 55 | Format "epub" -> RawBlock "latex" macros 56 | _ -> RawBlock "latex" macros 57 | return (Pandoc nullMeta [block] <> p) 58 | injectLatexMacros _ _ = return mempty 59 | 60 | main :: IO () 61 | main = toJSONFilter 62 | ((\fmt -> injectLatexMacros fmt 63 | >=> walkM doInclude 64 | >=> walkM doSlice 65 | >=> walkM doHtml) :: Maybe Format -> Pandoc -> IO Pandoc) 66 | -------------------------------------------------------------------------------- /html/latex_macros: -------------------------------------------------------------------------------- 1 | \newcommand{\andalso}{\quad\quad} 2 | \newcommand{\infabbrev}[2]{\infax{#1 \quad\eqdef\quad #2}} 3 | \newcommand{\infrule}[2]{\displaystyle \dfrac{#1}{#2}} 4 | \newcommand{\ar}{\rightarrow} 5 | \newcommand{\Int}{\mathtt{Int}} 6 | \newcommand{\Bool}{\mathtt{Bool}} 7 | \newcommand{\becomes}{\Downarrow} 8 | \newcommand{\trule}[1]{(\textbf{#1})} 9 | \newcommand{\FV}[1]{\mathtt{fv}(#1)} 10 | \newcommand{\FTV}[1]{\mathtt{ftv}(#1)} 11 | \newcommand{\BV}[1]{\mathtt{bv}(#1)} 12 | \newcommand{\compiles}[1]{\text{C}\llbracket{#1}\rrbracket} 13 | \newcommand{\exec}[1]{\text{E}\llbracket{#1}\rrbracket} 14 | \renewcommand{\t}[1]{\mathtt{#1}} 15 | \newcommand{\ite}[3]{\text{if }#1\text{ then }#2\text{ else }#3} 16 | -------------------------------------------------------------------------------- /html/parallel_hashmap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/896f1a03e429c45d9fe9638e892fc1da73befadd/html/parallel_hashmap.pdf -------------------------------------------------------------------------------- /html/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Parallel Hashmap (Gregory Popovitch) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | $if(highlighting-css)$ 17 | 20 | $endif$ 21 | $for(css)$ 22 | 23 | $endfor$ 24 | $if(math)$ 25 | $if(html5)$ 26 | $else$ 27 | $math$ 28 | $endif$ 29 | $endif$ 30 | $for(header-includes)$ 31 | $header-includes$ 32 | $endfor$ 33 | 34 | 35 | 36 | 37 |

38 | 39 |
40 | 41 |
42 | 43 | $body$ 44 |
45 |
46 | 47 |
48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /parallel_hashmap/meminfo.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 | uint64_t GetTotalMemoryUsed(); 33 | uint64_t GetProcessMemoryUsed(); 34 | uint64_t GetPhysicalMemory(); 35 | 36 | uint64_t GetSystemMemory() 37 | { 38 | #ifdef SPP_WIN 39 | MEMORYSTATUSEX memInfo; 40 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 41 | GlobalMemoryStatusEx(&memInfo); 42 | return static_cast(memInfo.ullTotalPageFile); 43 | #elif defined(__linux__) 44 | struct sysinfo memInfo; 45 | sysinfo (&memInfo); 46 | auto totalVirtualMem = memInfo.totalram; 47 | 48 | totalVirtualMem += memInfo.totalswap; 49 | totalVirtualMem *= memInfo.mem_unit; 50 | return static_cast(totalVirtualMem); 51 | #elif defined(__FreeBSD__) 52 | kvm_t *kd; 53 | u_int pageCnt; 54 | size_t pageCntLen = sizeof(pageCnt); 55 | u_int pageSize; 56 | struct kvm_swap kswap; 57 | uint64_t totalVirtualMem; 58 | 59 | pageSize = static_cast(getpagesize()); 60 | 61 | sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); 62 | totalVirtualMem = pageCnt * pageSize; 63 | 64 | kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); 65 | kvm_getswapinfo(kd, &kswap, 1, 0); 66 | kvm_close(kd); 67 | totalVirtualMem += kswap.ksw_total * pageSize; 68 | 69 | return totalVirtualMem; 70 | #else 71 | return 0; 72 | #endif 73 | } 74 | 75 | uint64_t GetTotalMemoryUsed() 76 | { 77 | #ifdef SPP_WIN 78 | MEMORYSTATUSEX memInfo; 79 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 80 | GlobalMemoryStatusEx(&memInfo); 81 | return static_cast(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile); 82 | #elif defined(__linux__) 83 | struct sysinfo memInfo; 84 | sysinfo(&memInfo); 85 | auto virtualMemUsed = memInfo.totalram - memInfo.freeram; 86 | 87 | virtualMemUsed += memInfo.totalswap - memInfo.freeswap; 88 | virtualMemUsed *= memInfo.mem_unit; 89 | 90 | return static_cast(virtualMemUsed); 91 | #elif defined(__FreeBSD__) 92 | kvm_t *kd; 93 | u_int pageSize; 94 | u_int pageCnt, freeCnt; 95 | size_t pageCntLen = sizeof(pageCnt); 96 | size_t freeCntLen = sizeof(freeCnt); 97 | struct kvm_swap kswap; 98 | uint64_t virtualMemUsed; 99 | 100 | pageSize = static_cast(getpagesize()); 101 | 102 | sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0); 103 | sysctlbyname("vm.stats.vm.v_free_count", &freeCnt, &freeCntLen, NULL, 0); 104 | virtualMemUsed = (pageCnt - freeCnt) * pageSize; 105 | 106 | kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); 107 | kvm_getswapinfo(kd, &kswap, 1, 0); 108 | kvm_close(kd); 109 | virtualMemUsed += kswap.ksw_used * pageSize; 110 | 111 | return virtualMemUsed; 112 | #else 113 | return 0; 114 | #endif 115 | } 116 | 117 | uint64_t GetProcessMemoryUsed() 118 | { 119 | #ifdef SPP_WIN 120 | PROCESS_MEMORY_COUNTERS_EX pmc; 121 | GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); 122 | return static_cast(pmc.PrivateUsage); 123 | #elif defined(__linux__) 124 | auto parseLine = 125 | [](char* line)->int 126 | { 127 | auto i = strlen(line); 128 | 129 | while(*line < '0' || *line > '9') 130 | { 131 | line++; 132 | } 133 | 134 | line[i-3] = '\0'; 135 | i = atoi(line); 136 | return i; 137 | }; 138 | 139 | auto file = fopen("/proc/self/status", "r"); 140 | auto result = -1; 141 | char line[128]; 142 | 143 | while(fgets(line, 128, file) != nullptr) 144 | { 145 | if(strncmp(line, "VmSize:", 7) == 0) 146 | { 147 | result = parseLine(line); 148 | break; 149 | } 150 | } 151 | 152 | fclose(file); 153 | return static_cast(result) * 1024; 154 | #elif defined(__FreeBSD__) 155 | struct kinfo_proc info; 156 | size_t infoLen = sizeof(info); 157 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; 158 | 159 | sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &infoLen, NULL, 0); 160 | return static_cast(info.ki_rssize * getpagesize()); 161 | #else 162 | return 0; 163 | #endif 164 | } 165 | 166 | uint64_t GetPhysicalMemory() 167 | { 168 | #ifdef SPP_WIN 169 | MEMORYSTATUSEX memInfo; 170 | memInfo.dwLength = sizeof(MEMORYSTATUSEX); 171 | GlobalMemoryStatusEx(&memInfo); 172 | return static_cast(memInfo.ullTotalPhys); 173 | #elif defined(__linux__) 174 | struct sysinfo memInfo; 175 | sysinfo(&memInfo); 176 | 177 | auto totalPhysMem = memInfo.totalram; 178 | 179 | totalPhysMem *= memInfo.mem_unit; 180 | return static_cast(totalPhysMem); 181 | #elif defined(__FreeBSD__) 182 | u_long physMem; 183 | size_t physMemLen = sizeof(physMem); 184 | int mib[] = { CTL_HW, HW_PHYSMEM }; 185 | 186 | sysctl(mib, sizeof(mib) / sizeof(*mib), &physMem, &physMemLen, NULL, 0); 187 | return physMem; 188 | #else 189 | return 0; 190 | #endif 191 | } 192 | 193 | } 194 | 195 | #endif // spp_memory_h_guard 196 | -------------------------------------------------------------------------------- /phmap_gdb.py: -------------------------------------------------------------------------------- 1 | # Python GDB formatters for parallel-hashmap 2 | # tested with GCC 10.2 / GDB 9.2 3 | # to install it, ensure the script location is in the Python path 4 | # and type the following command (or put it in $HOME/.gdbinit): 5 | 6 | # python 7 | # import phmap_gdb 8 | # end 9 | 10 | 11 | import gdb.printing 12 | 13 | 14 | def counter(): 15 | i = 0 16 | while(True): 17 | yield str(i) 18 | i += 1 19 | 20 | 21 | def slot_iterator(base_obj): 22 | index = -1 23 | n_items = 0 24 | size = int(base_obj["size_"]) 25 | while n_items < size: 26 | index += 1 27 | if int(base_obj["ctrl_"][index]) < 0: 28 | continue 29 | 30 | n_items += 1 31 | yield base_obj["slots_"][index] 32 | 33 | 34 | def parallel_slot_iterator(base_obj): 35 | array = base_obj["sets_"] 36 | array_len = int(array.type.template_argument(1)) 37 | for index in range(array_len): 38 | obj = array["_M_elems"][index]["set_"] 39 | yield from slot_iterator(obj) 40 | 41 | 42 | def flat_map_iterator(name, item): 43 | yield (next(name), item["value"]["first"]) 44 | yield (next(name), item["value"]["second"]) 45 | 46 | 47 | def flat_set_iterator(name, item): 48 | yield (next(name), item) 49 | 50 | 51 | def node_map_iterator(name, item): 52 | yield (next(name), item.dereference()["first"]) 53 | yield (next(name), item.dereference()["second"]) 54 | 55 | 56 | def node_set_iterator(name, item): 57 | yield (next(name), item.dereference()) 58 | 59 | 60 | def traverse(iterator, slot_type_iterator): 61 | name = counter() 62 | for item in iterator: 63 | yield from slot_type_iterator(name, item) 64 | 65 | 66 | def parallel_size(parallel_hash_obj): 67 | array = parallel_hash_obj["sets_"] 68 | array_len = int(array.type.template_argument(1)) 69 | size = 0 70 | for index in range(array_len): 71 | size += array["_M_elems"][index]["set_"]["size_"] 72 | 73 | return size 74 | 75 | 76 | class FlatMapPrinter: 77 | def __init__(self, val): 78 | self.val = val 79 | 80 | def children(self): 81 | return traverse(slot_iterator(self.val), flat_map_iterator) 82 | 83 | def to_string(self): 84 | return f"phmap::flat_hash_map with {int(self.val['size_'])} elements" 85 | 86 | def display_hint(self): 87 | return "map" 88 | 89 | 90 | class FlatSetPrinter: 91 | def __init__(self, val): 92 | self.val = val 93 | 94 | def children(self): 95 | return traverse(slot_iterator(self.val), flat_set_iterator) 96 | 97 | def to_string(self): 98 | return f"phmap::flat_hash_set with {int(self.val['size_'])} elements" 99 | 100 | def display_hint(self): 101 | return "array" 102 | 103 | 104 | class NodeMapPrinter: 105 | def __init__(self, val): 106 | self.val = val 107 | 108 | def children(self): 109 | return traverse(slot_iterator(self.val), node_map_iterator) 110 | 111 | def to_string(self): 112 | return f"phmap::node_hash_map with {int(self.val['size_'])} elements" 113 | 114 | def display_hint(self): 115 | return "map" 116 | 117 | 118 | class NodeSetPrinter: 119 | def __init__(self, val): 120 | self.val = val 121 | 122 | def children(self): 123 | return traverse(slot_iterator(self.val), node_set_iterator) 124 | 125 | def to_string(self): 126 | return f"phmap::node_hash_set with {int(self.val['size_'])} elements" 127 | 128 | def display_hint(self): 129 | return "array" 130 | 131 | 132 | class ParallelFlatMapPrinter: 133 | def __init__(self, val): 134 | self.val = val 135 | 136 | def children(self): 137 | return traverse(parallel_slot_iterator(self.val), flat_map_iterator) 138 | 139 | def to_string(self): 140 | return f"phmap::parallel_flat_hash_map with {parallel_size(self.val)} elements" 141 | 142 | def display_hint(self): 143 | return "map" 144 | 145 | 146 | class ParallelFlatSetPrinter: 147 | def __init__(self, val): 148 | self.val = val 149 | 150 | def children(self): 151 | return traverse(parallel_slot_iterator(self.val), flat_set_iterator) 152 | 153 | def to_string(self): 154 | return f"phmap::parallel_flat_hash_set with {parallel_size(self.val)} elements" 155 | 156 | def display_hint(self): 157 | return "array" 158 | 159 | 160 | class ParallelNodeMapPrinter: 161 | def __init__(self, val): 162 | self.val = val 163 | 164 | def children(self): 165 | return traverse(parallel_slot_iterator(self.val), node_map_iterator) 166 | 167 | def to_string(self): 168 | return f"phmap::parallel_node_hash_map with {parallel_size(self.val)} elements" 169 | 170 | def display_hint(self): 171 | return "map" 172 | 173 | 174 | class ParallelNodeSetPrinter: 175 | def __init__(self, val): 176 | self.val = val 177 | 178 | def children(self): 179 | return traverse(parallel_slot_iterator(self.val), node_set_iterator) 180 | 181 | def to_string(self): 182 | return f"phmap::parallel_node_hash_set with {parallel_size(self.val)} elements" 183 | 184 | def display_hint(self): 185 | return "array" 186 | 187 | 188 | def build_pretty_printer(): 189 | pp = gdb.printing.RegexpCollectionPrettyPrinter("phmap") 190 | pp.add_printer('flat_hash_map', '^phmap::flat_hash_map<.*>$', FlatMapPrinter) 191 | pp.add_printer('flat_hash_set', '^phmap::flat_hash_set<.*>$', FlatSetPrinter) 192 | pp.add_printer('node_hash_map', '^phmap::node_hash_map<.*>$', NodeMapPrinter) 193 | pp.add_printer('node_hash_set', '^phmap::node_hash_set<.*>$', NodeSetPrinter) 194 | pp.add_printer('parallel_flat_hash_map', '^phmap::parallel_flat_hash_map<.*>$', ParallelFlatMapPrinter) 195 | pp.add_printer('parallel_flat_hash_set', '^phmap::parallel_flat_hash_set<.*>$', ParallelFlatSetPrinter) 196 | pp.add_printer('parallel_node_hash_map', '^phmap::parallel_node_hash_map<.*>$', ParallelNodeMapPrinter) 197 | pp.add_printer('parallel_node_hash_set', '^phmap::parallel_node_hash_set<.*>$', ParallelNodeSetPrinter) 198 | return pp 199 | 200 | 201 | gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer()) 202 | -------------------------------------------------------------------------------- /tests/dump_load_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "gtest/gtest.h" 8 | 9 | #include "parallel_hashmap/phmap_dump.h" 10 | 11 | namespace phmap { 12 | namespace priv { 13 | namespace { 14 | 15 | TEST(DumpLoad, FlatHashSet_uint32) { 16 | phmap::flat_hash_set st1 = { 1991, 1202 }; 17 | 18 | { 19 | phmap::BinaryOutputArchive ar_out("./dump.data"); 20 | EXPECT_TRUE(st1.phmap_dump(ar_out)); 21 | } 22 | 23 | phmap::flat_hash_set st2; 24 | { 25 | phmap::BinaryInputArchive ar_in("./dump.data"); 26 | EXPECT_TRUE(st2.phmap_load(ar_in)); 27 | } 28 | EXPECT_TRUE(st1 == st2); 29 | 30 | { 31 | std::stringstream ss; 32 | phmap::BinaryOutputArchive ar_out(ss); 33 | EXPECT_TRUE(st1.phmap_dump(ar_out)); 34 | phmap::flat_hash_set st3; 35 | phmap::BinaryInputArchive ar_in(ss); 36 | EXPECT_TRUE(st3.phmap_load(ar_in)); 37 | EXPECT_TRUE(st1 == st3); 38 | } 39 | } 40 | 41 | TEST(DumpLoad, FlatHashMap_uint64_uint32) { 42 | phmap::flat_hash_map mp1 = { 43 | { 78731, 99}, {13141, 299}, {2651, 101} }; 44 | 45 | { 46 | phmap::BinaryOutputArchive ar_out("./dump.data"); 47 | EXPECT_TRUE(mp1.phmap_dump(ar_out)); 48 | } 49 | 50 | phmap::flat_hash_map mp2; 51 | { 52 | phmap::BinaryInputArchive ar_in("./dump.data"); 53 | EXPECT_TRUE(mp2.phmap_load(ar_in)); 54 | } 55 | 56 | { 57 | std::stringstream ss; 58 | phmap::BinaryOutputArchive ar_out(ss); 59 | EXPECT_TRUE(mp1.phmap_dump(ar_out)); 60 | phmap::flat_hash_map mp3; 61 | phmap::BinaryInputArchive ar_in(ss); 62 | EXPECT_TRUE(mp3.phmap_load(ar_in)); 63 | EXPECT_TRUE(mp1 == mp3); 64 | } 65 | 66 | EXPECT_TRUE(mp1 == mp2); 67 | } 68 | 69 | TEST(DumpLoad, ParallelFlatHashMap_uint64_uint32) { 70 | phmap::parallel_flat_hash_map mp1 = { 71 | {99, 299}, {992, 2991}, {299, 1299} }; 72 | 73 | { 74 | phmap::BinaryOutputArchive ar_out("./dump.data"); 75 | EXPECT_TRUE(mp1.phmap_dump(ar_out)); 76 | } 77 | 78 | phmap::parallel_flat_hash_map mp2; 79 | { 80 | phmap::BinaryInputArchive ar_in("./dump.data"); 81 | EXPECT_TRUE(mp2.phmap_load(ar_in)); 82 | } 83 | EXPECT_TRUE(mp1 == mp2); 84 | 85 | // test stringstream and dump/load in the middle of the stream 86 | { 87 | char hello[] = "Hello"; 88 | std::stringstream ss; 89 | ss.write(hello, 5); 90 | phmap::BinaryOutputArchive ar_out(ss); 91 | EXPECT_TRUE(mp1.phmap_dump(ar_out)); 92 | phmap::parallel_flat_hash_map mp3; 93 | phmap::BinaryInputArchive ar_in(ss); 94 | char s[5]; 95 | ss.read(s, 5); 96 | for (int i = 0; i < 5; ++i) { 97 | EXPECT_EQ(hello[i], s[i]); 98 | } 99 | EXPECT_TRUE(mp3.phmap_load(ar_in)); 100 | EXPECT_TRUE(mp1 == mp3); 101 | } 102 | } 103 | 104 | } 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /tests/erase_if_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include "parallel_hashmap/phmap.h" 6 | 7 | namespace phmap { 8 | namespace priv { 9 | namespace { 10 | 11 | TEST(EraseIf, FlatHashSet_uint32) { 12 | phmap::flat_hash_set st1 = { 3, 6, 7, 9 }; 13 | auto num_erased = erase_if(st1, [](const uint32_t& v) { return v >= 7; }); 14 | EXPECT_TRUE(num_erased == 2); 15 | 16 | phmap::flat_hash_set st2 = { 0, 2, 3, 6 }; 17 | num_erased = erase_if(st2, [](const uint32_t& v) { return v <= 2; }); 18 | EXPECT_TRUE(num_erased == 2); 19 | 20 | EXPECT_TRUE(st1 == st2); 21 | } 22 | 23 | TEST(EraseIf, FlatHashMap_uint64_uint32) { 24 | using map = phmap::flat_hash_map; 25 | map st1 = { {3, 0}, {6, 0}, {7, 0}, {9, 0} }; 26 | auto num_erased = erase_if(st1, [](const map::value_type& v) { return v.first >= 7; }); 27 | EXPECT_TRUE(num_erased == 2); 28 | 29 | map st2 = { {0, 0}, {2, 0}, {3, 0}, {6, 0} }; 30 | num_erased = erase_if(st2, [](const map::value_type& v) { return v.first <= 2; }); 31 | EXPECT_TRUE(num_erased == 2); 32 | 33 | EXPECT_TRUE(st1 == st2); 34 | } 35 | 36 | TEST(EraseIf, ParallelFlatHashMap_uint64_uint32) { 37 | using map = phmap::parallel_flat_hash_map; 38 | map st1 = { {3, 0}, {6, 0}, {7, 0}, {9, 0} }; 39 | auto num_erased = erase_if(st1, [](const map::value_type& v) { return v.first >= 7; }); 40 | EXPECT_TRUE(num_erased == 2); 41 | 42 | map st2 = { {0, 0}, {2, 0}, {3, 0}, {6, 0} }; 43 | num_erased = erase_if(st2, [](const map::value_type& v) { return v.first <= 2; }); 44 | EXPECT_TRUE(num_erased == 2); 45 | 46 | EXPECT_TRUE(st1 == st2); 47 | } 48 | 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /tests/flat_hash_set_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIS_HASH_SET 16 | #define THIS_HASH_SET flat_hash_set 17 | #define THIS_TEST_NAME FlatHashSet 18 | #endif 19 | 20 | #include "parallel_hashmap/phmap.h" 21 | 22 | #include 23 | 24 | #include "hash_generator_testing.h" 25 | #include "unordered_set_constructor_test.h" 26 | #include "unordered_set_lookup_test.h" 27 | #include "unordered_set_members_test.h" 28 | #include "unordered_set_modifiers_test.h" 29 | 30 | namespace phmap { 31 | namespace priv { 32 | namespace { 33 | 34 | using ::phmap::priv::hash_internal::Enum; 35 | using ::phmap::priv::hash_internal::EnumClass; 36 | using ::testing::Pointee; 37 | using ::testing::UnorderedElementsAre; 38 | using ::testing::UnorderedElementsAreArray; 39 | 40 | template 41 | using Set = 42 | phmap::THIS_HASH_SET>; 43 | 44 | using SetTypes = 45 | ::testing::Types, Set, Set, Set>; 46 | 47 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, ConstructorTest, SetTypes); 48 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, LookupTest, SetTypes); 49 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, MembersTest, SetTypes); 50 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, ModifiersTest, SetTypes); 51 | 52 | #if PHMAP_HAVE_STD_STRING_VIEW 53 | TEST(THIS_TEST_NAME, EmplaceString) { 54 | std::vector v = {"a", "b"}; 55 | phmap::THIS_HASH_SET hs(v.begin(), v.end()); 56 | //EXPECT_THAT(hs, UnorderedElementsAreArray(v)); 57 | } 58 | #endif 59 | 60 | TEST(THIS_TEST_NAME, BitfieldArgument) { 61 | union { 62 | int n : 1; 63 | }; 64 | n = 0; 65 | phmap::THIS_HASH_SET s = {n}; 66 | s.insert(n); 67 | s.insert(s.end(), n); 68 | s.insert({n}); 69 | s.erase(n); 70 | s.count(n); 71 | s.prefetch(n); 72 | s.find(n); 73 | s.contains(n); 74 | s.equal_range(n); 75 | } 76 | 77 | TEST(THIS_TEST_NAME, MergeExtractInsert) { 78 | struct Hash { 79 | size_t operator()(const std::unique_ptr& p) const { return *p; } 80 | }; 81 | struct Eq { 82 | bool operator()(const std::unique_ptr& a, 83 | const std::unique_ptr& b) const { 84 | return *a == *b; 85 | } 86 | }; 87 | phmap::THIS_HASH_SET, Hash, Eq> set1, set2; 88 | set1.insert(phmap::make_unique(7)); 89 | set1.insert(phmap::make_unique(17)); 90 | 91 | set2.insert(phmap::make_unique(7)); 92 | set2.insert(phmap::make_unique(19)); 93 | 94 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17))); 95 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19))); 96 | 97 | set1.merge(set2); 98 | 99 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19))); 100 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); 101 | 102 | auto node = set1.extract(phmap::make_unique(7)); 103 | EXPECT_TRUE(node); 104 | EXPECT_THAT(node.value(), Pointee(7)); 105 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19))); 106 | 107 | auto insert_result = set2.insert(std::move(node)); 108 | EXPECT_FALSE(node); 109 | EXPECT_FALSE(insert_result.inserted); 110 | EXPECT_TRUE(insert_result.node); 111 | EXPECT_THAT(insert_result.node.value(), Pointee(7)); 112 | EXPECT_EQ(**insert_result.position, 7); 113 | EXPECT_NE(insert_result.position->get(), insert_result.node.value().get()); 114 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); 115 | 116 | node = set1.extract(phmap::make_unique(17)); 117 | EXPECT_TRUE(node); 118 | EXPECT_THAT(node.value(), Pointee(17)); 119 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19))); 120 | 121 | node.value() = phmap::make_unique(23); 122 | 123 | insert_result = set2.insert(std::move(node)); 124 | EXPECT_FALSE(node); 125 | EXPECT_TRUE(insert_result.inserted); 126 | EXPECT_FALSE(insert_result.node); 127 | EXPECT_EQ(**insert_result.position, 23); 128 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23))); 129 | } 130 | 131 | } // namespace 132 | } // namespace priv 133 | } // namespace phmap 134 | -------------------------------------------------------------------------------- /tests/hash_policy_testing_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "parallel_hashmap/phmap.h" 16 | #include "hash_policy_testing.h" 17 | 18 | #include "gtest/gtest.h" 19 | 20 | namespace phmap { 21 | namespace priv { 22 | namespace { 23 | 24 | TEST(_, Hash) { 25 | StatefulTestingHash h1; 26 | EXPECT_EQ(1, h1.id()); 27 | StatefulTestingHash h2; 28 | EXPECT_EQ(2, h2.id()); 29 | StatefulTestingHash h1c(h1); 30 | EXPECT_EQ(1, h1c.id()); 31 | StatefulTestingHash h2m(std::move(h2)); 32 | EXPECT_EQ(2, h2m.id()); 33 | EXPECT_EQ(0, h2.id()); 34 | StatefulTestingHash h3; 35 | EXPECT_EQ(3, h3.id()); 36 | h3 = StatefulTestingHash(); 37 | EXPECT_EQ(4, h3.id()); 38 | h3 = std::move(h1); 39 | EXPECT_EQ(1, h3.id()); 40 | } 41 | 42 | } // namespace 43 | } // namespace priv 44 | } // namespace phmap 45 | -------------------------------------------------------------------------------- /tests/hashtable_debug.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This library provides APIs to debug the probing behavior of hash tables. 16 | // 17 | // In general, the probing behavior is a black box for users and only the 18 | // side effects can be measured in the form of performance differences. 19 | // These APIs give a glimpse on the actual behavior of the probing algorithms in 20 | // these hashtables given a specified hash function and a set of elements. 21 | // 22 | // The probe count distribution can be used to assess the quality of the hash 23 | // function for that particular hash table. Note that a hash function that 24 | // performs well in one hash table implementation does not necessarily performs 25 | // well in a different one. 26 | // 27 | // This library supports std::unordered_{set,map}, dense_hash_{set,map} and 28 | // phmap::{flat,node,string}_hash_{set,map}. 29 | 30 | #ifndef PHMAP_PRIV_HASHTABLE_DEBUG_H_ 31 | #define PHMAP_PRIV_HASHTABLE_DEBUG_H_ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace phmap { 39 | namespace priv { 40 | 41 | // Returns the number of probes required to lookup `key`. Returns 0 for a 42 | // search with no collisions. Higher values mean more hash collisions occurred; 43 | // however, the exact meaning of this number varies according to the container 44 | // type. 45 | template 46 | size_t GetHashtableDebugNumProbes( 47 | const C& c, const typename C::key_type& key) { 48 | return phmap::priv::hashtable_debug_internal:: 49 | HashtableDebugAccess::GetNumProbes(c, key); 50 | } 51 | 52 | // Gets a histogram of the number of probes for each elements in the container. 53 | // The sum of all the values in the vector is equal to container.size(). 54 | template 55 | std::vector GetHashtableDebugNumProbesHistogram(const C& container) { 56 | std::vector v; 57 | for (auto it = container.begin(); it != container.end(); ++it) { 58 | size_t num_probes = GetHashtableDebugNumProbes( 59 | container, 60 | phmap::priv::hashtable_debug_internal::GetKey(*it, 0)); 61 | v.resize((std::max)(v.size(), num_probes + 1)); 62 | v[num_probes]++; 63 | } 64 | return v; 65 | } 66 | 67 | struct HashtableDebugProbeSummary { 68 | size_t total_elements; 69 | size_t total_num_probes; 70 | double mean; 71 | }; 72 | 73 | // Gets a summary of the probe count distribution for the elements in the 74 | // container. 75 | template 76 | HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) { 77 | auto probes = GetHashtableDebugNumProbesHistogram(container); 78 | HashtableDebugProbeSummary summary = {}; 79 | for (size_t i = 0; i < probes.size(); ++i) { 80 | summary.total_elements += probes[i]; 81 | summary.total_num_probes += probes[i] * i; 82 | } 83 | summary.mean = 1.0 * summary.total_num_probes / summary.total_elements; 84 | return summary; 85 | } 86 | 87 | // Returns the number of bytes requested from the allocator by the container 88 | // and not freed. 89 | template 90 | size_t AllocatedByteSize(const C& c) { 91 | return phmap::priv::hashtable_debug_internal:: 92 | HashtableDebugAccess::AllocatedByteSize(c); 93 | } 94 | 95 | // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C` 96 | // and `c.size()` is equal to `num_elements`. 97 | template 98 | size_t LowerBoundAllocatedByteSize(size_t num_elements) { 99 | return phmap::priv::hashtable_debug_internal:: 100 | HashtableDebugAccess::LowerBoundAllocatedByteSize(num_elements); 101 | } 102 | 103 | } // namespace priv 104 | } // namespace phmap 105 | 106 | #endif // PHMAP_PRIV_HASHTABLE_DEBUG_H_ 107 | -------------------------------------------------------------------------------- /tests/node_hash_policy_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "parallel_hashmap/phmap.h" 17 | 18 | #include "gmock/gmock.h" 19 | #include "gtest/gtest.h" 20 | 21 | namespace phmap { 22 | namespace priv { 23 | namespace { 24 | 25 | using ::testing::Pointee; 26 | 27 | struct Policy : node_hash_policy { 28 | using key_type = int; 29 | using init_type = int; 30 | 31 | template 32 | static int* new_element(Alloc*, int value) { 33 | return new int(value); 34 | } 35 | 36 | template 37 | static void delete_element(Alloc* , int* elem) { 38 | delete elem; 39 | } 40 | }; 41 | 42 | using NodePolicy = hash_policy_traits; 43 | 44 | struct NodeTest : ::testing::Test { 45 | std::allocator alloc; 46 | int n = 53; 47 | int* a = &n; 48 | }; 49 | 50 | TEST_F(NodeTest, ConstructDestroy) { 51 | NodePolicy::construct(&alloc, &a, 42); 52 | EXPECT_THAT(a, Pointee(42)); 53 | NodePolicy::destroy(&alloc, &a); 54 | } 55 | 56 | TEST_F(NodeTest, transfer) { 57 | int s = 42; 58 | int* b = &s; 59 | NodePolicy::transfer(&alloc, &a, &b); 60 | EXPECT_EQ(&s, a); 61 | } 62 | 63 | } // namespace 64 | } // namespace priv 65 | } // namespace phmap 66 | -------------------------------------------------------------------------------- /tests/node_hash_set_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef THIS_HASH_SET 16 | #define THIS_HASH_SET node_hash_set 17 | #define THIS_TEST_NAME NodeHashSet 18 | #endif 19 | 20 | #include "parallel_hashmap/phmap.h" 21 | 22 | #include "unordered_set_constructor_test.h" 23 | #include "unordered_set_lookup_test.h" 24 | #include "unordered_set_members_test.h" 25 | #include "unordered_set_modifiers_test.h" 26 | 27 | namespace phmap { 28 | namespace priv { 29 | namespace { 30 | using ::phmap::priv::hash_internal::Enum; 31 | using ::phmap::priv::hash_internal::EnumClass; 32 | using ::testing::Pointee; 33 | using ::testing::UnorderedElementsAre; 34 | 35 | using SetTypes = ::testing::Types< 36 | THIS_HASH_SET>, 37 | THIS_HASH_SET>, 39 | THIS_HASH_SET>, 40 | THIS_HASH_SET>>; 42 | 43 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, ConstructorTest, SetTypes); 44 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, LookupTest, SetTypes); 45 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, MembersTest, SetTypes); 46 | INSTANTIATE_TYPED_TEST_SUITE_P(THIS_TEST_NAME, ModifiersTest, SetTypes); 47 | 48 | TEST(THIS_TEST_NAME, MoveableNotCopyableCompiles) { 49 | THIS_HASH_SET> t; 50 | THIS_HASH_SET> u; 51 | u = std::move(t); 52 | } 53 | 54 | TEST(THIS_TEST_NAME, MergeExtractInsert) { 55 | struct Hash { 56 | size_t operator()(const std::unique_ptr& p) const { return *p; } 57 | }; 58 | struct Eq { 59 | bool operator()(const std::unique_ptr& a, 60 | const std::unique_ptr& b) const { 61 | return *a == *b; 62 | } 63 | }; 64 | phmap::THIS_HASH_SET, Hash, Eq> set1, set2; 65 | set1.insert(phmap::make_unique(7)); 66 | set1.insert(phmap::make_unique(17)); 67 | 68 | set2.insert(phmap::make_unique(7)); 69 | set2.insert(phmap::make_unique(19)); 70 | 71 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17))); 72 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19))); 73 | 74 | set1.merge(set2); 75 | 76 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19))); 77 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); 78 | 79 | auto node = set1.extract(phmap::make_unique(7)); 80 | EXPECT_TRUE(node); 81 | EXPECT_THAT(node.value(), Pointee(7)); 82 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19))); 83 | 84 | auto insert_result = set2.insert(std::move(node)); 85 | EXPECT_FALSE(node); 86 | EXPECT_FALSE(insert_result.inserted); 87 | EXPECT_TRUE(insert_result.node); 88 | EXPECT_THAT(insert_result.node.value(), Pointee(7)); 89 | EXPECT_EQ(**insert_result.position, 7); 90 | EXPECT_NE(insert_result.position->get(), insert_result.node.value().get()); 91 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); 92 | 93 | node = set1.extract(phmap::make_unique(17)); 94 | EXPECT_TRUE(node); 95 | EXPECT_THAT(node.value(), Pointee(17)); 96 | EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19))); 97 | 98 | node.value() = phmap::make_unique(23); 99 | 100 | insert_result = set2.insert(std::move(node)); 101 | EXPECT_FALSE(node); 102 | EXPECT_TRUE(insert_result.inserted); 103 | EXPECT_FALSE(insert_result.node); 104 | EXPECT_EQ(**insert_result.position, 23); 105 | EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23))); 106 | } 107 | 108 | } // namespace 109 | } // namespace priv 110 | } // namespace phmap 111 | -------------------------------------------------------------------------------- /tests/parallel_flat_hash_map_mutex_test.cc: -------------------------------------------------------------------------------- 1 | #define THIS_HASH_MAP parallel_flat_hash_map 2 | #define THIS_TEST_NAME ParallelFlatHashMap 3 | 4 | #if 1 5 | #define THIS_EXTRA_TPL_PARAMS , 4, std::mutex 6 | #else 7 | #include 8 | #include 9 | #define THIS_EXTRA_TPL_PARAMS , 4, boost::upgrade_mutex 10 | #endif 11 | 12 | #define THIS_EXTRA_TPL_PARAMS_NULLMUTEX , 4, phmap::NullMutex 13 | 14 | #include "parallel_hash_map_test.cc" 15 | -------------------------------------------------------------------------------- /tests/parallel_flat_hash_map_test.cc: -------------------------------------------------------------------------------- 1 | #define THIS_HASH_MAP parallel_flat_hash_map 2 | #define THIS_TEST_NAME ParallelFlatHashMap 3 | #include 4 | 5 | #include "parallel_hash_map_test.cc" 6 | 7 | 8 | #if PHMAP_HAVE_SHARED_MUTEX 9 | #include 10 | 11 | template using HashEqual = phmap::priv::hash_default_eq; 12 | template using HashFn = phmap::priv::hash_default_hash; 13 | template using Allocator = phmap::priv::Allocator; 14 | 15 | template 16 | using parallel_flat_hash_map = 17 | phmap::parallel_flat_hash_map, HashEqual, 18 | Allocator>, N, 19 | std::shared_mutex>; 20 | 21 | using Table = parallel_flat_hash_map; 22 | 23 | TEST(THIS_TEST_NAME, ConcurrencyCheck) { 24 | static constexpr int THREADS = 10; 25 | static constexpr int EPOCH = 1000; 26 | static constexpr int KEY = 12345; 27 | 28 | auto Incr = [](Table *table) { 29 | auto exist_fn = [](typename Table::value_type &value) { value.second += 1; }; 30 | auto emplace_fn = [](const typename Table::constructor &ctor) { 31 | ctor(KEY, 1); 32 | }; 33 | for (int i = 0; i < EPOCH; ++i) { 34 | (void)table->lazy_emplace_l(KEY, exist_fn, emplace_fn); 35 | } 36 | }; 37 | 38 | Table table; 39 | std::vector threads; 40 | threads.reserve(THREADS); 41 | for (int i = 0; i < THREADS; ++i) { 42 | threads.emplace_back([&]() { Incr(&table); }); 43 | } 44 | 45 | for (auto &thread : threads) { 46 | thread.join(); 47 | } 48 | 49 | EXPECT_EQ(table[KEY], 10000); 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /tests/parallel_flat_hash_set_test.cc: -------------------------------------------------------------------------------- 1 | #define THIS_HASH_SET parallel_flat_hash_set 2 | #define THIS_TEST_NAME ParallelFlatHashSet 3 | 4 | #include "parallel_hash_set_test.cc" 5 | 6 | -------------------------------------------------------------------------------- /tests/parallel_hash_map_test.cc: -------------------------------------------------------------------------------- 1 | #ifndef THIS_HASH_MAP 2 | #define THIS_HASH_MAP parallel_flat_hash_map 3 | #define THIS_TEST_NAME ParallelFlatHashMap 4 | #endif 5 | 6 | #include "flat_hash_map_test.cc" 7 | 8 | namespace phmap { 9 | namespace priv { 10 | namespace { 11 | 12 | TEST(THIS_TEST_NAME, Swap) { 13 | using Map = ThisMap; 14 | using MapB = ThisMap_NullMutex; 15 | 16 | Map t; 17 | EXPECT_TRUE(t.find(0) == t.end()); 18 | auto res = t.emplace(0, 1); 19 | EXPECT_TRUE(res.second); 20 | EXPECT_EQ(1, t.size()); 21 | MapB u; 22 | t.swap(u); 23 | EXPECT_EQ(0, t.size()); 24 | EXPECT_EQ(1, u.size()); 25 | EXPECT_TRUE(t.find(0) == t.end()); 26 | EXPECT_TRUE(u[0] == 1); 27 | } 28 | 29 | 30 | TEST(THIS_TEST_NAME, IfContains) { 31 | // ---------------- 32 | // test if_contains 33 | // ---------------- 34 | 35 | using Map = ThisMap; 36 | Map m = { {1, 7}, {2, 9} }; 37 | const Map& const_m(m); 38 | 39 | auto val = 0; 40 | auto get_value = [&val](const Map::value_type& v) { val = v.second; }; 41 | EXPECT_TRUE(const_m.if_contains(2, get_value)); 42 | EXPECT_EQ(val, 9); 43 | 44 | EXPECT_FALSE(m.if_contains(3, get_value)); 45 | } 46 | 47 | TEST(THIS_TEST_NAME, ModifyIf) { 48 | // -------------- 49 | // test modify_if 50 | // -------------- 51 | using Map = ThisMap; 52 | Map m = { {1, 7}, {2, 9} }; 53 | 54 | auto set_value = [](Map::value_type& v) { v.second = 11; }; 55 | EXPECT_TRUE(m.modify_if(2, set_value)); 56 | EXPECT_EQ(m[2], 11); 57 | 58 | EXPECT_FALSE(m.modify_if(3, set_value)); // because m[3] does not exist 59 | } 60 | 61 | TEST(THIS_TEST_NAME, TryEmplaceL) { 62 | // ------------------ 63 | // test try_emplace_l 64 | // ------------------ 65 | using Map = ThisMap; 66 | Map m = { {1, 7}, {2, 9} }; 67 | 68 | // overwrite an existing value 69 | m.try_emplace_l(2, [](Map::value_type& v) { v.second = 5; }); 70 | EXPECT_EQ(m[2], 5); 71 | 72 | // insert a value that is not already present. Will be default initialised to 0 and lambda not called 73 | m.try_emplace_l(3, 74 | [](Map::value_type& v) { v.second = 6; }, // called only when key was already present 75 | 1); // argument to construct new value is key not present 76 | EXPECT_EQ(m[3], 1); 77 | 78 | // insert a value that is not already present, provide argument to value-construct it 79 | m.try_emplace_l(4, 80 | [](Map::value_type& ) {}, // called only when key was already present 81 | 999); // argument to construct new value is key not present 82 | 83 | EXPECT_EQ(m[4], 999); 84 | } 85 | 86 | TEST(THIS_TEST_NAME, LazyEmplaceL) { 87 | // -------------------- 88 | // test lazy_emplace_l 89 | // -------------------- 90 | using Map = ThisMap; 91 | Map m = { {1, 7}, {2, 9} }; 92 | 93 | // insert a value that is not already present. 94 | // right now m[5] does not exist 95 | m.lazy_emplace_l(5, 96 | [](Map::value_type& v) { v.second = 6; }, // called only when key was already present 97 | [](const Map::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present 98 | EXPECT_EQ(m[5], 13); 99 | 100 | // change a value that is present. Currently m[5] == 13 101 | m.lazy_emplace_l(5, 102 | [](Map::value_type& v) { v.second = 6; }, // called only when key was already present 103 | [](const Map::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present 104 | EXPECT_EQ(m[5], 6); 105 | } 106 | 107 | TEST(THIS_TEST_NAME, EraseIf) { 108 | // ------------- 109 | // test erase_if 110 | // ------------- 111 | using Map = ThisMap; 112 | Map m = { {1, 7}, {2, 9}, {5, 6} }; 113 | 114 | EXPECT_EQ(m.erase_if(9, [](Map::value_type& v) { assert(0); return v.second == 12; }), false); // m[9] not present - lambda not called 115 | EXPECT_EQ(m.erase_if(5, [](Map::value_type& v) { return v.second == 12; }), false); // m[5] == 6, so erase not performed 116 | EXPECT_EQ(m[5], 6); 117 | EXPECT_EQ(m.erase_if(5, [](Map::value_type& v) { return v.second == 6; }), true); // lambda returns true, so m[5] erased 118 | EXPECT_EQ(m[5], 0); 119 | } 120 | 121 | TEST(THIS_TEST_NAME, ForEach) { 122 | // ------------- 123 | // test for_each 124 | // ------------- 125 | using Map = ThisMap; 126 | Map m = { {1, 7}, {2, 8}, {5, 11} }; 127 | 128 | // increment all values by 1 129 | m.for_each_m([](Map::value_type &pair) { ++pair.second; }); 130 | 131 | int counter = 0; 132 | m.for_each([&counter](const Map::value_type &pair) { 133 | ++counter; 134 | EXPECT_EQ(pair.first + 7, pair.second); 135 | }); 136 | EXPECT_EQ(counter, 3); 137 | 138 | counter = 0; 139 | for (size_t i=0; i; 155 | Map m = { {1, 4}, {11, 4} }; 156 | 157 | // emplace_single insert a value if not already present, else removes it 158 | for (int i=0; i<12; ++i) 159 | m.emplace_single(i, [i](const Map::constructor& ctor) { ctor(i, 4); }); 160 | EXPECT_EQ(m.count(0), 1); 161 | EXPECT_EQ(m.count(1), 0); 162 | EXPECT_EQ(m.count(2), 1); 163 | EXPECT_EQ(m.count(11), 0); 164 | } 165 | 166 | 167 | } // namespace 168 | } // namespace priv 169 | } // namespace phmap 170 | -------------------------------------------------------------------------------- /tests/parallel_hash_set_test.cc: -------------------------------------------------------------------------------- 1 | #ifndef THIS_HASH_SET 2 | #define THIS_HASH_SET parallel_flat_hash_set 3 | #define THIS_TEST_NAME ParallelFlatHashSet 4 | #endif 5 | 6 | #include "flat_hash_set_test.cc" 7 | 8 | namespace phmap { 9 | namespace priv { 10 | namespace { 11 | 12 | struct Entry 13 | { 14 | Entry(int k, int v=0) : key(k), value(v) {} 15 | 16 | bool operator==(const Entry &o) const 17 | { 18 | return key == o.key; // not checking value 19 | } 20 | 21 | // Demonstrates how to provide the hash function as a friend member function of the class 22 | // This can be used as an alternative to providing a std::hash specialization 23 | // -------------------------------------------------------------------------------------- 24 | friend size_t hash_value(const Entry &p) 25 | { 26 | return phmap::HashState().combine(0, p.key); // not checking value 27 | } 28 | 29 | int key; 30 | int value; 31 | }; 32 | 33 | TEST(THIS_TEST_NAME, IfContains) { 34 | // ---------------- 35 | // test if_contains 36 | // ---------------- 37 | using Set = phmap::THIS_HASH_SET; 38 | Set m = { {1, 7}, {2, 9} }; 39 | const Set& const_m(m); 40 | 41 | auto val = 0; 42 | auto get_value = [&val](const Set::value_type& v) { val = v.value; }; 43 | EXPECT_TRUE(const_m.if_contains(Entry{2}, get_value)); 44 | EXPECT_EQ(val, 9); 45 | 46 | EXPECT_FALSE(m.if_contains(Entry{3}, get_value)); 47 | } 48 | 49 | TEST(THIS_TEST_NAME, ModifyIf) { 50 | // -------------- 51 | // test modify_if 52 | // -------------- 53 | using Set = phmap::THIS_HASH_SET; 54 | Set m = { {1, 7}, {2, 9} }; 55 | 56 | auto set_value = [](Set::value_type& v) { v.value = 11; }; 57 | EXPECT_TRUE(m.modify_if(Entry{2}, set_value)); 58 | 59 | auto val = 0; 60 | auto get_value = [&val](const Set::value_type& v) { val = v.value; }; 61 | EXPECT_TRUE(m.if_contains(Entry{2}, get_value)); 62 | EXPECT_EQ(val, 11); 63 | 64 | EXPECT_FALSE(m.modify_if(Entry{3}, set_value)); // because m[3] does not exist 65 | } 66 | 67 | TEST(THIS_TEST_NAME, LazyEmplaceL) { 68 | // -------------------- 69 | // test lazy_emplace_l 70 | // -------------------- 71 | using Set = phmap::THIS_HASH_SET; 72 | Set m = { {1, 7}, {2, 9} }; 73 | 74 | // insert a value that is not already present. 75 | // right now m[5] does not exist 76 | m.lazy_emplace_l(Entry{5}, 77 | [](Set::value_type& v) { v.value = 6; }, // called only when key was already present 78 | [](const Set::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present 79 | EXPECT_EQ(m.find(Entry{5})->value, 13); 80 | 81 | // change a value that is present. 82 | m.lazy_emplace_l(Entry{5}, 83 | [](Set::value_type& v) { v.value = 6; }, // called only when key was already present 84 | [](const Set::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present 85 | EXPECT_EQ(m.find(Entry{5})->value, 6); 86 | } 87 | 88 | TEST(THIS_TEST_NAME, EraseIf) { 89 | // ------------- 90 | // test erase_if 91 | // ------------- 92 | using Set = phmap::THIS_HASH_SET; 93 | Set m = { {1, 7}, {2, 9}, {5, 6} }; 94 | 95 | EXPECT_EQ(m.erase_if(Entry{9}, [](Set::value_type& v) { assert(0); return v.value == 12; }), false); // m[9] not present - lambda not called 96 | EXPECT_EQ(m.erase_if(Entry{5}, [](Set::value_type& v) { return v.value == 12; }), false); // m[5] == 6, so erase not performed 97 | EXPECT_EQ(m.find(Entry{5})->value, 6); 98 | EXPECT_EQ(m.erase_if(Entry{5}, [](Set::value_type& v) { return v.value == 6; }), true); // lambda returns true, so m[5] erased 99 | EXPECT_EQ(m.find(Entry{5}), m.end()); 100 | } 101 | 102 | TEST(THIS_TEST_NAME, ForEach) { 103 | // ------------- 104 | // test for_each 105 | // ------------- 106 | using Set = phmap::THIS_HASH_SET; 107 | Set m = { {1, 7}, {2, 8}, {5, 11} }; 108 | 109 | int counter = 0; 110 | m.for_each([&counter](const Set::value_type &v) { 111 | ++counter; 112 | EXPECT_EQ(v.key + 6, v.value); 113 | }); 114 | EXPECT_EQ(counter, 3); 115 | } 116 | 117 | TEST(THIS_TEST_NAME, EmplaceSingle) { 118 | using Set = phmap::THIS_HASH_SET; 119 | 120 | // -------------------- 121 | // test emplace_single 122 | // -------------------- 123 | Set m = { 1, 11 }; 124 | 125 | // emplace_single insert a value if not already present, else removes it 126 | for (int i=0; i<12; ++i) 127 | m.emplace_single(i, [i](const Set::constructor& ctor) { ctor(i); }); 128 | EXPECT_EQ(m.count(0), 1); 129 | EXPECT_EQ(m.count(1), 0); 130 | EXPECT_EQ(m.count(2), 1); 131 | EXPECT_EQ(m.count(11), 0); 132 | } 133 | 134 | } // namespace 135 | } // namespace priv 136 | } // namespace phmap 137 | -------------------------------------------------------------------------------- /tests/parallel_node_hash_map_test.cc: -------------------------------------------------------------------------------- 1 | #define THIS_HASH_MAP parallel_node_hash_map 2 | #define THIS_TEST_NAME ParallelNodeHashMap 3 | 4 | #include "parallel_hash_map_test.cc" 5 | -------------------------------------------------------------------------------- /tests/parallel_node_hash_set_test.cc: -------------------------------------------------------------------------------- 1 | #define THIS_HASH_SET parallel_node_hash_set 2 | #define THIS_TEST_NAME ParallelNodeHashSet 3 | 4 | #include "node_hash_set_test.cc" 5 | -------------------------------------------------------------------------------- /tests/tracked.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PHMAP_PRIV_TRACKED_H_ 16 | #define PHMAP_PRIV_TRACKED_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace phmap { 23 | namespace priv { 24 | 25 | // A class that tracks its copies and moves so that it can be queried in tests. 26 | template 27 | class Tracked { 28 | public: 29 | Tracked() {} 30 | // NOLINTNEXTLINE(runtime/explicit) 31 | Tracked(const T& val) : val_(val) {} 32 | Tracked(const Tracked& that) 33 | : val_(that.val_), 34 | num_moves_(that.num_moves_), 35 | num_copies_(that.num_copies_) { 36 | ++(*num_copies_); 37 | } 38 | Tracked(Tracked&& that) 39 | : val_(std::move(that.val_)), 40 | num_moves_(std::move(that.num_moves_)), 41 | num_copies_(std::move(that.num_copies_)) { 42 | ++(*num_moves_); 43 | } 44 | Tracked& operator=(const Tracked& that) { 45 | val_ = that.val_; 46 | num_moves_ = that.num_moves_; 47 | num_copies_ = that.num_copies_; 48 | ++(*num_copies_); 49 | } 50 | Tracked& operator=(Tracked&& that) { 51 | val_ = std::move(that.val_); 52 | num_moves_ = std::move(that.num_moves_); 53 | num_copies_ = std::move(that.num_copies_); 54 | ++(*num_moves_); 55 | } 56 | 57 | const T& val() const { return val_; } 58 | 59 | friend bool operator==(const Tracked& a, const Tracked& b) { 60 | return a.val_ == b.val_; 61 | } 62 | friend bool operator!=(const Tracked& a, const Tracked& b) { 63 | return !(a == b); 64 | } 65 | 66 | size_t num_copies() { return *num_copies_; } 67 | size_t num_moves() { return *num_moves_; } 68 | 69 | private: 70 | T val_; 71 | std::shared_ptr num_moves_ = std::make_shared(0); 72 | std::shared_ptr num_copies_ = std::make_shared(0); 73 | }; 74 | 75 | } // namespace priv 76 | } // namespace phmap 77 | 78 | #endif // PHMAP_PRIV_TRACKED_H_ 79 | -------------------------------------------------------------------------------- /tests/unordered_map_lookup_test.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PHMAP_PRIV_UNORDERED_MAP_LOOKUP_TEST_H_ 16 | #define PHMAP_PRIV_UNORDERED_MAP_LOOKUP_TEST_H_ 17 | 18 | #ifdef _MSC_VER 19 | #pragma warning(push, 0) 20 | #endif 21 | 22 | #include "gmock/gmock.h" 23 | #include "gtest/gtest.h" 24 | #include "hash_generator_testing.h" 25 | #include "hash_policy_testing.h" 26 | 27 | #ifdef _MSC_VER 28 | #pragma warning(pop) 29 | #endif 30 | 31 | namespace phmap { 32 | namespace priv { 33 | 34 | template 35 | class LookupTest : public ::testing::Test {}; 36 | 37 | TYPED_TEST_SUITE_P(LookupTest); 38 | 39 | TYPED_TEST_P(LookupTest, At) { 40 | using T = hash_internal::GeneratedType; 41 | std::vector values; 42 | std::generate_n(std::back_inserter(values), 10, 43 | hash_internal::Generator()); 44 | TypeParam m(values.begin(), values.end()); 45 | for (const auto& p : values) { 46 | const auto& val = m.at(p.first); 47 | EXPECT_EQ(p.second, val) << ::testing::PrintToString(p.first); 48 | } 49 | } 50 | 51 | TYPED_TEST_P(LookupTest, OperatorBracket) { 52 | using T = hash_internal::GeneratedType; 53 | using V = typename TypeParam::mapped_type; 54 | std::vector values; 55 | std::generate_n(std::back_inserter(values), 10, 56 | hash_internal::Generator()); 57 | TypeParam m; 58 | for (const auto& p : values) { 59 | auto& val = m[p.first]; 60 | EXPECT_EQ(V(), val) << ::testing::PrintToString(p.first); 61 | val = p.second; 62 | } 63 | for (const auto& p : values) 64 | EXPECT_EQ(p.second, m[p.first]) << ::testing::PrintToString(p.first); 65 | } 66 | 67 | TYPED_TEST_P(LookupTest, Count) { 68 | using T = hash_internal::GeneratedType; 69 | std::vector values; 70 | std::generate_n(std::back_inserter(values), 10, 71 | hash_internal::Generator()); 72 | TypeParam m; 73 | for (const auto& p : values) 74 | EXPECT_EQ(0, m.count(p.first)) << ::testing::PrintToString(p.first); 75 | m.insert(values.begin(), values.end()); 76 | for (const auto& p : values) 77 | EXPECT_EQ(1, m.count(p.first)) << ::testing::PrintToString(p.first); 78 | } 79 | 80 | TYPED_TEST_P(LookupTest, Find) { 81 | using std::get; 82 | using T = hash_internal::GeneratedType; 83 | std::vector values; 84 | std::generate_n(std::back_inserter(values), 10, 85 | hash_internal::Generator()); 86 | TypeParam m; 87 | for (const auto& p : values) 88 | EXPECT_TRUE(m.end() == m.find(p.first)) 89 | << ::testing::PrintToString(p.first); 90 | m.insert(values.begin(), values.end()); 91 | for (const auto& p : values) { 92 | auto it = m.find(p.first); 93 | EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(p.first); 94 | EXPECT_EQ(p.second, get<1>(*it)) << ::testing::PrintToString(p.first); 95 | } 96 | } 97 | 98 | TYPED_TEST_P(LookupTest, EqualRange) { 99 | using std::get; 100 | using T = hash_internal::GeneratedType; 101 | std::vector values; 102 | std::generate_n(std::back_inserter(values), 10, 103 | hash_internal::Generator()); 104 | TypeParam m; 105 | for (const auto& p : values) { 106 | auto r = m.equal_range(p.first); 107 | ASSERT_EQ(0, std::distance(r.first, r.second)); 108 | } 109 | m.insert(values.begin(), values.end()); 110 | for (const auto& p : values) { 111 | auto r = m.equal_range(p.first); 112 | ASSERT_EQ(1, std::distance(r.first, r.second)); 113 | EXPECT_EQ(p.second, get<1>(*r.first)) << ::testing::PrintToString(p.first); 114 | } 115 | } 116 | 117 | REGISTER_TYPED_TEST_SUITE_P(LookupTest, At, OperatorBracket, Count, Find, 118 | EqualRange); 119 | 120 | } // namespace priv 121 | } // namespace phmap 122 | 123 | #endif // PHMAP_PRIV_UNORDERED_MAP_LOOKUP_TEST_H_ 124 | -------------------------------------------------------------------------------- /tests/unordered_map_members_test.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PHMAP_PRIV_UNORDERED_MAP_MEMBERS_TEST_H_ 16 | #define PHMAP_PRIV_UNORDERED_MAP_MEMBERS_TEST_H_ 17 | 18 | #include 19 | 20 | #ifdef _MSC_VER 21 | #pragma warning(push, 0) 22 | #endif 23 | 24 | #include "gmock/gmock.h" 25 | #include "gtest/gtest.h" 26 | #include "hash_generator_testing.h" 27 | #include "hash_policy_testing.h" 28 | 29 | #ifdef _MSC_VER 30 | #pragma warning(pop) 31 | #endif 32 | 33 | namespace phmap { 34 | namespace priv { 35 | 36 | template 37 | class MembersTest : public ::testing::Test {}; 38 | 39 | TYPED_TEST_SUITE_P(MembersTest); 40 | 41 | template 42 | void UseType() {} 43 | 44 | TYPED_TEST_P(MembersTest, Typedefs) { 45 | EXPECT_TRUE((std::is_same, 47 | typename TypeParam::value_type>())); 48 | EXPECT_TRUE((phmap::conjunction< 49 | phmap::negation>, 50 | std::is_integral>())); 51 | EXPECT_TRUE((phmap::conjunction< 52 | std::is_signed, 53 | std::is_integral>())); 54 | EXPECT_TRUE((std::is_convertible< 55 | decltype(std::declval()( 56 | std::declval())), 57 | size_t>())); 58 | EXPECT_TRUE((std::is_convertible< 59 | decltype(std::declval()( 60 | std::declval(), 61 | std::declval())), 62 | bool>())); 63 | EXPECT_TRUE((std::is_same())); 65 | EXPECT_TRUE((std::is_same())); 67 | EXPECT_TRUE((std::is_same())); 69 | EXPECT_TRUE((std::is_same::pointer, 71 | typename TypeParam::pointer>())); 72 | EXPECT_TRUE( 73 | (std::is_same::const_pointer, 75 | typename TypeParam::const_pointer>())); 76 | } 77 | 78 | TYPED_TEST_P(MembersTest, SimpleFunctions) { 79 | EXPECT_GT(TypeParam().max_size(), 0); 80 | } 81 | 82 | TYPED_TEST_P(MembersTest, BeginEnd) { 83 | TypeParam t = {typename TypeParam::value_type{}}; 84 | EXPECT_EQ(t.begin(), t.cbegin()); 85 | EXPECT_EQ(t.end(), t.cend()); 86 | EXPECT_NE(t.begin(), t.end()); 87 | EXPECT_NE(t.cbegin(), t.cend()); 88 | } 89 | 90 | REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); 91 | 92 | } // namespace priv 93 | } // namespace phmap 94 | 95 | #endif // PHMAP_PRIV_UNORDERED_MAP_MEMBERS_TEST_H_ 96 | -------------------------------------------------------------------------------- /tests/unordered_set_lookup_test.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PHMAP_PRIV_UNORDERED_SET_LOOKUP_TEST_H_ 16 | #define PHMAP_PRIV_UNORDERED_SET_LOOKUP_TEST_H_ 17 | 18 | #include "gmock/gmock.h" 19 | #include "gtest/gtest.h" 20 | #include "hash_generator_testing.h" 21 | #include "hash_policy_testing.h" 22 | 23 | namespace phmap { 24 | namespace priv { 25 | 26 | template 27 | class LookupTest : public ::testing::Test {}; 28 | 29 | TYPED_TEST_SUITE_P(LookupTest); 30 | 31 | TYPED_TEST_P(LookupTest, Count) { 32 | using T = hash_internal::GeneratedType; 33 | std::vector values; 34 | std::generate_n(std::back_inserter(values), 10, 35 | hash_internal::Generator()); 36 | TypeParam m; 37 | for (const auto& v : values) 38 | EXPECT_EQ(0, m.count(v)) << ::testing::PrintToString(v); 39 | m.insert(values.begin(), values.end()); 40 | for (const auto& v : values) 41 | EXPECT_EQ(1, m.count(v)) << ::testing::PrintToString(v); 42 | } 43 | 44 | TYPED_TEST_P(LookupTest, Find) { 45 | using T = hash_internal::GeneratedType; 46 | std::vector values; 47 | std::generate_n(std::back_inserter(values), 10, 48 | hash_internal::Generator()); 49 | TypeParam m; 50 | for (const auto& v : values) 51 | EXPECT_TRUE(m.end() == m.find(v)) << ::testing::PrintToString(v); 52 | m.insert(values.begin(), values.end()); 53 | for (const auto& v : values) { 54 | typename TypeParam::iterator it = m.find(v); 55 | static_assert(std::is_same::value, 57 | ""); 58 | static_assert(std::is_same())>::value, 60 | ""); 61 | EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(v); 62 | EXPECT_EQ(v, *it) << ::testing::PrintToString(v); 63 | } 64 | } 65 | 66 | TYPED_TEST_P(LookupTest, EqualRange) { 67 | using T = hash_internal::GeneratedType; 68 | std::vector values; 69 | std::generate_n(std::back_inserter(values), 10, 70 | hash_internal::Generator()); 71 | TypeParam m; 72 | for (const auto& v : values) { 73 | auto r = m.equal_range(v); 74 | ASSERT_EQ(0, std::distance(r.first, r.second)); 75 | } 76 | m.insert(values.begin(), values.end()); 77 | for (const auto& v : values) { 78 | auto r = m.equal_range(v); 79 | ASSERT_EQ(1, std::distance(r.first, r.second)); 80 | EXPECT_EQ(v, *r.first); 81 | } 82 | } 83 | 84 | REGISTER_TYPED_TEST_SUITE_P(LookupTest, Count, Find, EqualRange); 85 | 86 | } // namespace priv 87 | } // namespace phmap 88 | 89 | #endif // PHMAP_PRIV_UNORDERED_SET_LOOKUP_TEST_H_ 90 | -------------------------------------------------------------------------------- /tests/unordered_set_members_test.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PHMAP_PRIV_UNORDERED_SET_MEMBERS_TEST_H_ 16 | #define PHMAP_PRIV_UNORDERED_SET_MEMBERS_TEST_H_ 17 | 18 | #include 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | 22 | namespace phmap { 23 | namespace priv { 24 | 25 | template 26 | class MembersTest : public ::testing::Test {}; 27 | 28 | TYPED_TEST_SUITE_P(MembersTest); 29 | 30 | template 31 | void UseType() {} 32 | 33 | TYPED_TEST_P(MembersTest, Typedefs) { 34 | EXPECT_TRUE((std::is_same())); 36 | EXPECT_TRUE((phmap::conjunction< 37 | phmap::negation>, 38 | std::is_integral>())); 39 | EXPECT_TRUE((phmap::conjunction< 40 | std::is_signed, 41 | std::is_integral>())); 42 | EXPECT_TRUE((std::is_convertible< 43 | decltype(std::declval()( 44 | std::declval())), 45 | size_t>())); 46 | EXPECT_TRUE((std::is_convertible< 47 | decltype(std::declval()( 48 | std::declval(), 49 | std::declval())), 50 | bool>())); 51 | EXPECT_TRUE((std::is_same())); 53 | EXPECT_TRUE((std::is_same())); 55 | EXPECT_TRUE((std::is_same())); 57 | EXPECT_TRUE((std::is_same::pointer, 59 | typename TypeParam::pointer>())); 60 | EXPECT_TRUE( 61 | (std::is_same::const_pointer, 63 | typename TypeParam::const_pointer>())); 64 | } 65 | 66 | TYPED_TEST_P(MembersTest, SimpleFunctions) { 67 | EXPECT_GT(TypeParam().max_size(), 0); 68 | } 69 | 70 | TYPED_TEST_P(MembersTest, BeginEnd) { 71 | TypeParam t = {typename TypeParam::value_type{}}; 72 | EXPECT_EQ(t.begin(), t.cbegin()); 73 | EXPECT_EQ(t.end(), t.cend()); 74 | EXPECT_NE(t.begin(), t.end()); 75 | EXPECT_NE(t.cbegin(), t.cend()); 76 | } 77 | 78 | REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); 79 | 80 | } // namespace priv 81 | } // namespace phmap 82 | 83 | #endif // PHMAP_PRIV_UNORDERED_SET_MEMBERS_TEST_H_ 84 | --------------------------------------------------------------------------------