Live data from Hacker News

Zig's New Relationship with LLVM

kristoff.it

181–190 of 295 posts

Re: Zig's New Relationship with LLVM

#181
post #75

Is a patched in-place binary identical to the binary that would be generated from a clean compilation?

No, some parts of the binary file will be unused in the final executable if you keep updating it incrementally.

The linkers will do what they can to reuse all previously freed blocks of the file, but you can imagine a scenario where a function is updated but does not fit in the block that was previously allocated for it. In this case, a new block of the file will be allocated for this function.

If you then add a smaller function, it will occupy some portion of the old block. You can keep adding things into this block until some minimum amount of memory is reached (and as long as the declarations fit in there, obviously).

Repeating this process for all new declarations and for all the modifications of declarations, you can imagine some parts of the files will remain unused.

I hope this makes sense :)

Re: Zig's New Relationship with LLVM

#182

Earlier quoted context omitted.

Maybe this is an ignorant question, but how exactly is Rust influenced by web dev? I don't see how the aspects that make Rust popular (ownership, lifetimes, strong type system etc.) contribute much to web dev in particular. Languages used for the web like JS, Python, Ruby seem popular precisely because they abstract lower-level details.

Rust is heavily funded and maintained by Mozilla for use in Firefox's engine, Servo

AFAIK no it isn't anymore, it was one of the biggest teams jettisoned by Mozilla manglement in the layoffs a few weeks ago

(So, everyone promptly hyperfocused on how to bootstrap the Rust Foundation in a hurry)

Re: Zig's New Relationship with LLVM

#183
post #81

Earlier quoted context omitted.

Gold is much better than a 30% speedup IME. I went from >60 seconds per (config x arch x platform x near-leaf binary) combination with bfd, to 6x faster / Additionally, I'll note that gold's incremental linking is in-place binary patching, from what I can tell, and potentially another order-of-magnitude speedup even on your existing C projects - slide 11+ certainly appears to be describing such, and the caveats + res…

Were you working on a C/C++ project, out of curiosity? Of the two projects I've meaningfully benchmarked gold on, one was written in Rust and one was written in Go. I can believe that the Rust/Go toolchains make life difficult enough for gold that there is less of a speedup realized. And yeah, the `--incremental` support in gold is definitely very exciting. Unfortunately it is not complete enough that I've been able…

lld seems to reduce rust linking time significantly:

* 2x for linking rustc: https://blog.mozilla.org/nnethercote/2020/04/24/how-to-speed...

* 3x here: https://brokenco.de/2020/01/08/faster-rust-linking.html

* 2x faster than gold according to this: https://github.com/rust-lang/rust/issues/71515

Re: Zig's New Relationship with LLVM

#184
post #119
post #26

Yes, yes, yes, yes, yes. I'm going to make the kind of prediction that will set me up to be the laughingstock of HN in a few years. I think in-place binary patching will be the single most consequential development in build toolchains in the last twenty years; the most consequential development since a graduate student at the University of Illinois named Chris Lattner decided to embark on LLVM. If Zig is successful i…

At one point I got Chrome (a genuinely massive C++ project) to compile+link in under six seconds: http://neugierig.org/software/chromium/notes/2011/02/ninja.h... One of the big tricks not mentioned there is to decompose the app into multiple .so files (only when debugging -- release is still a single binary), so that you don't need to rebuild files unaffected by your incremental changes. I wrote more about that here:…

Why is release still monolithic? Startup speed?

Re: Zig's New Relationship with LLVM

#185
post #119

Earlier quoted context omitted.

At one point I got Chrome (a genuinely massive C++ project) to compile+link in under six seconds: http://neugierig.org/software/chromium/notes/2011/02/ninja.h... One of the big tricks not mentioned there is to decompose the app into multiple .so files (only when debugging -- release is still a single binary), so that you don't need to rebuild files unaffected by your incremental changes. I wrote more about that here:…

On the topic of monolithic versus component builds, how much does Chromium's decision to use a large statically-linked module for release builds actually impact performance, both startup and steady-state? Has this been measured? For me, this characteristic of Chromium was always one of the most striking differences between it and IE, and even though Firefox has had one monster DLL for a while, Chromium takes this fur…

I don't recall measuring it in particular. Some thoughts on it anyway:

1. Even if you take a single binary and split it into multiple objects, you still must do the same "linking" work of resolving where functions point to. It's just that work is done as part of startup, at runtime, by the dynamic loader. I have seen (in other contexts) splitting up a big C++ binary into multiple pieces dramatically impact startup time.

2. You can't inline across shared objects. Inlining is the "mother of all optimizations" -- it's the biggest benefit, and most other optimizations interact with it. (For the simplest case, imagine a getter on an object. Inlined, it compiles to a pointer+offset dereference, while across modules it compiles to a lookup in a pointer table plus a function call.) This also means you can't delete unused functions and so on.

3. There's no real memory savings from shared objects in this manner, even when different subprocesses of Chrome only use different subsets of the binary, because the OS only pages in the part of the binary it uses.

In general, given there's no user benefit for multiple shared objects, there's little reason to not have a single binary, except for build time. But for releases Chrome does a lot of other work (I think even profile-guided optimization these days, though that is after my time) for performance anyway. In the old days we were making changes to save as little as 40ms of startup time (http://neugierig.org/software/chromium/notes/2009/01/startup... and http://neugierig.org/software/chromium/notes/2009/04/perf.ht...). Looking back at the post now, I see that -fPIC cost 12% of the WebKit JS engine performance, which is maybe another strike against relocatable objects (though maybe these days we use PIE anyway? I am pretty rusty on all this).

If I had an app that was multiple shared objects I might be more interested in measuring the benefit before switching it to a single binary, since while I know the benefit is nonzero you have to balance it against the cost of implementation. But in Chrome's case we started with the single binary build, so there was little reason to measure.

I would guess in IE's case they were more interested in providing reusable APIs used by multiple other systems. In Chrome's case that was never a design goal.

Re: Zig's New Relationship with LLVM

#186
post #119

Earlier quoted context omitted.

At one point I got Chrome (a genuinely massive C++ project) to compile+link in under six seconds: http://neugierig.org/software/chromium/notes/2011/02/ninja.h... One of the big tricks not mentioned there is to decompose the app into multiple .so files (only when debugging -- release is still a single binary), so that you don't need to rebuild files unaffected by your incremental changes. I wrote more about that here:…

Why is release still monolithic? Startup speed?

I responded to this in https://news.ycombinator.com/item?id=24619216

Re: Zig's New Relationship with LLVM

#187
post #161
post #75

Is a patched in-place binary identical to the binary that would be generated from a clean compilation?

Doesn't sound like it. Seems like it appends the new versions of the functions and updates the global offset table.

Not quite, the linkers (at least ELF and PE which I am familiar with) will "free" parts of the file (when a block needs to be reallocated because the decl code gets too big, or when a decl is removed) and reuse them when they can.

But in general, the final executable will not be the same, this is correct.

Re: Zig's New Relationship with LLVM

#188

I’ve often wanted to try out Zig at work but the dependency on the latest LLVM meant doing that first. Building LLVM from scratch on our platform takes more resources (including time) than I normally have available (LLDB needs like 6GB of RAM to link?) So, I welcome this news!

Zig releases are available in statically compiled form, no dependencies, no compilation necessary.

Re: Zig's New Relationship with LLVM

#189
post #109

Earlier quoted context omitted.

In Zig, as far as I understand, you really just pass an allocator around. I don't see any special syntax to support this? This could be done in Rust. There is, for example, the simple bump allocator bumpalo [1]. It would be nice if the the std collections supported this (in planning, but hasn't seen much progress), and most dependencies would not be built around a manually passed allocator. > Rust assumes most of the…

Again, it's not about what you are able to do in Rust vs. Zig, it's essentially about the API of the language. Zig's semantics around memory management are imperative: i.e. you are telling the compiler when to allocate and deallocate memory. Rust's are declarative: you tell the compiler how the memory for a given value should be managed, and the compiler interprets these requirements to decide when memory should be a…

> The point is there are also cases where imperative, explicit memory management is desirable.

I agree that is the case, and Zigs convention of passing an allocator around and making those calls very explicit has merit.

I think you are over-estimating the amount of "magic" in Rust though.

The allocation-related logic is not part of the language or the compiler, but comes from the std (or alloc) crate. You just don't usually see a "malloc" call because it is hidden behind types in the standard library, which default to a global allocator. You still "manually" allocate by calling eg `Box::new()`.

The only magic is that Rust calls a destructor ( Drop::drop) when a value goes out of scope. Types can implement drop and use it to deallocate or decrement a reference counter. This is made possible by using an affine type system with lifetimes and move semantics.

This is conceptually not that different from doing something like `defer allocator.free(x)` in Zig, though less explicit.

ps: I realize that conventions make a difference. But nothing in Zig would stop me from having a global variable with an allocator, and using that without passing it around.

Re: Zig's New Relationship with LLVM

#190
post #38

Earlier quoted context omitted.

Zig is also designed around safety and security. It's not as obvious as with Rust, but it's still there. Zig is way easier to verify than rust code (no hidden control flow, no hidden allocations that might fail, ...) which means a security expert can read a single function (without knowing more about the code) and can reason about that function. This does not work in languages like C++ and Rust where RAII is a common…

> No hidden control flow It's true that a macro in Rust could introduce an early, invisible return. But this can only happen inside a macro. A reasonable option would be to disallow those in a project where such concerns are critical. Otherwise Zigs "try" is similar to `?` in Rust, and "defer" comes pretty close to non-obvious control flow for me. I'm particularly not a big fan of "defer" (and "errdefer"), which I al…

fwiw, the defers apply to scopes in Zig (similar to D's scope(exit) and scope(failure)) and thus are always static, not dynamic like Go's.
Post reply on HN