Skip to content

Store captured samples in a hashmap during session#85

Merged
osyoyu merged 9 commits intomainfrom
session-samples-hashmap
Jan 18, 2026
Merged

Store captured samples in a hashmap during session#85
osyoyu merged 9 commits intomainfrom
session-samples-hashmap

Conversation

@osyoyu
Copy link
Copy Markdown
Owner

@osyoyu osyoyu commented Jan 18, 2026

This patch lets Pf2 store collected samples in a hashmap backed by khashl instead of a array. This allows far lower memory consumption - the original session->samples stored every single sample collected, making the profiler's memory footprint proportional to the target program's running time, but this new implementation only records counts of distinct stacks.

Not done here:

  • Make timestamp collection optional. That part is still a array
  • Reconsider storage of native stacks? This patch's impl considers the tuple of (ruby_stack, pc) for dedup, but those could be managed separately. However that change would probably require changes in the serialization format.

Benchmark

Max RSS of the following script:

seconds main this branch diff
18 s 382,404 KB 82,980 KB x0.22
36 s 706,932 KB 113,024 KB x0.16
54 s 1,039,900 KB 140,128 KB x0.13

(script written by Codex)

# frozen_string_literal: true

require 'pf2'

DURATION = (ENV['DURATION'] || '18').to_f
INTERVAL_MS = (ENV['INTERVAL_MS'] || '1').to_i
THREADS = (ENV['THREADS'] || '4').to_i

# CPU-heavy: mix of recursion + prime checks + bit ops

def tarai(x, y, z)
  x <= y ? y : tarai(tarai(x - 1, y, z), tarai(y - 1, z, x), tarai(z - 1, x, y))
end

def is_prime(n)
  return false if n < 2
  return true if n == 2
  return false if (n & 1) == 0
  i = 3
  while i * i <= n
    return false if (n % i).zero?
    i += 2
  end
  true
end

start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
profile = Pf2.profile(interval_ms: INTERVAL_MS, time_mode: :wall) do
  workers = THREADS.times.map do |idx|
    Thread.new do
      seed = 10_000 + (idx * 1000)
      while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) < DURATION
        tarai(12, 9, 1)
        n = seed
        200.times do
          is_prime(n)
          n += 1
        end
      end
    end
  end
  workers.each(&:join)
end

STDERR.puts "profile samples=#{profile[:samples].size} functions=#{profile[:functions].size}"

@osyoyu osyoyu merged commit d0ff1b5 into main Jan 18, 2026
12 checks passed
@osyoyu osyoyu deleted the session-samples-hashmap branch January 18, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant