Most active commenters
  • benreesman(5)
  • duped(3)

←back to thread

302 points Bogdanp | 34 comments | | HN request time: 1.439s | source | bottom
Show context
taylorallred ◴[] No.44390996[source]
So there's this guy you may have heard of called Ryan Fleury who makes the RAD debugger for Epic. The whole thing is made with 278k lines of C and is built as a unity build (all the code is included into one file that is compiled as a single translation unit). On a decent windows machine it takes 1.5 seconds to do a clean compile. This seems like a clear case-study that compilation can be incredibly fast and makes me wonder why other languages like Rust and Swift can't just do something similar to achieve similar speeds.
replies(18): >>44391046 #>>44391066 #>>44391100 #>>44391170 #>>44391214 #>>44391359 #>>44391671 #>>44391740 #>>44393057 #>>44393294 #>>44393629 #>>44394710 #>>44395044 #>>44395135 #>>44395226 #>>44395485 #>>44396044 #>>44401496 #
lordofgibbons ◴[] No.44391100[source]
The more your compiler does for you at build time, the longer it will take to build, it's that simple.

Go has sub-second build times even on massive code-bases. Why? because it doesn't do a lot at build time. It has a simple module system, (relatively) simple type system, and leaves a whole bunch of stuff be handled by the GC at runtime. It's great for its intended use case.

When you have things like macros, advanced type systems, and want robustness guarantees at build time.. then you have to pay for that.

replies(9): >>44391549 #>>44391582 #>>44391630 #>>44391910 #>>44394240 #>>44395833 #>>44397304 #>>44401934 #>>44402705 #
1. duped ◴[] No.44391582[source]
I think this is mostly a myth. If you look at Rust compiler benchmarks, while typechecking isn't _free_ it's also not the bottleneck.

A big reason that amalgamation builds of C and C++ can absolutely fly is because they aren't reparsing headers and generating exactly one object file so the linker has no work to do.

Once you add static linking to the toolchain (in all of its forms) things get really fucking slow.

Codegen is also a problem. Rust tends to generate a lot more code than C or C++, so while the compiler is done doing most of its typechecking work, the backend and assembler has a lot of things to chuck through.

replies(6): >>44392553 #>>44392826 #>>44394891 #>>44396127 #>>44396258 #>>44396355 #
2. treyd ◴[] No.44392553[source]
Not only does it generate more code, the initially generated code before optimizations is also often worse. For example, heavy use of iterators means a ton of generics being instantiated and a ton of call code for setting up and tearing down call frames. This gets heavily inlined and flattened out, so in the end it's extremely well-optimized, but it's a lot of work for the compiler. Writing it all out classically with for loops and ifs is possible, but it's harder to read.
replies(1): >>44397082 #
3. fingerlocks ◴[] No.44392826[source]
The swift compiler is definitely bottle necked by type checking. For example, as a language requirement, generic types are left more or less in-tact after compilation. They are type checked independent of what is happening. This is unlike C++ templates which are effectively copy-pasting the resolved type with the generic for every occurrence of type resolution.

This has tradeoffs: increased ABI stability at the cost of longer compile times.

replies(3): >>44393659 #>>44394911 #>>44397467 #
4. willtemperley ◴[] No.44393659[source]
A lot can be done by the programmer to mitigate slow builds in Swift. Breaking up long expressions into smaller ones and using explicit types where type inference is expensive for example.

I’d like to see tooling for this to pinpoint bottlenecks - it’s not always obvious what’s making builds slow.

replies(2): >>44394713 #>>44396157 #
5. ykonstant ◴[] No.44394713{3}[source]
>I’d like to see tooling for this to pinpoint bottlenecks - it’s not always obvious what’s making builds slow.

I second this enthusiastically.

replies(1): >>44395588 #
6. windward ◴[] No.44394891[source]
>Codegen is also a problem. Rust tends to generate a lot more code than C or C++

Wouldn't you say a lot of that comes from the macros and (by way of monomorphisation) the type system?

replies(1): >>44397739 #
7. windward ◴[] No.44394911[source]
>This is unlike C++ templates which are effectively copy-pasting the resolved type with the generic for every occurrence of type resolution.

Even this can lead to unworkable compile times, to the point that code is rewritten.

8. glhaynes ◴[] No.44395588{4}[source]
I'll third it. I've started to see more and more cargo culting of "fixes" that I'm extremely suspicious do nothing aside from making the code bulkier.
9. the-lazy-guy ◴[] No.44396127[source]
> Once you add static linking to the toolchain (in all of its forms) things get really fucking slow.

Could you expand on that, please? Every time you run dynmically linked program, it is linked at runtime. (unless it explicitly avoids linking unneccessary stuff by dlopening things lazily; which pretty much never happens). If it is fine to link on every program launch, linking at build time should not be a problem at all.

If you want to have link time optimization, that's another story. But you absolutely don't have to do that if you care about build speed.

replies(1): >>44403903 #
10. never_inline ◴[] No.44396157{3}[source]
> Breaking up long expressions into smaller ones

If it improves compile time, that sounds like a bug in the compiler or the design of the language itself.

11. blizdiddy ◴[] No.44396258[source]
Go is static by default and still fast as hell
replies(1): >>44396404 #
12. benreesman ◴[] No.44396355[source]
The meme that static linking is slow or produces anything other than the best executables is demonstrably false and the result of surprisingly sinister agendas. Get out readelf and nm and PS sometime and do the arithematic: most programs don't link much of glibc (and its static link is broken by design, musl is better at just about everything). Matt Godbolt has a great talk about how dynamic linking actually works that should give anyone pause.

DLLs got their start when early windowing systems didn't quite fit on the workstations of the era in the late 80s / early 90s.

In about 4 minutes both Microsoft and GNU were like, "let me get this straight, it will never work on another system and I can silently change it whenever I want?" Debian went along because it gives distro maintainers degrees of freedom they like and don't bear the costs of.

Fast forward 30 years and Docker is too profitable a problem to fix by the simple expedient of calling a stable kernel ABI on anything, and don't even get me started on how penetrated everything but libressl and libsodium are. Protip: TLS is popular with the establishment because even Wireshark requires special settings and privileges for a user to see their own traffic, security patches my ass. eBPF is easier.

Dynamic linking moves control from users to vendors and governments at ruinous cost in performance, props up bloated industries like the cloud compute and Docker industrial complex, and should die in a fire.

Don't take my word for it, swing by cat-v.org sometimes and see what the authors of Unix have to say about it.

I'll save the rant about how rustc somehow manages to be slower than clang++ and clang-tidy combined for another day.

replies(3): >>44396760 #>>44396875 #>>44396975 #
13. vintagedave ◴[] No.44396404[source]
Delphi is static by default and incredibly fast too.
replies(2): >>44399822 #>>44402941 #
14. jelder ◴[] No.44396760[source]
CppCon 2018: Matt Godbolt “The Bits Between the Bits: How We Get to main()"

https://www.youtube.com/watch?v=dOfucXtyEsU

15. jrmg ◴[] No.44396875[source]
…surprisingly sinister agendas.

Dynamic linking moves control from users to vendors and governments at ruinous cost in performance, props up bloated industries...

This is ridiculous. Not everything is a conspiracy!

replies(4): >>44396943 #>>44398118 #>>44399499 #>>44400236 #
16. k__ ◴[] No.44396943{3}[source]
That's an even more reasonable fear than trusting trust, and people seem to take that seriously.
17. duped ◴[] No.44396975[source]
I think you're confused about my comment and this thread - I'm talking about build times.
replies(1): >>44398148 #
18. estebank ◴[] No.44397082[source]
For loops are sugar around an Iterator instantiation:

  for i in 0..10 {}
translates to roughly

  let mut iter = Range { start: 0, end: 10 }.into_iter();
  while let Some(i) = iter.next() {}
19. slavapestov ◴[] No.44397467[source]
> This has tradeoffs: increased ABI stability at the cost of longer compile times.

Nah. Slow type checking in Swift is primarily caused by the fact that functions and operators can be overloaded on type.

Separately-compiled generics don't introduce any algorithmic complexity and are actually good for compile time, because you don't have to re-type check every template expansion more than once.

replies(2): >>44399893 #>>44399968 #
20. jandrewrogers ◴[] No.44397739[source]
Modern C++ in particular does a lot of similar, albeit not identical, codegen due to its extensive metaprogramming facilities. (C is, of course, dead simple.) I've never looked into it too much but anecdotally Rust does seem to generate significantly more code than C++ in cases where I would intuitively expect the codegen to be similar. For whatever reason, the "in theory" doesn't translate to "in practice" reliably.

I suspect this leaks into both compile-time and run-time costs.

21. benreesman ◴[] No.44398118{3}[source]
I didn't say anything was a conspiracy, let alone everything. I said inferior software is promoted by vendors on Linux as well as on MacOS and Windows with unpleasant consequences for users in a way that serves those vendors and the even more powerful institutions to which they are beholden. Sinister intentions are everywhere in this business (go read the opinions of the people who run YC), that's not even remotely controversial.

If fact, if there was anything remotely controversial about a bunch of extremely specific, extremely falsifiable claims I made, one imagines your rebuttal would have mentioned at least one.

I said inflmatory things (Docker is both arsonist and fireman at ruinous cost), but they're fucking true. That Alpine in the Docker jank? Links musl!

22. benreesman ◴[] No.44398148{3}[source]
You said something false and important and I took the opportunity to educate anyone reading about why this aspect of their computing experience is a mess. All of that is germane to how we ended up in a situation where someone is calling rustc with a Dockerfile and this is considered normal.
replies(1): >>44400814 #
23. computably ◴[] No.44399499{3}[source]
Bad incentives != conspiracy
24. zenlot ◴[] No.44399822{3}[source]
FreePascal to the game please
replies(1): >>44401261 #
25. fingerlocks ◴[] No.44399893{3}[source]
You’re absolutely right. I realized this later but it was too late to edit the post.
26. choeger ◴[] No.44399968{3}[source]
Separate compilation also enables easy parallelization of type checking.
27. trinix912 ◴[] No.44400236{3}[source]
Had they left "governments" out of there it would've been almost fine, but damn I didn't know it's now governments changing DLLs for us!
replies(1): >>44400720 #
28. benreesman ◴[] No.44400720{4}[source]
https://en.wikipedia.org/wiki/Equation_Group

https://en.wikipedia.org/wiki/Advanced_persistent_threat

https://en.wikipedia.org/wiki/Operation_Olympic_Games

https://simple.wikipedia.org/wiki/Stuxnet

https://en.wikipedia.org/wiki/Cozy_Bear

https://en.wikipedia.org/wiki/Fancy_Bear

https://en.wikipedia.org/wiki/Tailored_Access_Operations

29. duped ◴[] No.44400814{4}[source]
Seems like you still misunderstand both the comment and context and getting overly emotional/conspiratorial. You might want to work on those feelings.
replies(1): >>44401034 #
30. benreesman ◴[] No.44401034{5}[source]
No one is trying to take anyone's multi-gigabyte pile of dynamic library closure to deploy what should be a few hundred kilobytes of arbitrarily portable, secure by construction, built to last executable.

But people should make an informed choice, and there isn't any noble or high minded or well-meaning reason to try to shout that information down.

Don't confidently assert falsehoods unless you're prepared to have them refuted. You're entitled to peddle memes and I'm entitled to reply with corrections.

31. tukantje ◴[] No.44401261{4}[source]
Will the real FORTRAN please stand up?
32. pjmlp ◴[] No.44402941{3}[source]
And D, Active Oberon, Eiffel,....

Go got famous compile times, because for a decade a new generation educated in scripting languages and used to badly configured C and C++ projects, took for innovation, what was actually a return to old values in compiler development.

33. 1718627440 ◴[] No.44403903[source]
Reading your comment is sounds like the opposite would be true, because so much linking would be needed to be done at runtime. But that perception fails to realize, that when claiming an executable is linked dynamically, most symbols were also statically linked. It is only the few public exported symbols that are dynamically linked, because there are deemed to be a reasonable separate concern, that should be handled by someone elses codebase.

I think lazily linking is the default even if you don't use dlopen, i.e. every symbol gets linked upon first use. Of course that has the drawback, that the program can crash due to missing/incompatible libraries in the middle of work.

replies(1): >>44405021 #
34. AdelaideSimone ◴[] No.44405021{3}[source]
A lot of vendors use non-lazy binding for security reasons, and some platforms don't support anything other than RTLD_NOW (e.g., Android).

Anyway, while what you said is theoretically half-true, a fairly large number of libraries are not designed/encapsulated well. This means almost all of their symbols are exported dynamically, so, the idea that there are only "few public exported symbols" is unfortunately false.

However, something almost no one ever mentions is that ELF was actually designed to allow dynamic libraries to be fairly performant. It isn't something I would recommend, as it breaks many assumptions on Unices, (while you don't get the benefits of LTO) you can achieve code generation almost equivalent to static linking by using something like "-fno-semantic-interposition -Wl,-Bsymbolic,-z,now". MaskRay has a good explanation on it: https://maskray.me/blog/2021-05-16-elf-interposition-and-bsy...