Tristan Hume, a programmer, writes about performance, latency measurement, language design comparisons, and unusual projects like telefork.
Commenters cite it for the telefork post and the tree diff article, which another commenter says matches an approach they used.
> And I was thinking why did they make Swift for low level usage if they were going to use Rust.
Swift is great for application programming, but for low-level performance-critical systems programming it’s currently a huge pain in the rear. Swift’s philosophy is basically “give everything a Python-like level of dynamism, and then heavily inline/optimize out when statically possible.” For instance:
- Swift’s memory management is based on reference counting. It’s usually convenient and efficient (and lets you do cool things like copy-on-write), but when you know more about your object lifetimes than the compiler, the unnecessary retain/release traffic gets really annoying and is not easy to avoid.
- Swift’s generics are dynamic by default, which involves wrapping up every object into a possibly-heap-allocated container and using dynamic dispatch for everything. Usually the compiler will inline/specialize generics, but there’s no guarantee, especially when calling generics across module boundaries (due to ABI stability restrictions). Rust/C++ exclusively uses compile-time template instantiation, which is a little less flexible and doesn’t guarantee ABI stability but it always guarantees unimpaired performance. See [0] for a more detailed comparison.
- No stack-allocated arrays in Swift; all collections are reference-counted and stored on the heap.
- Still no async/await (or any real concurrency model at all) in Swift; it’s planned to be available in the next year or two.…
What tech interns do at trading companies, especially JS, always amazes me. Not because of the complexity of the problem domain but rather the rigor of the solution.
Something like Tristan Hume's famous blog post [0] on Tree Diffing would be unthinkable at Amazon, or most likely even Google or FB. If it's for internal use, they'd just slap together whatever works and looks nice, test the crap out of it and launch (part 2 optional). Nobody would want to spend weeks minimizing the white space like this, especially over 5 weeks.
I know multiple people who have interned at Trading companies - all making more than $9k a month - working on optimizing React rendering for tables. They have solutions that are amazing and wonderful because they are superior engineers and computer scientists than mere mortals like me, but for the problem space it seems pretty ridiculous.
I know people who have done crazy stuff that has gone into production during their Google, Facebook, or even lesser companies like Amazon internships but were compensated a fraction ($6k a month at Amazon vs. $10k a month at JS). I guess this is the dollar value difference of prestige.
[0]: http://thume.ca/2017/06/17/tree-diffing/
Hey HN, this is a side project that we made after realizing that a solid chunk of the time and code spent doing full-stack development was spent writing and debugging RPC layers - REST, GraphQL, GRPC, you name it.
The fact that the frontend and the backend are separate services leads to a bunch of cruft (https://thume.ca/2020/05/17/pipes-kill-productivity/ is a perfect description) and security holes, so we had the idea of merging them. It turns out that Phoenix LiveView already did server-side execution with changes streamed over WebSockets, but we weren't the biggest fans of having to learn an entirely new ecosystem and paradigm. So, we built a framework that allows (at the moment, admittedly simple) vanilla React apps to run on Node.js, with DOM updates being streamed to the client. That way, we can delete the RPC layer and greatly reduce development complexity and total code.
Would love to hear your thoughts!
It would be more accurate to say that idiomatic Rust encourages doing things which are slow to compile: lots of small generic functions everywhere. And the most effective way to speed this up is to avoid monomorphization by using RTTI to provide a single generic compiled implementation that can be reused for different types, like what Swift does when generics across the module boundary. But this is less efficient at runtime because of all the runtime checks and computations that now need to be done to deal with objects of different sizes etc, many direct or even inlined calls now become virtual etc.
Here's a somewhat dated but still good overview of various approaches to generics in different languages including C++, Rust, Swift, and Zig and their tradeoffs: https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/
This is from 2015, but emacs still has higher typing latency than vim in 2024.
https://pavelfatin.com/typing-with-pleasure/
If you have better numbers and comparisons for emacs keypress-to-pixel latency, etc. I'd be interested.
Note classic vi started up faster than vim.
Device makes little to no difference: Raspberry Pi 5, MacBook Pro M1, ThinkPad P1, etc. - emacs is clunky on all of them. I like emacs and it's my daily driver. But it isn't fast.
see also:
"Computer latency: 1997-2017" https://danluu.com/input-lag/
"Measuring keyboard-to-photon latency with a light sensor" https://thume.ca/2020/05/20/making-a-latency-tester/
This is a really cool example of tree diffing via path finding. I noticed that this was the approach I used when I did tree diffing, and sure enough looks like this was inspired by autochrome which was inspired by my post (https://thume.ca/2017/06/17/tree-diffing/).
I'm curious exactly why A* failed here. It worked great for me, as long as you design a good heuristic. I imagine it might have been complicated to design a good heuristic with an expanded move set. I see autochrome had to abandon A* and has an explanation of why, but that explanation shouldn't apply to difftastic I think.