Module: Rperf::Table
- Defined in:
- lib/rperf/table.rb
Constant Summary collapse
- ROWS_LIMIT =
50
Class Method Summary collapse
- .diff_json(base, head) ⇒ Object
-
.diff_rows(base, head, limit: ROWS_LIMIT) ⇒ Object
Rows: method, self_pct_base, self_pct_head, delta_pt — |delta_pt| descending, top 50.
- .diff_summary(base, head) ⇒ Object
- .diff_tsv(base, head) ⇒ Object
-
.flat_cum_by_name(data) ⇒ Object
Flat / cumulative weights merged by method name (compute_flat_cum keys are [label, path]; a method split across paths counts once).
- .ms(ns) ⇒ Object
- .pct(weight, total) ⇒ Object
- .report_json(data) ⇒ Object
-
.report_rows(data, limit: ROWS_LIMIT) ⇒ Object
Rows: method, self_pct, total_pct, self_ms — self_pct descending, top 50 plus an “(other)” row aggregating the rest.
- .report_summary(data) ⇒ Object
- .report_tsv(data) ⇒ Object
- .summary_line(summary) ⇒ Object
-
.tsv_cell(value) ⇒ Object
TSV has no escaping convention — replace separator characters so a pathological method name (define_method allows any string) cannot shift columns or split a row.
Class Method Details
.diff_json(base, head) ⇒ Object
119 120 121 |
# File 'lib/rperf/table.rb', line 119 def diff_json(base, head) JSON.generate(diff_rows(base, head) + [{ summary: diff_summary(base, head) }]) end |
.diff_rows(base, head, limit: ROWS_LIMIT) ⇒ Object
Rows: method, self_pct_base, self_pct_head, delta_pt — |delta_pt| descending, top 50. Per-method allocation data does not exist in rperf profiles, so allocation appears only in the summary.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rperf/table.rb', line 78 def diff_rows(base, head, limit: ROWS_LIMIT) base_flat, _, base_total = flat_cum_by_name(base) head_flat, _, head_total = flat_cum_by_name(head) names = (base_flat.keys | head_flat.keys) rows = names.map do |name| b = pct(base_flat[name] || 0, base_total) h = pct(head_flat[name] || 0, head_total) { method: name, self_pct_base: b, self_pct_head: h, delta_pt: (h - b).round(2), } end rows.sort_by! { |r| [-r[:delta_pt].abs, r[:method]] } rows.first(limit) end |
.diff_summary(base, head) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/rperf/table.rb', line 96 def diff_summary(base, head) b = report_summary(base) h = report_summary(head) out = {} %i[total_ms allocated_objects gc_count_minor gc_count_major].each do |key| out[:"#{key}_base"] = b[key] if b[key] out[:"#{key}_head"] = h[key] if h[key] out[:"#{key}_delta"] = (h[key] - b[key]).round(1) if b[key] && h[key] end out end |
.diff_tsv(base, head) ⇒ Object
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/rperf/table.rb', line 108 def diff_tsv(base, head) rows = diff_rows(base, head) out = String.new out << "method\tself_pct_base\tself_pct_head\tdelta_pt\n" rows.each do |r| out << [tsv_cell(r[:method]), r[:self_pct_base], r[:self_pct_head], r[:delta_pt]].join("\t") << "\n" end out << summary_line(diff_summary(base, head)) out end |
.flat_cum_by_name(data) ⇒ Object
Flat / cumulative weights merged by method name (compute_flat_cum keys are [label, path]; a method split across paths counts once).
127 128 129 130 131 132 133 134 |
# File 'lib/rperf/table.rb', line 127 def flat_cum_by_name(data) result = Rperf.send(:compute_flat_cum, data[:aggregated_samples] || []) flat = Hash.new(0) result[:flat].each { |(label, _path), w| flat[label] += w } cum = Hash.new(0) result[:cum].each { |(label, _path), w| cum[label] += w } [flat, cum, result[:total_weight]] end |
.ms(ns) ⇒ Object
140 141 142 |
# File 'lib/rperf/table.rb', line 140 def ms(ns) (ns / 1e6).round(1) end |
.pct(weight, total) ⇒ Object
136 137 138 |
# File 'lib/rperf/table.rb', line 136 def pct(weight, total) total > 0 ? (weight * 100.0 / total).round(2) : 0.0 end |
.report_json(data) ⇒ Object
69 70 71 |
# File 'lib/rperf/table.rb', line 69 def report_json(data) JSON.generate(report_rows(data) + [{ summary: report_summary(data) }]) end |
.report_rows(data, limit: ROWS_LIMIT) ⇒ Object
Rows: method, self_pct, total_pct, self_ms — self_pct descending, top 50 plus an “(other)” row aggregating the rest.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/rperf/table.rb', line 21 def report_rows(data, limit: ROWS_LIMIT) flat, cum, total = flat_cum_by_name(data) entries = flat.sort_by { |name, w| [-w, name] } rows = entries.first(limit).map do |name, w| { method: name, self_pct: pct(w, total), total_pct: pct(cum[name], total), self_ms: ms(w), } end if entries.size > limit # Aggregate the raw weights, not the already-rounded row values: # per-row rounding errors are systematic and would make the (other) # row inconsistent with itself (pct vs ms) and with the true total rest_weight = entries.drop(limit).sum { |_, w| w } rows << { method: "(other)", self_pct: pct(rest_weight, total), total_pct: nil, # overlapping cumulative values cannot be summed self_ms: ms(rest_weight), } end rows end |
.report_summary(data) ⇒ Object
47 48 49 50 51 52 53 54 55 56 |
# File 'lib/rperf/table.rb', line 47 def report_summary(data) s = data[:summary] || Meta.build_summary(data) out = {} out[:total_ms] = s[:total_ms] if s[:total_ms] out[:cpu_ms] = s[:cpu_ms] if s[:cpu_ms] out[:allocated_objects] = s[:allocated_objects] if s[:allocated_objects] out[:gc_count_minor] = s[:gc_count_minor] if s[:gc_count_minor] out[:gc_count_major] = s[:gc_count_major] if s[:gc_count_major] out end |
.report_tsv(data) ⇒ Object
58 59 60 61 62 63 64 65 66 67 |
# File 'lib/rperf/table.rb', line 58 def report_tsv(data) rows = report_rows(data) out = String.new out << "method\tself_pct\ttotal_pct\tself_ms\n" rows.each do |r| out << [tsv_cell(r[:method]), r[:self_pct], r[:total_pct], r[:self_ms]].map { |v| v.nil? ? "" : v.to_s }.join("\t") << "\n" end out << summary_line(report_summary(data)) out end |
.summary_line(summary) ⇒ Object
144 145 146 |
# File 'lib/rperf/table.rb', line 144 def summary_line(summary) (["# summary"] + summary.map { |k, v| "#{k}=#{v}" }).join("\t") + "\n" end |
.tsv_cell(value) ⇒ Object
TSV has no escaping convention — replace separator characters so a pathological method name (define_method allows any string) cannot shift columns or split a row.
151 152 153 154 |
# File 'lib/rperf/table.rb', line 151 def tsv_cell(value) s = value.to_s s.match?(/[\t\n\r]/) ? s.gsub(/[\t\n\r]/, " ") : s end |