invoke with bin/jemalloc rails s
We weren’t using 3 before, but have been running 5 in prod for ~10 months now, we saw ~25% less memory usage compared to default malloc
see also https://gist.github.com/jjb/9ff0d3f622c8bbe904fe7a82e35152fc
invoke with bin/jemalloc rails s
We weren’t using 3 before, but have been running 5 in prod for ~10 months now, we saw ~25% less memory usage compared to default malloc
see also https://gist.github.com/jjb/9ff0d3f622c8bbe904fe7a82e35152fc
| #!/usr/bin/env ruby | |
| # frozen_string_literal: true | |
| ENV['LD_PRELOAD'] = ENV['JEMALLOC_PATH'] | |
| ENV['MALLOC_CONF'] = { | |
| # Maximum number of arenas to use for automatic multiplexing of threads and arenas. | |
| # The default is four times the number of CPUs, or one if there is a single CPU. | |
| narenas: 2, | |
| # Enabling jemalloc background threads generally improves the tail latency for application threads, | |
| # since unused memory purging is shifted to the dedicated background threads. | |
| # In addition, unintended purging delay caused by application inactivity is avoided with background threads. | |
| # | |
| # Suggested: background_thread:true when jemalloc managed threads can be allowed. | |
| background_thread: true, | |
| # Transparent hugepage (THP) mode. | |
| # Settings "always", "never" and "default" are available if THP is supported by the operating system. | |
| # The "always" setting enables transparent hugepage for all user memory mappings with MADV_HUGEPAGE; | |
| # "never" ensures no transparent hugepage with MADV_NOHUGEPAGE; the default setting "default" makes no changes. | |
| thp: 'never', | |
| # Decay time determines how fast jemalloc returns unused pages back to the operating system, | |
| # and therefore provides a fairly straightforward trade-off between CPU and memory usage. | |
| # Shorter decay time purges unused pages faster to reduces memory usage | |
| # (usually at the cost of more CPU cycles spent on purging), and vice versa. | |
| # | |
| # Suggested: tune the values based on the desired trade-offs. | |
| dirty_decay_ms: 1_000, # Default 10_000 | |
| muzzy_decay_ms: 0 # Default 10_000 | |
| }.map { |conf| conf.join(':') }.join(',').freeze # Convert to a MALLOC_CONF string | |
| require 'pathname' | |
| ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', | |
| Pathname.new(__FILE__).realpath) | |
| bundle_binstub = File.expand_path('bundle', __dir__) | |
| if File.file?(bundle_binstub) | |
| if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300)) | |
| load(bundle_binstub) | |
| else | |
| abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. | |
| Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") | |
| end | |
| end | |
| require 'rubygems' | |
| require 'bundler/setup' | |
| arg = ARGV.shift | |
| if arg.nil? | |
| warn "Usage: #{$PROGRAM_NAME} executable_name" | |
| warn "Usage: #{$PROGRAM_NAME} gem_name/executable_name" | |
| exit(1) | |
| end | |
| # If the executable is the same as the gem, we can pass "executable_name" vs "gem_name/executable_name" | |
| gem, executable = arg.split('/') | |
| executable ||= gem | |
| gem = 'railties' if gem == 'rails' # Rails has no executables, they're stored in railties | |
| if gem.empty? | |
| warn('Exiting: Must supply a gem bin or gem_name/executable') | |
| exit(1) | |
| end | |
| load Gem.bin_path(gem, executable) |