#!/usr/bin/env ruby
# Memory impact of retaining many String#byteslice results, for
# https://bugs.ruby-lang.org/issues/22186.
#
# Usage: ruby misc/str_subseq_memory.rb [SIZE...]

require "objspace"

SIZES = ARGV.empty? ? [10, 50, 100, 250, 500, 999, 1000] : ARGV.map { |a| Integer(a) }
N = 200_000

SRC = ("0123456789abcdef" * 128).freeze

def rss_kb
  `ps -o rss= -p #{Process.pid}`.to_i
end

puts RUBY_DESCRIPTION
printf("%6s  %11s  %11s  %13s  %s\n", "size", "per-slice", "total", "RSS delta", "flags")

SIZES.each do |size|
  beg = SRC.bytesize - size
  r, w = IO.pipe
  fork do
    r.close
    GC.start
    before_mem = ObjectSpace.memsize_of_all(String)
    before_rss = rss_kb
    slices = Array.new(N) { SRC.byteslice(beg, size) }
    GC.start
    total = ObjectSpace.memsize_of_all(String) - before_mem
    rss = rss_kb - before_rss
    dump = ObjectSpace.dump(slices.first)
    flags = %w[embedded shared].select { |f| dump.include?("\"#{f}\":true") }.join(",")
    w.puts [ObjectSpace.memsize_of(slices.first), total, rss, flags].join(" ")
    slices.clear
  end
  w.close
  per, total, rss, flags = r.read.split
  Process.wait
  printf("%6d  %10dB  %9.1fMB  %11.1fMB  %s\n",
         size, per, total.to_i / 1024.0 / 1024, rss.to_i / 1024.0, flags || "(heap copy)")
end
