[HN Gopher] Zig Profiling on Apple Silicon
       ___________________________________________________________________
        
       Zig Profiling on Apple Silicon
        
       Author : signa11
       Score  : 87 points
       Date   : 2025-07-29 02:15 UTC (2 days ago)
        
 (HTM) web link (blog.bugsiki.dev)
 (TXT) w3m dump (blog.bugsiki.dev)
        
       | turnsout wrote:
       | This is cool! I'm anxious to dig into Zig on an M1, and this
       | helps.
       | 
       | Side note, the CSS is broken for me in Safari--I get this error:
       | Cannot load stylesheet https://blog.bugsiki.dev/assets/css/styles
       | heet.b609c58d5c11bb90b1a54e04005d74ad1ddf22165eb79f5533967e57df9c
       | 3b50.css. Failed integrity metadata check. Content length: 4943,
       | Expected content length: -1, Expected metadata:
       | sha256-tgnFjVwRu5CxpU4EAF10rR3fIhZet59VM5Z+V9+cO1A=
        
       | christophilus wrote:
       | Mostly unrelated, but I recently built a small utility in Zig,
       | Hare, Go, Odin, and V.
       | 
       | I never completed the Odin / V implementations since their
       | standard libraries didn't have argon2 (a requirement for me).
       | 
       | Of the group, I liked Odin's syntax and feel best. Hare was also
       | very nice. But Zig's optimized binary sizes were unbeatable. 96KB
       | vs 500KB for Hare and 2.5MB for Go.
       | 
       | Zig's standard library and tooling are impressive. Personally, I
       | wish more projects were written in Zig instead of Rust since Zig
       | doesn't seem to have the package explosion problem that Rust has.
        
         | Arnavion wrote:
         | There isn't anything fundamental about Zig that makes it immune
         | from package explosion. It simply isn't popular and stable
         | enough for that to have happened yet.
        
           | conradev wrote:
           | Zig's standard library is structured so that you only compile
           | what you need, so Zig has no problems adding richer
           | functionality to its standard library. This is influenced by
           | Zig's decision to only have a single compilation unit per
           | compile.
           | 
           | HTTP, for example, can go into Zig's standard library,
           | whereas C or Rust cannot do that. This helps control package
           | explosion.
        
             | sionisrecur wrote:
             | As far as I know rust has dead code elimination. Is the
             | problem that compilation takes longer?
        
             | Arnavion wrote:
             | The reason Rust doesn't have those things in libstd is
             | because it doesn't want to commit to backward compatibility
             | for them. Note that Rust is 1.0 and has no plans to switch
             | to 2.0.
             | 
             | Zig will presumably have the same care about backward
             | compatibility once it becomes stable.
        
             | SkiFire13 wrote:
             | On this topic, Rust just added an (unstable for now) option
             | to delay codegen until needed https://blog.rust-
             | lang.org/inside-rust/2025/07/15/call-for-t...
             | 
             | Just like Zig's decision it is a double edged sword: on one
             | side you can avoid compiling code that will not be needed.
             | However on the other side you make incremental compilation
             | worse, since you will have to recompile it more often.
             | 
             | > HTTP, for example, can go into Zig's standard library,
             | whereas C or Rust cannot do that.
             | 
             | Rust _could_ put HTTP in the standard library, the dead
             | code would simply be removed when linking. The reason it's
             | not in the stdlib has more to do with the fact that:
             | 
             | - it would need to be supported by a team that's already
             | overworked - it would need to be supported indefinitely, so
             | it would have to be the right interface on the first try.
             | 
             | This effectively prevents any big and complex feature from
             | making to the Rust stdlib.
             | 
             | > This helps control package explosion.
             | 
             | Arguably it helps controlling the number of "packages", but
             | not the amount of code in those packages. If I split a
             | "package" in 4 crates in Rust to get better compile times
             | it's still mostly the same code, but you now see a number 4
             | times bigger.
        
               | BrouteMinou wrote:
               | And potentially, 4 times the number of different authors,
               | and 4 times the supply chain attack potential.
               | 
               | It's not just about the number of bytes.
        
               | conradev wrote:
               | It's funny because the biggest impact in my eyes is
               | social and not technical: the standard library is
               | controlled by project governance whereas libraries are
               | not.
               | 
               | Zig moves fast with it's BFDL structure, Rust moves slow
               | with its committees, and both are by design. I'm excited
               | to watch the Zig standard library evolve quickly,
               | especially once the language has settled things like I/O.
        
             | mananaysiempre wrote:
             | > HTTP, for example, can go into Zig's standard library,
             | whereas C or Rust cannot do that.
             | 
             | Yes they can? If you look at old-timey C libraries, for
             | example (standard or otherwise), you'll see that they have
             | the relatively awkward structure of one public function per
             | file. That's an adaptation for static linking, which only
             | pulls in the object files that contain symbols required by
             | the program, thus it's beneficial to make the object (and
             | therefore source) files as small as possible. Yes, the
             | compiler still needs to produce of these object files, but
             | unlike with Zig it only needs to do so once per (separately
             | compiled) library.
             | 
             | An inferior emulation of the same trick, popular in the
             | embedded community, is passing --gc-sections to ld, but
             | unless you're very careful something will fall off that you
             | weren't planning on. (Of course C++ makes things more
             | complicated, as always, so you may be forced to use this
             | option.)
             | 
             | Glibc's (and specifically Drepper's) aversion to static
             | linking did us all a disservice by making static libraries
             | less popular.
             | 
             | This does not mean that there isn't some benefit to whole-
             | program compilation. E.g. GCC can derive assumptions about
             | a non-exported function's arguments and propagate them into
             | the function's body even if it does not decide to inline it
             | (look for "constprop" symbols in objdump). So that's also a
             | thing that people do, in particular in gamedev where the
             | somewhat stylized source code structure it requires is less
             | of a problem. (Game developers confusingly call this a
             | "unity" build--personally I prefer the term "amalgamation"
             | used by Lua and SQLite.)
        
             | Someone wrote:
             | > Zig's standard library is structured so that you only
             | compile what you need,
             | 
             | > so Zig has no problems adding richer functionality to its
             | standard library.
             | 
             | That's orthogonal to both
             | 
             | - whether to compile from source in every compilation unit
             | or to compile once, and use (shared) libraries,
             | 
             | and to
             | 
             | - whether to provide the standard library as a single unit,
             | as is typically done in C, where it is a (shared) library,
             | or as multiple interdependent smaller units as in Zig,
             | where the standard library contains multiple source files.
             | 
             | > HTTP, for example, can go into Zig's standard library,
             | 
             | The claim "you only compile what you need" also is
             | incorrect. Zig's source files (luckily) aren't fine-grained
             | enough to support that.
             | 
             | For example, https://github.com/ziglang/zig/blob/master/lib
             | /std/http/Clie... contains stuff that not every user of
             | that module will need, for example connection pools and a
             | function to flush a buffer.
             | 
             | The compiler may not generate code for such unused
             | definitions, but it has to parse them, if only to figure
             | out where the next definition starts.
        
               | ozgrakkurt wrote:
               | I guess the difference is zig doesn't even properly type
               | check a function if you didn't use it. Whereas rust
               | relies on the linker to remove machine code after that
               | code went through the entire compiler pipeline.
               | 
               | Zig not even type checking some code when compiling felt
               | a bit off but now I just try to make sure all code is at
               | least being smoke tested, so I don't get random
               | compilation errors down the line
        
         | CJefferson wrote:
         | Why do you think Zig won't have the same issue? In my
         | experience lack of packages seem to come from no standard place
         | to put them (C++, then you end up with effective package stores
         | like Boost), or just not popular enough.
         | 
         | Most languages I work on (Rust, Python, JavaScript, Haskell),
         | have a huge number of packages.
        
           | treyd wrote:
           | I think that the "package explosion problem" actually does
           | exist in C/C++ and Go but it's just hidden. Very often a
           | project takes the form of various components, and you only
           | depend on a subset of them. In these older-style languages
           | these are all shipped as a single unit, but in newer
           | languages these are shipped more explicitly so you can see
           | what the shape of your dependency tree really looks like.
           | 
           | Boost is a great example of this, since it does a ton of
           | different things, but the boundaries between components are
           | not quite as obvious as having a "dependencies.lock" to look
           | at. Tokio has a ton of different packages but often you only
           | need a few of them.
        
             | SkiFire13 wrote:
             | Also often dependencies can be hidden by depending on a
             | single system library, but that then internally contains a
             | ton of stuff. Let's be real about dependencies:
             | https://wiki.alopex.li/LetsBeRealAboutDependencies
        
               | kbolino wrote:
               | The other thing, which is also a double-edged sword, is
               | that "system dependencies" on Linux (generally) have only
               | one version installed at a time and that version is
               | expected to be suitable for all dependents. Distro
               | managers/packagers often put in nontrivial work to make
               | this happen. So you often install something new with few
               | apparent dependencies because the rest are already
               | installed. With dependency locking in Rust etc., you'll
               | often "re-"install the same package many times because
               | the version is slightly different than what you already
               | had.
        
         | dragonelite wrote:
         | Its was so nice to just quickly write up a static file server.
         | Just to play with wasm without needing to use the usual python
         | http server thingy.
         | 
         | I was told the std.http.* isn't meant to be used for production
         | servers but its nice that i now have a drop in zig file that
         | contains a very very... bare bones static file server i can add
         | to zig projects that might need it.
        
       | saagarjha wrote:
       | Just use Instruments. Yes, I know it's slow, but it's miles ahead
       | of anything on Linux. If you're looking for an identical
       | profiling experience on macOS as you get on Linux you're greatly
       | missing out.
        
         | ForLoveOfCats wrote:
         | XCode is painful but I always miss Instruments whenever I'm
         | profiling something on my Linux systems
        
         | verte_zerg wrote:
         | What kind of extra functionality are you getting from
         | Instruments that isn't covered by other tools?
        
           | dagmx wrote:
           | Other than just the niceness of the interface, a key one is
           | that the M4 generation added profiling of CPU branching and
           | afaik instruments is the only thing that supports it right
           | now
        
             | verte_zerg wrote:
             | In the M4, Apple mostly added counters only for the SME
             | engine. The full list of supported counters can be found in
             | the official guide:
             | https://developer.apple.com/documentation/apple-
             | silicon/cpu-...
             | 
             | Regarding branch profiling, all arm64 (M1+) cpus support
             | these counters: - BRANCH_CALL_INDIR_MISPRED_NONSPEC -
             | BRANCH_COND_MISPRED_NONSPEC - BRANCH_INDIR_MISPRED_NONSPEC
             | - BRANCH_MISPRED_NONSPEC - BRANCH_RET_INDIR_MISPRED_NONSPEC
             | - INST_BRANCH - INST_BRANCH_CALL - INST_BRANCH_COND -
             | INST_BRANCH_INDIR - INST_BRANCH_RET - INST_BRANCH_TAKEN
             | 
             | afaik there is no limitation to implementing the fetching
             | of all these counters based on ibireme's research on kperf.
             | btw, forked "poop" already can fetch
             | BRANCH_MISPRED_NONSPEC.
        
       ___________________________________________________________________
       (page generated 2025-07-31 23:01 UTC)