Recent submissions

Comment · 1 month ago
> The whole point of abstraction is that the implementation details aren't known.

I disagree with that phrasing; it is better to say that abstractions allow a programmer to ignore unimportant details. For example, when developing two modules (possibly even by different teams), all they know about each other is a lean interface, without any implementation details. However, the compiler might know everything.

> So in C++ we always suffer from this problem called "zero overhead abstractions" or "abstraction costs"

This is another odd term. In Rust, the term used instead is "zero-cost abstractions," referring to cases where the compiler can generate instructions for higher-level code just as efficiently.

> So the main practical benefit of a JIT isn't that at all, but that it can do the "mother of all optimisations" - inlining - far more aggressively.

I’ll reiterate that I disagree with this: inlining is performed very efficiently during monomorphization. And monomorphization is used very frequently in Rust.

> After more than 25 years of working with C++

I don't have much experience with C++; I mostly use Rust. I can only assume that the C++ development experience is far worse than Rust - especially when trying to write software that is both reliable and fast. This may be particularly relevant to older C++.

So, a Dog, a Cat, and an Abstract Mammal walk into a bar...

https://godbolt.org/z/dEsW1sfM8

I didn't want to do this, but I went ahead and created a small example showing that monomorphization and inlining work remarkably well. (Obviously, this example does not address memory management)

Comment · 1 month ago
> Assembly (which is faster than anything in the same sense: for any program in any language, there exists and Assembly program that's at least as fast)

At least you aren't claiming that the JVM is ~1.5 faster than perfectly written assembly :)

I disagree with a lot of what you’re writing. However, we’ve reached the point where we need to run benchmarks and analyze the generated code (this is easy to do for compiled languages using https://godbolt.org/, but for the JVM, it can be a bit more complex, given the warm-up factor).

So, there is one fundamental point I started with:

> JIT is effective for languages where the source code lacks sufficient information (dynamic typing, where anything can be null)

And your answer is:

> A JIT can make such languages decently fast, but that's not how it's used in Java. In Java it is used for speculative optimisation, which allows far more aggressive optimisation than an AOT compiler can do.

Essentially, you are saying that the compiler can apply aggressive optimizations when it knows what is happening in the code.

But I say that JIT is needed so the compiler can figure out what is happening in the code and perform aggressive optimizations.

There are many things that can be inferred from the code without needing to execute it. The question is how difficult it is to make such an inference: in one scenario, the compiler might attempt to track whether specific data changes-and, if it can prove this, mark the data as immutable and apply certain optimizations-whereas in another, it might already possess the information that the data is immutable.

Moreover, information about immutability is useful not only to the compiler but also to the programmer. Just like information about types: it benefits both the compiler and the programmer. Imagine a fan of JS or Python joining our conversation and claiming that both Java and Rust are low-level languages because you have to specify types - something they view as complex and a hindrance to development speed.

The same applies to the GC: the compiler can perform more optimizations when it knows when memory needs to be cleared (move it to stack or even place the data on registers). The JVM attempts to do this (via escape analysis), but there are limitations; consequently, data ends up on the heap, and GC operations come at a cost (due to data movement).

Rust simply makes it easy to obtain far more information, enabling aggressive optimizations that are both immediate and guaranteed.

There remain a small number of cases, such as `switch` statements - where one branch executes 99% of the time, while the other 99 branches execute only 1% of the time. In such instances, the JIT could indeed perform further optimizations; however, I am not even sure if the overhead of monitoring wouldn't outweigh the benefits. And the question is when and how to perform PGO, or whether to perform it at all.

Comment · 1 month ago
> I have seen it mentioned everywhere, but is this actually true?

Yes, it just adding or subtraction int to stack pointer register. I’m not certain, but the only thing that might be faster is accessing data at a fixed address - that is, global variables.

Comment · 1 month ago
Thank you for the reply.

Do you mind a reasoned discussion?

> A JIT with speculative optimisation and a moving GC.

Idiomatic Rust, through its concepts of ownership and borrowing, encourages a pattern where you receive data as an argument or create it directly, perform operations on it, and then discard it via RAII. This bears some resemblance to functional programming. This approach does not apply to buffers of unknown size, which still require heap allocation; unfortunately, Rust lacks automatic buffer reuse. However, such optimization is theoretically possible. The stack is definitely faster than anything else.

> This means that you need to translate data storage or code storage to hardware addresses, and that interferes with both moving collection and with JIT compilation.

You don't need GC if you allocate data on stack. You also do not need to dereference the pointer.

> dynamic dispatch

You mentioned templates. In Rust, traits that are monomorphized - much like templates-are the standard approach; using vtables or `dyn trait` is a relatively rare use case. This stems from the fact that all code is known at compile time and there is no dynamic loading, allowing the compiler to eliminate polymorphism from the code entirely.

> and shared heap objects

This might be considered convenient, but in my view, it also leads to code that is harder to maintain when objects can be modified from multiple places. However, I think that is outside the scope of the current discussion.

> We get around it with selective use of templates in C++ (or comptime in Zig), but it has to be selective, and it's viral.

Yes, monomorphization is the default solution in Rust. It is not always viral either, because when using it, you often define specific types, and they do not spread beyond that scope.

I suppose you could say that the programming style I am talking about is complex, inconvenient, unmaintainable, and so on. What I mean is, assuming this programming style is sufficiently convenient—and perhaps even has its own advantages - then none of the optimizations you listed offer an edge, and the Rust code will definitely be faster.

Comment · 1 month ago
> because of essential constraints of low-level languages that prevent them from doing certain optimisations that matter mostly in large programs

Which specific optimizations are you referring to?

In my experience, this is largely a myth; compared to Rust, you actually get even faster code right away.

JIT is effective for languages where the source code lacks sufficient information (dynamic typing, where anything can be null).

Comment · 5 months ago
Ada is the lost knowledge of a past, more advanced civilization.
Comment · 5 months ago
> No, you’re clearly wrong; golang was always going to add support for generic functions.

Let's get this straight. I'll give you a long quote from Rob Pike's article where he describes the history of the go language:

""" One thing that is conspicuously absent is of course a type hierarchy. Allow me to be rude about that for a minute.

Early in the rollout of Go I was told by someone that he could not imagine working in a language without generic types. As I have reported elsewhere, I found that an odd remark.

To be fair he was probably saying in his own way that he really liked what the STL does for him in C++. For the purpose of argument, though, let's take his claim at face value.

What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types.

But more important, what it says is that types are the way to lift that burden. Types. Not polymorphic functions or language primitives or helpers of other kinds, but types.

That's the detail that sticks with me.

Programmers who come to Go from C++ and Java miss the idea of programming with types, particularly inheritance and subclassing and all that. Perhaps I'm a philistine about types but I've never found that model particularly expressive.

My late friend Alain Fournier once told me that he considered the lowest form of academic work to be taxonomy. And you know what? Type hierarchies are just taxonomy. You need to decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. Is a sortable array an array that sorts or a sorter represented by an array? If you believe that types address all design issues you must make that decision.

I believe that's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you.

That, of course, is where interfaces come into Go. But they're part of a bigger picture, the true Go philosophy. """

Rob Pike, 2012

I can draw a few conclusions from this: firstly, he didn't want to add generics at all because he didn't think they were useful, and secondly, he doesn't understand programming very well and doesn't know what generics are and confuses them with inheritance.

Comment · 5 months ago
Comment · 5 months ago
Comment · 5 months ago
So you mean to say that PHP5 and Js from 2007 had a well-founded design?
Comment · 5 months ago
I agree that they were clearly not in a hurry. I disagree that they are doing everything right. I am interested to see how they will fix the 'million dollars mistake'.
Comment · 5 months ago
Indeed, in 2012, it was not clear to anyone that generics were needed /s
Comment · 8 months ago
> but the good parts of JS are better than anything else in existence

What you talking about?! I can't think of a single thing in Js that I could say is good.

Okay, two big corporations have invested a lot of money and effort into making V8 and TypeScript, and now it's useful. But I don't consider it exactly part of Js.

Comment · 1 year ago

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton