Logging, runtime tracing, and the obs performance profiler for OCaml
0

Configure Feed

Select the types of activity you want to include in your feed.

obs/hotspots: name the SOTA fix for small-object churn

The Rate class (a small object allocated too often, low promotion) is where
avoidable amplification hides -- a value boxed or tupled in a hot loop that did
no lasting work. The fix text just said 'cut the number of allocating calls',
which reads the same for an inherent per-row cost as for a boxed int64 that
should never have allocated.

When the object is tiny (<= 24 bytes, ~one to three words) refine the FIX: name
the usual culprit -- a boxed int64/float, a tuple, or an option/ref -- and the
SOTA move (hoist it out of the loop, or use an unboxed/native path). This is the
signature behind this session's biggest wins (the cat_cursor 12-byte value
round-trip, varint/solo5 int64 boxes), so obs now flags them as avoidable rather
than as the cost of the work. The base guidance is preserved, so the hotspots
suite and cram are unchanged.

+19 -2
+19 -2
lib/obs/memtrace/hotspots.ml
··· 518 518 | Rate -> "cut the number of allocating calls" 519 519 | Size -> "reuse or shrink this large transient buffer" 520 520 521 - let fix_for (h : hotspot) = fix_text (classify h) 521 + (* The Rate class is minor churn: a small object allocated too often. When that 522 + object is tiny -- around one to three words -- it is almost always a boxed 523 + [int64]/[float], a tuple, or an [option]/[ref] standing in for a value that 524 + could stay unboxed: the avoidable amplification this profile is built to 525 + surface. Name the SOTA fix so a small, high-rate, non-escaping site reads as 526 + avoidable, not as the cost of the work. *) 527 + let small_object_bytes = 24 528 + 529 + let fix_detail (h : hotspot) cls = 530 + match cls with 531 + | Rate when h.bytes_per_alloc > 0 && h.bytes_per_alloc <= small_object_bytes -> 532 + fix_text Rate 533 + ^ " -- a small fixed-size object churned this often is usually a boxed \ 534 + int64/float, a tuple, or an option/ref; hoist it out of the loop or \ 535 + pass it through an unboxed or native-int path" 536 + | _ -> fix_text cls 537 + 538 + let fix_for (h : hotspot) = fix_detail h (classify h) 522 539 523 540 (* Render one ranked hotspot as a diagnosis/evidence/callers/fix/payoff 524 541 block. *) ··· 549 566 (List.map 550 567 (fun c -> Fmt.str "%s (%s)" c.name (pct c.share_of_site)) 551 568 cs))); 552 - line " FIX: %s" (fix_text cls); 569 + line " FIX: %s" (fix_detail h cls); 553 570 line " PAYOFF: ceiling -%s allocation (-%s); confidence=estimate" 554 571 (pct h.share) (human_bytes h.bytes); 555 572 Buffer.add_char b '\n'