Class: Hone::Correlator

Inherits:
Object
  • Object
show all
Defined in:
lib/hone/correlator.rb

Overview

Correlates static analysis findings with runtime profiler data to prioritize optimizations based on actual CPU and allocation impact.

Hone's core value proposition is that not all optimization opportunities are equal. A pattern detected in a hot method that consumes 10% of CPU time is far more valuable to fix than the same pattern in cold initialization code.

The Correlator bridges static and dynamic analysis by:

  1. Looking up which method contains each finding
  2. Retrieving CPU percentage from profiler data (if available)
  3. Retrieving allocation percentage from memory profile (if available)
  4. Assigning priority based on thresholds and optimization type
  5. Sorting findings by impact (hottest first)

Examples:

Basic usage with profile data

method_map = Hone::MethodMap.new.add_file("app.rb")
cpu_profile = Hone::ProfileData.load("cpu_profile.json")
memory_profile = Hone::MemoryProfile.load("memory_profile.json")
correlator = Hone::Correlator.new(method_map:, cpu_profile:, memory_profile:)

scanner = Hone::Scanner.new
findings = scanner.scan_file("app.rb")

prioritized = correlator.correlate(findings)
prioritized.each do |finding|
  puts "#{finding.priority}: #{finding.message} (#{finding.cpu_percent}% CPU, #{finding.alloc_percent}% alloc)"
end

Usage without profile data (static-only mode)

method_map = Hone::MethodMap.new.add_file("app.rb")
correlator = Hone::Correlator.new(method_map:)

findings = scanner.scan_file("app.rb")
enriched = correlator.correlate(findings)
# All findings will have priority: :unknown

See Also:

Constant Summary collapse

HOT_THRESHOLD =

Threshold for hot methods: >5% of CPU/allocation time These represent critical optimization targets

5.0
WARM_THRESHOLD =

Threshold for warm methods: 1-5% of CPU/allocation time These are worth optimizing but less urgent than hot

1.0
PRIORITIES =

Multi-dimension priority levels assigned to findings based on CPU/allocation usage

Returns:

  • (Array<Symbol>)

    Valid priority values

i[hot_cpu hot_alloc jit_unfriendly warm cold unknown].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(method_map:, cpu_profile: nil, memory_profile: nil, profile_data: nil, hot_threshold: HOT_THRESHOLD, warm_threshold: WARM_THRESHOLD) ⇒ Correlator

Creates a new Correlator with method mapping and optional profiler data.

Parameters:

  • method_map (MethodMap)

    Mapping of source locations to method definitions

  • cpu_profile (#cpu_percent_for, nil) (defaults to: nil)

    CPU profiler data responding to #cpu_percent_for(method) returning Float or nil. When nil, CPU-based priorities cannot be calculated.

  • memory_profile (#alloc_percent_for, nil) (defaults to: nil)

    Memory profiler data responding to #alloc_percent_for(method) returning Float or nil. When nil, allocation-based priorities cannot be calculated.

  • profile_data (#cpu_percent_for, nil) (defaults to: nil)

    DEPRECATED: Use cpu_profile instead. Kept for backward compatibility.

  • hot_threshold (Float) (defaults to: HOT_THRESHOLD)

    Threshold for hot methods (default: HOT_THRESHOLD). Methods with CPU or allocation usage above this are marked :hot_cpu or :hot_alloc.

  • warm_threshold (Float) (defaults to: WARM_THRESHOLD)

    Threshold for warm methods (default: WARM_THRESHOLD). Methods with usage between warm and hot thresholds are marked :warm.



72
73
74
75
76
77
78
79
80
# File 'lib/hone/correlator.rb', line 72

def initialize(method_map:, cpu_profile: nil, memory_profile: nil, profile_data: nil,
  hot_threshold: HOT_THRESHOLD, warm_threshold: WARM_THRESHOLD)
  @method_map = method_map
  # Support backward compatibility: profile_data is treated as cpu_profile
  @cpu_profile = cpu_profile || profile_data
  @memory_profile = memory_profile
  @hot_threshold = hot_threshold
  @warm_threshold = warm_threshold
end

Instance Attribute Details

#hot_thresholdFloat (readonly)

Returns Threshold for hot methods.

Returns:

  • (Float)

    Threshold for hot methods



83
84
85
# File 'lib/hone/correlator.rb', line 83

def hot_threshold
  @hot_threshold
end

#warm_thresholdFloat (readonly)

Returns Threshold for warm methods.

Returns:

  • (Float)

    Threshold for warm methods



86
87
88
# File 'lib/hone/correlator.rb', line 86

def warm_threshold
  @warm_threshold
end

Class Method Details

.filter_by_priority(findings, priority) ⇒ Array<Finding>

Returns findings filtered by priority level.

Parameters:

  • findings (Array<Finding>)

    Correlated findings

  • priority (Symbol)

    One of :hot, :warm, :cold, :unknown

Returns:

  • (Array<Finding>)

    Findings matching the given priority



124
125
126
# File 'lib/hone/correlator.rb', line 124

def self.filter_by_priority(findings, priority)
  findings.select { |f| f.priority == priority }
end

.group_by_priority(findings) ⇒ Hash<Symbol, Array<Finding>>

Groups findings by priority for reporting.

Parameters:

  • findings (Array<Finding>)

    Correlated findings

Returns:

  • (Hash<Symbol, Array<Finding>>)

    Findings grouped by priority



133
134
135
# File 'lib/hone/correlator.rb', line 133

def self.group_by_priority(findings)
  findings.group_by(&:priority)
end

.summary(findings) ⇒ Hash

Returns summary statistics for correlated findings.

Parameters:

  • findings (Array<Finding>)

    Correlated findings

Returns:

  • (Hash)

    Statistics including counts by priority



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/hone/correlator.rb', line 142

def self.summary(findings)
  by_priority = group_by_priority(findings)

  {
    total: findings.size,
    hot_cpu: by_priority[:hot_cpu]&.size || 0,
    hot_alloc: by_priority[:hot_alloc]&.size || 0,
    jit_unfriendly: by_priority[:jit_unfriendly]&.size || 0,
    warm: by_priority[:warm]&.size || 0,
    cold: by_priority[:cold]&.size || 0,
    unknown: by_priority[:unknown]&.size || 0,
    max_cpu_percent: findings.filter_map(&:cpu_percent).max || 0.0,
    total_cpu_percent: findings.filter_map(&:cpu_percent).sum,
    max_alloc_percent: findings.filter_map(&:alloc_percent).max || 0.0,
    total_alloc_percent: findings.filter_map(&:alloc_percent).sum
  }
end

Instance Method Details

#correlate(findings) ⇒ Array<Finding>

Correlates findings with runtime profile data.

For each finding, this method:

  1. Looks up the containing method using the MethodMap
  2. Queries the profiler for CPU percentage (if cpu_profile present)
  3. Queries the profiler for allocation percentage (if memory_profile present)
  4. Calculates priority based on thresholds and optimization type
  5. Returns enriched findings sorted by impact (descending)

Examples:

raw_findings = scanner.scan_file("hot_code.rb")
prioritized = correlator.correlate(raw_findings)

# Process hot CPU findings first
prioritized.select { |f| f.priority == :hot_cpu }.each do |finding|
  puts "URGENT: #{finding.message}"
end

Parameters:

  • findings (Array<Finding>)

    Raw findings from Scanner

Returns:

  • (Array<Finding>)

    Enriched findings with method_name, cpu_percent, alloc_percent, and priority populated, sorted by max impact descending



110
111
112
113
114
115
116
# File 'lib/hone/correlator.rb', line 110

def correlate(findings)
  enriched = findings.map do |finding|
    enrich_finding(finding)
  end

  sort_by_impact(enriched)
end