HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
mstange
samply
Rust2022–2025
mstange / samply

samply

Command-line sampling profiler for macOS, Linux, and Windows

13Distinct people
16Mentions
0Explicit recommenders
4.5KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Command-line sampling profiler for macOS, Linux, and Windows

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:56:07 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year16 dated mentions

Observed mentions: 2022-09-04 10:42:58 UTC to 2025-07-10 13:44:00 UTC.

02462022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 1 mentions; 1 submissions, 0 comments20222023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 4 mentions; 2 submissions, 2 comments20232024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 7 mentions; 1 submissions, 6 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 4 mentions; 1 submissions, 3 comments2025

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC110
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC422
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC716
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC413

What Hacker News says

16 sources
Every time I see people use flamegraphs it's the ancient Perl version. There's a much better version!!! Use the Go version of pprof: https://github.com/google/pprof Run it like `pprof -http : your_profile.out` and it will open a browser with a really nice interactive flamegraph (way better than the Perl version), plus a call graph, source line profiling, top functions, etc. etc. It's so much better. Don't use the Perl version. I should probably write a post showing how to do this. Another also-much-better alternative is Samply (https://github.com/mstange/samply) which uses the Firefox Profiler as a GUI. I don't like it quite as much as pprof but it's clearly still much better than what's in this article: https://share.firefox.dev/3j3PJoK
In Optimizing a Math Expression Parser in Rust
Thanks for your response. > I also know that I wouldn't pull out a profiler, unless I was experiencing unacceptable performance, which may not happen on my (or the reviewers') end at all. At some level it doesn't matter in that case, right? We only have a finite amount of time in the world to do things, and if it's fast enough then time is better spent elsewhere. For a GPU driver, I would imagine that unacceptable performance would be visible quite quickly in p99+ frame times, which the gaming community has figured out are important (they call them "1% lows"). My general philosophy having written Rust for a long time (and before that, C, Python and a bunch of other languages) is to nail correctness first, don't do anything too egregious performance-wise (so try and avoid quadratic or worse algorithms), then worry about performance only to
Read the rest
the extent that you can measure average and outlier performance. RAII really, really helps in getting correctness right. (Rust also helps with performance. Idiomatic Rust tends to be quite fast due to aggressive inlining, especially with PGO.) > ... which again rings really similar to the memory management story in C (in my head anyways). I think where I differ is that profilers are a fairly mature and well-understood technology. You can even put profiles of Linux or macOS programs directly into a web browser profiler view, and get a really nice idea of where time is being spent (e.g. [1]). By the way, try out samply or a similar tool some time, it's really easy to use. I just don't think it's possible to get there with any amount of tooling on top of C, especially with existing codebases -- the language is just not rich enough to support the kinds of annotations required for a sound static analysis ("sound" here means all memory safety bugs are in code not otherwise marked unsafe). So yes, they look formally similar, but the difference in practice is enormous. [1] https://github.com/mstange/samply
In RAII and the Rust/Linux Drama
There is no free lunch in software :) > using the syntax of C pointers (but without pointer arithmetic) Go structs, when they have their address taken, act the same way object references do in C# or Java (that is, *MyStruct). Taking an address of a struct in Go and assigning it to a location likely turns it into a heap allocation. Even when not, it is important to understand what a stack in Go is and how it is implemented: Go makes a different set of tradeoffs by using a virtual/managed stack. When more memory on stack is required - the request goes through an allocator and the new memory range is attached to a previous one in a linked-list fashion (if this has changed in recent versions - please correct me). This is rather effective but notably can suffer from locality issues where, for example, two adjacently accessed structs in a hot l
Read the rest
oop are placed in different memory locations. Modern CPUs can deal with such non-locality of data well, but it is a concern still. It also, in a way, makes it somewhat similar to the role Gen0 / Nursery GC heaps play in .NET and OpenJDK GC implementations - heap allocations just bump a pointer within thread-local allocation context, and if a specific heap has ran out of free memory - more memory is requested from GC (or a collection is triggered, or both). By nature of both having generational heaps and moving garbage collector, the data in the heap has high locality and consecutively allocated objects are placed in a linear fashion. One of the main drawbacks of such approach is much higher implementation complexity. Additionally, the Go choice comes at a tradeoff of making FFI (comparatively) expensive - you pay about 0.5-2ns for a call across interop into C in .NET (when statically linked, it's just a direct call + branch), where-as the price of FFI in GoGC is around 50ns (and also interacts worse with the executor if you block goroutines). Overall, the design behind memory management in Go is interesting and makes a lot of sense for the scenarios Go was intended for (lightweight networked microservices, CLI tooling, background daemons, etc.). However, it trades off smaller memory footprint for a significantly lower allocation throughput. Because GC in Go is, at least partially, write-barrier driven, it makes WBs much more expensive - something you pay for when you assign a Go struct pointer to a heap or not provably local location (I don't know if Go performs WB elision/cheap WB selection the way OpenJDK and .NET do). To explore this further, I put together a small demonstration[0] based on BenchmarksGame's BinaryTrees suite which stresses this exact scenario. Both Go and C# there can be additionally inspected with e.g. dtrace on macOS or most other native profilers like Samply[1]. > Now, in working with real C# code and real C# programmers, all this is false... > but if you want to store it in a field, you need to use some class type as a box, This does not correspond to language specification or the kind of code that is being written outside of enterprise. First of all, `ref T` syntax in C# is a much more powerful concept than a simple reference to a local variable. `ref T` in .NET is a 'byref' aka managed pointer. Byrefs can point to arbitrary memory and are GC-aware. This means that they can point to stack, unmanged memory (you can even mmap it directly) GC on NonGC heap object interiors, etc. If they point to GC heap object interiors, they are appropriately updated by GC when it moves the objects, and are ignored when they are not. They cannot be stored on heap, which does cause some tension, but if you have a pointer-rich deep data structure, then classes are a better choice almost every time. Byrefs can be held by ref structs and the most common example used everywhere today is Span<T> - internally it is (ref T _reference, int _length) and is used for interacting with and slicing of arbitrary contiguous memory. You can find additional details on low-level memory management techniques here: https://news.ycombinator.com/item?id=40963672 Byrefs, alongside regular C pointers in C#, support pointer arithmetics as well. Of course it is just as unsafe, but for targeted hot paths this is indispensable. You can use it for fancy things like vectorized byte pair count within a sequence without having to pin the memory: https://github.com/U8String/U8String/blob/split-refactor/Sou... Last but not least, the average line-of-business code indeed rarely uses structs - in the past it was mostly classes, today it is a mix of classes, records, and sometimes record structs for single-field wrappers (still rarely). However, C# is a multi-paradigm language with strong low-level capabilities and there is a sea of projects beyond enterprise that make use of all the new and old low-level features. It's something that both C# and .NET were designed in mind from the very beginning. In this regard, they brings you much closer to the metal than Go unless significant changes are introduced to it. If you're interested, feel free to explore Ryujinx[2] and Garnet[3] which are more recent examples of projects demonstrating suitability of C# in domains historically reserved for C, C++ and Rust. [0]: https://gist.github.com/neon-sunset/72e6aa57c6a4c5eb0e2711e1... [1]: https://github.com/mstange/samply [2]: https://github.com/search?q=repo%3ARyujinx%2FRyujinx+ref+str... [3]: https://github.com/search?q=repo%3Amicrosoft%2Fgarnet+struct...*
In Go structs are copied on assignment (and other things about Go I'd missed)
This is easy in most modern programming languages. JVM ecosystem has IntelliJ Idea profiler and similar advanced tools (AFAIK). .NET has VS/Rider/dotnet-trace profilers (they are very detailed) to produce flamegraphs. Then there are native profilers which can work with any AOT compiled language that produces canonically symbolicated binaries: Rust, C#/F#(AOT mode), Go, Swift, C++, etc. For example, you can do `samply record ./some_binary`[0] and then explore multi-threaded flamegraph once completed (I use it to profile C#, it's more convenient than dotTrace for preliminary perf work and is usually more than sufficient). [0] https://github.com/mstange/samply
In The Performance Impact of C++'s `final` Keyword
There's always room for improvement, for example, Samply [0] is a wonderful profiler that uses the same APIs that `perf` uses, but unwinds the stacks as they come rather than dumping them all to disk and then having to process them in bulk. Samply unwinds significantly faster than `perf` because it caches unwind information. That being said, this approach still has some limitations, such as that very deep stacks won't be unwound, as the size of the process stack the kernel sends is quite limited. - [0]: https://github.com/mstange/samply
In The return of the frame pointers
IMHO, perf's decision to write whole stacks directly to the disk and unwinding them as a post-process is a really bad design. It wastes disk space, and as the author pointed out, it also has a lot of IO overhead. As an alternative approach, https://github.com/mstange/samply processes data streamed from perf and unwinds it in realtime. The unwinding overhead is surprisingly low: it only takes around 1% of (single) CPU per CPU profiled. Solving the disk waste alone has been a tremendous improvement of profiling experience. As a bonus, the unwinding and symbolization works reliably while I frequently had postprocessing not terminating when using the perf CLI directly.
In Frame pointers vs. DWARF – my verdict

Showing the 15 most recent of 16 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.