← Back to context

Comment by xondono

3 days ago

> Interpreted languages are now within a single digit multiple of natively compiled languages.

You have to be either clueless or delusional if you really believe that.

Let me specify that what I'm calling interpreted (and I'm sure carmack agrees) is languages with a VM and JIT.

The JVM and Javascript both fall into this category.

The proof is in the pudding. [1]

The JS version that ran in 8.54 seconds [2] did not use any sort of fancy escape hatches to get there. It's effectively the naive solution.

But if you look at the winning C version, you'll note that it went all out pulling every single SIMD trick in the book to win [3]. And with all that, the JS version is still only ~4x slower (single digit multiplier).

And if you look at the C++ version which is a near direct translation [4] which isn't using all the SIMD tricks in the books to win, it ran in 5.15. Bringing the multiple down to 1.7x.

Perhaps you weren't thinking of these JIT languages as being interpreted. That's fair. But if you did, you need to adjust your mental model of what's slow. JITs have come a VERY long way in the last 20 years.

I will say that languages like python remain slow. That wasn't what I was thinking of when I said "interpreted". It's definitely more than fair to call it an interpreted language.

[1] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

[2] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

[3] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

[4] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

  • And here we get into the absurdity of microbenchmarks like these.

    Yes, you can get the JVM to crunch some numbers relatively fast, particularly if the operations are repetitive enough that you can pull JIT tricks.

    Now try to run something that looks a bit more like an actual application and less like a cherry picked example, and as soon as you start moving memory around the gap jumps to orders of magnitude.

    • > and as soon as you start moving memory around the gap jumps to orders of magnitude.

      Depends on what you mean by "moving memory". In terms of heap allocation performance, the JVM and I suspect the JS engine will end up trouncing C/C++. Why? Because a GC allocator will outperform manual memory management in terms of absolute allocation rate. Because memory for a C++ allocator is pinned, unless you do a bunch of work utilizing things like bump allocators you'll simply flounder in terms of what the JVM does by default.

      All(?) of the JVM GCs are moving collectors. That means new allocation ends being a simple pointer bump with a bounds check. Really hard to beat in terms of perf.

      But it doesn't stop there. If we are talking real applications, then one real thing the JVM does better than C++ is concurrent programming. A GC works far better for managing concurrent data. So much so that when you look at high performance multithreaded C++ code you'll often find a garbage collector implementation. Something that Java gets by default. If you aren't google writing chrome, you are either aggressively copying memory or you are using something like atomic reference counters. Both of which will absolutely nuke performance.

      But that's not all. One other thing the JVM does better than C++ can generally hope to do is devirtualization. In real applications, you are likely either in template hell for C++ to get performance or you are dealing with some form of interfaces and inheritance which ultimately creates code that the compiler will struggle to optimize without going to an extreme like PGO. The JVM gets PGO for free because it doesn't have to blindly optimize methods.

      These simple microbenchmarks are a gift to C/C++, not the JVM.

  • fwiw There are a few naive un-optimised single-thread #8 n-body programs transliterated line-by-line literal style into different programming languages from the same original. [1]

    > a single digit multiple

    By which you mean < 10× ?

    So not those Java -Xint, PHP, Ruby, Python 3 programs?

    > interpreted

    Roberto Ierusalimschy said "the distinguishing feature of interpreted languages is not that they are not compiled, but that any eventual compiler is part of the language runtime and that, therefore, it is possible (and easy) to execute code generated on the fly." [2]

    [1] https://benchmarksgame-team.pages.debian.net/benchmarksgame/...

    [2] "Programming in Lua" 1st ed p57

    • > By which you mean < 10× ?

      Yup

      > So not those Java -Xint, PHP, Ruby, Python 3 programs?

      Pretty much. The Java -Xint stuff turns off the JIT and purely uses the interpreter. That's not how anyone (that I'm aware of) uses java. I had to look up exactly what that flag did.

      Ruby I think might be underperforming because `yjit` isn't enabled by default. I suspect the same of Python (it just got a jit with python 3.13. Looks like it's still marked as experimental)

      1 reply →

A simple do-nothing for loop in JavaScript via my browser's web console will run at hundreds of MHz. Single-threaded, implicitly working in floating-point (JavaScript being what it is) and on 2014 hardware (3GHz CPU).