Comment by shermantanktop

1 day ago

Flamegraphs are wonderful.

Me: looks at my code. "sure, ok, looks alright."

Me: looks at the resulting flamegraph. "what the hell is this?!?!?"

I've found all kinds of crazy stuff in codebases this way. Static initializers that aren't static, one-line logger calls that trigger expensive serialization, heavy string-parsing calls that don't memoize patterns, etc. Unfortunately some of those are my fault.

I also like icicle graphs for this. They're flamegraphs, but aggregated in the reverse order. (I.e. if you have calls A->B->C and D->E->C, then both calls to C are aggregated together, rather than being stacked on top of B and E respectively. It can make it easier to see what's wrong when you have a bunch of distinct codepaths that all invoke a common library where you're spending too much time.)

Regular flamegraphs are good too, icicle graphs are just another tool in the toolbox.

  • So someone else linked the original flamegraph site [0] and it describes icicle graphs as "inverting the y axis" but that's not only what's happening, right? You bucket top-down the stack opposed to bottom-up, correct?

    [0] https://www.brendangregg.com/flamegraphs.html

    • It's certainly possible that what I encountered, labeled as an 'icicle graph', is a nonstandard usage of the term. But if so, that's a shame. I don't think inverting the y-axis is useful by itself, the different bucketing is what makes for an actually useful change.

    • Right, what is needed is something trie-like, with the root being the most fine-grained call.

Also cool that when you open it in a new tab, the svg [0] is interactive! You can zoom in by clicking on sections, and there's a button to reset the zoom level.

[0]: https://questdb.com/images/blog/2026-01-13/before.svg

I always found profiling performance critical code and experimenting with optimisations to be one of the most enjoyable parts of development - probably because of the number of surprises that I encountered ("Why on Earth is that so slow?").

I might be very wrong in every way but, string parsing and or manipulating and memoiziation... sound like a super strange combo? For the first you know you're already doing expensive allocations, but the 2nd is also not a pattern I really see apart from in JS codebases. Could you provide more context on how this actually bit you in the behind? memoizing strings seems like a complicated and error prone "welp it feels better now" territory in my mind so I'm genuinely curious.

  • In Java it can be a bad toString() implementation hiding behind a + used for string assembly.

    Or another great one: new instances of ObjectMapper created inside a method for a single call and then thrown away.

  • > but the 2nd is also not a pattern I really see apart from in JS codebases.

    If you're referring to "one-line logger calls that trigger expensive serialization", it's also common in java.

I've never used flamegraphs but would like to know about them. Can you explain more? Or where should I start?