auto_tune.rb 1.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. #!/usr/bin/env ruby
  2. # Instantiate about one process per X MiB of available memory, scaling up to as
  3. # close to MAX_THREADS as possible while observing an upper bound based on the
  4. # number of virtual/logical CPUs. If there are fewer processes than
  5. # MAX_THREADS, add threads per process to reach MAX_THREADS.
  6. require 'etc'
  7. KB_PER_WORKER = 128 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
  8. MIN_WORKERS = 15
  9. MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
  10. MIN_THREADS_PER_WORKER = 1
  11. MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
  12. def meminfo(arg)
  13. File.open('/proc/meminfo') do |f|
  14. f.each_line do |line|
  15. key, value = line.split(/:\s+/)
  16. return value.split(/\s+/).first.to_i if key == arg
  17. end
  18. end
  19. fail "Unable to find `#{arg}' in /proc/meminfo!"
  20. end
  21. def auto_tune
  22. avail_mem = meminfo('MemAvailable') * 0.8 - MAX_THREADS * 1_024
  23. workers = [
  24. [(1.0 * avail_mem / KB_PER_WORKER).floor, MIN_WORKERS].max,
  25. [(Etc.nprocessors * MAX_WORKERS_PER_VCPU).ceil, MIN_WORKERS].max
  26. ].min
  27. threads_per_worker = [
  28. workers < MAX_THREADS ? (1.0 * MAX_THREADS / workers).ceil : -Float::INFINITY,
  29. MIN_THREADS_PER_WORKER
  30. ].max
  31. [workers, threads_per_worker]
  32. end
  33. p auto_tune if $0 == __FILE__