├── .gitignore ├── LICENSE.txt ├── README.md ├── make_jemallocs.rb ├── make_mimalloc.rb ├── make_rpmalloc.rb ├── make_tcmallocs.rb ├── stress_mem.rb └── test_all.rb /.gitignore: -------------------------------------------------------------------------------- 1 | gperftools/* 2 | Hoard/* 3 | jemalloc/* 4 | jemalloc_lib/* 5 | lockless_allocator/* 6 | tcmalloc_lib/* 7 | Hoard 8 | gperftools/* 9 | gperftools 10 | jemalloc 11 | mimalloc_lib 12 | mimalloc 13 | rpmalloc_lib 14 | rpmalloc 15 | 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Sam Saffron 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | To run benchmarks 2 | 3 | 1. Install lockless from https://locklessinc.com/downloads/ in lockless_allocator path make 4 | 2. Install Hoard from https://github.com/emeryberger/Hoard in Hoard and make 5 | 4. Run `ruby make_jemallocs.rb` 6 | 5. Run `ruby make_tcmallocs.rb` # You need to install libunwind-dev before 7 | 3. Run `ruby make_mimalloc.rb` 8 | 6. Run `ruby test_all.rb` 9 | 7. Run `STRESS_THREAD=10 ruby test_all.rb` 10 | 11 | Results: (current) 12 | 13 | Use `STRESS_THREADS=N` to simulate multi threaded ruby behavior 14 | 15 | ### 8 threads 16 | 17 | 18 | ``` 19 | sam@arch allocator_bench % STRESS_THREADS=8 ruby test_all.rb 20 | ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux] 21 | built-in mem: 186336 duration: 3.422897896 22 | built-in mem (MALLOC_ARENA_MAX=2): 135780 duration: 3.492815563 23 | mimalloc master: 236204 duration: 2.947209624 24 | rpmalloc master: 291428 duration: 2.823917527 25 | tcmalloc 2.0 mem: 142860 duration: 2.796963668 26 | tcmalloc 2.1 mem: 134952 duration: 2.9891215 27 | tcmalloc 2.2 mem: 131860 duration: 2.965217507 28 | tcmalloc 2.3 mem: 132268 duration: 2.992975175 29 | tcmalloc 2.4 mem: 141756 duration: 2.817269882 30 | tcmalloc 2.5 mem: 136376 duration: 2.763516053 31 | tcmalloc 2.6 mem: 135892 duration: 2.837330961 32 | tcmalloc 2.6.1 mem: 138776 duration: 2.800292649 33 | tcmalloc 2.6.2 mem: 133392 duration: 2.785083378 34 | tcmalloc 2.6.3 mem: 128448 duration: 2.870347233 35 | tcmalloc 2.7 mem: 134840 duration: 2.816580265 36 | jemalloc 3.5.1 mem: 153808 duration: 2.98163907 37 | jemalloc 3.6.0 mem: 139564 duration: 3.122444062 38 | jemalloc 4.0.0 mem: 151132 duration: 3.001511518 39 | jemalloc 4.0.1 mem: 152276 duration: 3.017984175 40 | jemalloc 4.0.2 mem: 150684 duration: 2.993723311 41 | jemalloc 4.0.3 mem: 157552 duration: 2.955524769 42 | jemalloc 4.0.4 mem: 153460 duration: 2.995114601 43 | jemalloc 4.1.0 mem: 148816 duration: 2.962291218 44 | jemalloc 4.1.1 mem: 146660 duration: 2.951603234 45 | jemalloc 4.2.0 mem: 147800 duration: 2.956029203 46 | jemalloc 4.2.1 mem: 148324 duration: 2.968610441 47 | jemalloc 4.3.0 mem: 169020 duration: 3.110291342 48 | jemalloc 4.3.1 mem: 151936 duration: 2.967231767 49 | jemalloc 4.4.0 mem: 160780 duration: 2.977113841 50 | jemalloc 4.5.0 mem: 158028 duration: 2.949980599 51 | jemalloc 5.0.0 mem: 157076 duration: 3.068324983 52 | jemalloc 5.0.1 mem: 169572 duration: 2.905734288 53 | jemalloc 5.1.0 mem: 153852 duration: 2.925958087 54 | jemalloc 5.2.0 mem: 162008 duration: 2.837378225 55 | jemalloc 5.2.1 mem: 155912 duration: 2.922026484 56 | lockless 1.4 mem: 249540 duration: 2.858882662 57 | Hoard HEAD mem: 182500 duration: 2.988038568 58 | ``` 59 | 60 | 61 | ### Single threaded 62 | 63 | ``` 64 | sam@arch allocator_bench % STRESS_THREADS=1 ruby test_all.rb 65 | ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux] 66 | built-in mem: 169444 duration: 3.295604446 67 | built-in mem (MALLOC_ARENA_MAX=2): 162400 duration: 3.309020006 68 | mimalloc master: 143520 duration: 3.216014001 69 | rpmalloc master: 158732 duration: 3.183002545 70 | tcmalloc 2.0 mem: 137736 duration: 3.21834025 71 | tcmalloc 2.1 mem: 138532 duration: 3.199263054 72 | tcmalloc 2.2 mem: 171124 duration: 2.793094939 73 | tcmalloc 2.3 mem: 140144 duration: 3.157911078 74 | tcmalloc 2.4 mem: 169384 duration: 2.777076104 75 | tcmalloc 2.5 mem: 169816 duration: 2.703555291 76 | tcmalloc 2.6 mem: 172612 duration: 2.619339545 77 | tcmalloc 2.6.1 mem: 172512 duration: 2.601444421 78 | tcmalloc 2.6.2 mem: 144872 duration: 2.887256198 79 | tcmalloc 2.6.3 mem: 159620 duration: 2.526488765 80 | tcmalloc 2.7 mem: 173096 duration: 2.619000445 81 | jemalloc 3.5.1 mem: 144348 duration: 3.04128121 82 | jemalloc 3.6.0 mem: 144320 duration: 3.053938303 83 | jemalloc 4.0.0 mem: 182760 duration: 3.016305146 84 | jemalloc 4.0.1 mem: 169596 duration: 2.972724988 85 | jemalloc 4.0.2 mem: 168892 duration: 2.928217891 86 | jemalloc 4.0.3 mem: 168924 duration: 2.952081469 87 | jemalloc 4.0.4 mem: 161744 duration: 2.820295919 88 | jemalloc 4.1.0 mem: 169072 duration: 2.915838045 89 | jemalloc 4.1.1 mem: 169064 duration: 2.949833676 90 | jemalloc 4.2.0 mem: 179084 duration: 2.922669411 91 | jemalloc 4.2.1 mem: 169128 duration: 2.900649117 92 | jemalloc 4.3.0 mem: 176408 duration: 3.083000318 93 | jemalloc 4.3.1 mem: 168748 duration: 2.90019844 94 | jemalloc 4.4.0 mem: 168928 duration: 2.896176489 95 | jemalloc 4.5.0 mem: 174472 duration: 2.785766161 96 | jemalloc 5.0.0 mem: 149940 duration: 2.896406334 97 | jemalloc 5.0.1 mem: 150204 duration: 2.837875746 98 | jemalloc 5.1.0 mem: 148476 duration: 2.824247363 99 | jemalloc 5.2.0 mem: 168224 duration: 2.763762322 100 | jemalloc 5.2.1 mem: 149664 duration: 2.930834257 101 | lockless 1.4 mem: 164468 duration: 2.873501934 102 | Hoard HEAD mem: 174396 duration: 2.903436464 103 | ``` 104 | 105 | 106 | Results: (old) 107 | 108 | ``` 109 | sam@ubuntu allocator_bench % ruby test_all.rb 110 | ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux] 111 | built-in mem: 136560 duration: 4.92845984 112 | Using the Hoard memory allocator (http://www.hoard.org), version 3.12.0 113 | Using the Hoard memory allocator (http://www.hoard.org), version 3.12.0 114 | Using the Hoard memory allocator (http://www.hoard.org), version 3.12.0 115 | Using the Hoard memory allocator (http://www.hoard.org), version 3.12.0 116 | Hoard HEAD mem: 167772 duration: 4.231493358 117 | lockless 1.4 mem: 166860 duration: 3.567809758 118 | tcmalloc 2.0 mem: 138608 duration: 3.96352459 119 | tcmalloc 2.1 mem: 145676 duration: 3.821032815 120 | tcmalloc 2.2 mem: 141204 duration: 3.830105653 121 | tcmalloc 2.3 mem: 139096 duration: 3.777037605 122 | tcmalloc 2.4 mem: 137548 duration: 3.826598715 123 | tcmalloc 2.5 mem: 139524 duration: 3.825563646 124 | tcmalloc 2.6 mem: 141256 duration: 3.857502124 125 | tcmalloc 2.6.1 mem: 141500 duration: 3.778023667 126 | tcmalloc 2.6.2 mem: 141244 duration: 3.798630141 127 | tcmalloc 2.6.3 mem: 140320 duration: 3.824998923 128 | tcmalloc 2.7 mem: 143192 duration: 3.726972205 129 | jemalloc 3.0.0 mem: 141232 duration: 4.177167928 130 | jemalloc 3.1.0 mem: 162304 duration: 3.939940393 131 | jemalloc 3.2.0 mem: 165188 duration: 3.929158876 132 | jemalloc 3.3.0 mem: 165164 duration: 4.135042063 133 | jemalloc 3.3.1 mem: 153676 duration: 4.08339298 134 | jemalloc 3.4.0 mem: 142332 duration: 4.287041471 135 | jemalloc 3.4.1 mem: 165240 duration: 4.025545814 136 | jemalloc 3.5.0 mem: 133892 duration: 4.065554619 137 | jemalloc 3.5.1 mem: 165216 duration: 4.079860268 138 | jemalloc 3.6.0 mem: 165164 duration: 4.233464591 139 | jemalloc 4.0.0 mem: 141336 duration: 4.574956268 140 | jemalloc 4.0.1 mem: 170944 duration: 4.248128008 141 | jemalloc 4.0.2 mem: 177168 duration: 4.329534696 142 | jemalloc 4.0.3 mem: 173996 duration: 3.920414509 143 | jemalloc 4.0.4 mem: 166488 duration: 4.043237997 144 | jemalloc 4.1.0 mem: 144080 duration: 4.084126294 145 | jemalloc 4.1.1 mem: 140752 duration: 4.122630669 146 | jemalloc 4.2.0 mem: 141840 duration: 4.434104776 147 | jemalloc 4.2.1 mem: 142844 duration: 4.346471957 148 | jemalloc 4.3.0 mem: 176972 duration: 4.883788911 149 | jemalloc 4.3.1 mem: 142052 duration: 4.234083442 150 | jemalloc 4.4.0 mem: 142344 duration: 4.159591159 151 | jemalloc 4.5.0 mem: 139484 duration: 3.954587993 152 | jemalloc 5.0.0 mem: 166592 duration: 4.013335086 153 | jemalloc 5.0.1 mem: 173500 duration: 3.979304747 154 | ``` 155 | 156 | 157 | ``` 158 | sam@ubuntu allocators % ruby test_all.rb 159 | ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-linux] 160 | built-in mem: 177816 duration: 5.463205008 161 | Hoard 3.12 mem: 191836 duration: 4.526371788 162 | lockless 1.3 mem: 196888 duration: 3.689470492 163 | tcmalloc 2.0 mem: 177840 duration: 4.184403986 164 | tcmalloc 2.1 mem: 178952 duration: 4.047054641 165 | tcmalloc 2.2 mem: 180420 duration: 4.021828904 166 | tcmalloc 2.3 mem: 179756 duration: 3.940098988 167 | tcmalloc 2.4 mem: 168828 duration: 4.073669654 168 | tcmalloc 2.5 mem: 171492 duration: 4.087337723 169 | jemalloc 3.0.0 mem: 171652 duration: 4.250548191 170 | jemalloc 3.1.0 mem: 172364 duration: 4.200444839 171 | jemalloc 3.2.0 mem: 176748 duration: 4.249052335 172 | jemalloc 3.3.0 mem: 177104 duration: 4.173927908 173 | jemalloc 3.3.1 mem: 177476 duration: 4.200211943 174 | jemalloc 3.4.0 mem: 176948 duration: 4.246032144 175 | jemalloc 3.4.1 mem: 177436 duration: 4.334329763 176 | jemalloc 3.5.0 mem: 177416 duration: 4.310483395 177 | jemalloc 3.5.1 mem: 177148 duration: 4.314614055 178 | jemalloc 3.6.0 mem: 176984 duration: 4.387391573 179 | jemalloc 4.0.0 mem: 192256 duration: 4.594605504 180 | jemalloc 4.0.1 mem: 185760 duration: 4.415316793 181 | jemalloc 4.0.2 mem: 191228 duration: 4.264772339 182 | jemalloc 4.0.3 mem: 192276 duration: 4.406336821 183 | jemalloc 4.0.4 mem: 193844 duration: 4.292252613 184 | jemalloc 4.1.0 mem: 184900 duration: 4.185297596 185 | jemalloc 4.1.1 mem: 185212 duration: 4.174412318 186 | jemalloc 4.2.0 mem: 185148 duration: 4.142911436 187 | jemalloc 4.2.1 mem: 189636 duration: 4.116592894 188 | jemalloc 4.3.0 mem: 230164 duration: 7.376474329 189 | jemalloc 4.3.1 mem: 186060 duration: 4.103093871 190 | jemalloc 4.4.0 mem: 182524 duration: 4.137382241 191 | ``` 192 | -------------------------------------------------------------------------------- /make_jemallocs.rb: -------------------------------------------------------------------------------- 1 | @pwd = `pwd`.strip 2 | 3 | unless Dir.exist? 'jemalloc' 4 | puts "Checking out initial source" 5 | `git clone https://github.com/jemalloc/jemalloc` 6 | end 7 | 8 | tags = `cd jemalloc && git tag && cd -`.split("\n") 9 | 10 | def build_tag(tag) 11 | p "Branch with tag: #{tag}" 12 | `mkdir -p #{@pwd}/jemalloc_lib/#{tag}` 13 | `cd jemalloc && git clean -f && git checkout #{tag} && autoconf && ./configure --prefix=#{@pwd}/jemalloc_lib/#{tag} && make && make install_bin install_include install_lib && make relclean && git clean -f && cd -` 14 | end 15 | 16 | tags.each do |t| 17 | build_tag(t) if t > "3.5.0" 18 | end 19 | -------------------------------------------------------------------------------- /make_mimalloc.rb: -------------------------------------------------------------------------------- 1 | @pwd = `pwd`.strip 2 | 3 | unless Dir.exist? 'mimalloc' 4 | puts "Checking out initial source" 5 | `git clone https://github.com/microsoft/mimalloc.git` 6 | end 7 | 8 | def build_master 9 | `mkdir -p #{@pwd}/mimalloc_lib` 10 | Dir.chdir("mimalloc") do 11 | `mkdir -p out/release` 12 | Dir.chdir("out/release") do 13 | `cmake ../..` 14 | `make` 15 | `cp libmimalloc.so ../../../mimalloc_lib` 16 | end 17 | end 18 | end 19 | 20 | build_master 21 | 22 | -------------------------------------------------------------------------------- /make_rpmalloc.rb: -------------------------------------------------------------------------------- 1 | @pwd = `pwd`.strip 2 | 3 | unless Dir.exist? 'rpmalloc' 4 | puts "Checking out initial source" 5 | `git clone https://github.com/mjansson/rpmalloc.git` 6 | end 7 | 8 | def build_master 9 | `mkdir -p #{@pwd}/rpmalloc_lib` 10 | Dir.chdir("rpmalloc") do 11 | `python configure.py -c release` 12 | `ninja` 13 | `cp bin/linux/release/x86-64/librpmallocwrap.so ../rpmalloc_lib` 14 | end 15 | end 16 | 17 | build_master 18 | 19 | -------------------------------------------------------------------------------- /make_tcmallocs.rb: -------------------------------------------------------------------------------- 1 | @pwd = `pwd`.strip 2 | 3 | unless Dir.exist? 'gperftools' 4 | puts "Checking out initial source" 5 | `git clone https://github.com/gperftools/gperftools.git` 6 | end 7 | 8 | 9 | def build_tag(tag) 10 | prefix = "#{@pwd}/tcmalloc_lib/#{tag.gsub("gperftools-", "")}" 11 | `mkdir -p #{prefix}` 12 | `cd gperftools && rm -fr .gitignore && git clean -f && git reset --hard && git checkout #{tag} && ./autogen.sh && ./configure --prefix=#{prefix} && cd -` 13 | 14 | if tag =~ /2\.0/ 15 | patch = File.read("gperftools/src/base/linuxthreads.cc") 16 | patch.gsub!("static void SignalHandler(int signum, siginfo_t *si, void *data)", "static void SignalHandler(int signum, siginfo *si, void *data)") 17 | File.write("gperftools/src/base/linuxthreads.cc", patch) 18 | end 19 | 20 | `cd gperftools && make install && cd -` 21 | end 22 | 23 | 24 | build_tag("gperftools-2.0") 25 | build_tag("gperftools-2.1") 26 | build_tag("gperftools-2.2") 27 | build_tag("gperftools-2.3") 28 | build_tag("gperftools-2.4") 29 | build_tag("gperftools-2.5") 30 | build_tag("gperftools-2.6") 31 | build_tag("gperftools-2.6.1") 32 | build_tag("gperftools-2.6.2") 33 | build_tag("gperftools-2.6.3") 34 | build_tag("gperftools-2.7") 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /stress_mem.rb: -------------------------------------------------------------------------------- 1 | @retained = [] 2 | 3 | SEED=1 4 | MAX_STRING_SIZE = 100 5 | RAND = Random.new(SEED) 6 | LARGE_ALLOC_MAX_SIZE = 100_000 7 | 8 | 9 | def stress(allocate_count, retain_count, chunk_size) 10 | chunk = [] 11 | while retain_count > 0 || allocate_count > 0 12 | if retain_count == 0 || (RAND.rand < 0.5 && allocate_count > 0) 13 | chunk << " " * RAND.rand(MAX_STRING_SIZE) 14 | allocate_count -= 1 15 | if chunk.length > chunk_size 16 | chunk = [" " * RAND.rand(LARGE_ALLOC_MAX_SIZE)] 17 | end 18 | else 19 | @retained << " " * Random.rand(MAX_STRING_SIZE) 20 | retain_count -= 1 21 | end 22 | end 23 | end 24 | 25 | start = Time.now 26 | 27 | threads = [1, ENV["STRESS_THREADS"].to_i].max 28 | 29 | (0...threads).map do 30 | Thread.new do 31 | stress(12_000_000/threads, 600_000/threads, 200_000/threads) 32 | end 33 | end.each(&:join) 34 | 35 | duration = (Time.now - start).to_f 36 | 37 | _, size = `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i) 38 | puts "#{size},#{duration}" 39 | -------------------------------------------------------------------------------- /test_all.rb: -------------------------------------------------------------------------------- 1 | pwd = `pwd`.strip 2 | 3 | ENV.delete 'LD_PRELOAD' 4 | ENV.keys.each do |k| 5 | if k =~ /^RUBY_/ 6 | ENV.delete k 7 | end 8 | end 9 | 10 | puts `ruby --version` 11 | mem,duration = `ruby stress_mem.rb`.strip.split(",") 12 | puts "built-in mem: #{mem} duration: #{duration}" 13 | 14 | mem,duration = `MALLOC_ARENA_MAX=2 ruby stress_mem.rb`.strip.split(",") 15 | puts "built-in mem (MALLOC_ARENA_MAX=2): #{mem} duration: #{duration}" 16 | 17 | mem,duration = `LD_PRELOAD=#{pwd}/mimalloc_lib/libmimalloc.so ruby stress_mem.rb`.strip.split(",") 18 | puts "mimalloc master: #{mem} duration: #{duration}" 19 | 20 | mem,duration = `LD_PRELOAD=#{pwd}/rpmalloc_lib/librpmallocwrap.so ruby stress_mem.rb`.strip.split(",") 21 | puts "rpmalloc master: #{mem} duration: #{duration}" 22 | 23 | Dir["tcmalloc_lib/*"].sort.each do |path| 24 | mem,duration = `LD_PRELOAD=#{pwd}/#{path}/lib/libtcmalloc_minimal.so ruby stress_mem.rb`.strip.split(",") 25 | puts "#{path.gsub('_lib/', ' ')} mem: #{mem} duration: #{duration}" 26 | end 27 | 28 | Dir["jemalloc_lib/*"].sort.each do |path| 29 | mem,duration = `LD_PRELOAD=#{pwd}/#{path}/lib/libjemalloc.so ruby stress_mem.rb`.strip.split(",") 30 | puts "#{path.gsub('_lib/', ' ')} mem: #{mem} duration: #{duration}" 31 | end 32 | 33 | mem,duration = `LD_PRELOAD=#{pwd}/lockless_allocator/libllalloc.so.1.4 ruby stress_mem.rb`.strip.split(",") 34 | puts "lockless 1.4 mem: #{mem} duration: #{duration}" 35 | 36 | mem,duration = `LD_PRELOAD=#{pwd}/Hoard/src/libhoard.so ruby stress_mem.rb`.strip.split(",") 37 | puts "Hoard HEAD mem: #{mem} duration: #{duration}" 38 | 39 | --------------------------------------------------------------------------------