Best programming language for high performance (January 2017)?

I keep hoping that the field of programming language will evolve. I am a bit tired to program in Java and C… I’d like better languages.

I am particularly interested in what I generally call “high-performance programming”. I want to pick languages where I can get the most out of my hardware. It is fine to program in JavaScript and Python, but there comes a time where you tackle large problems on hardware that is barely sufficient.

  • For my purposes, I am going to narrow it down to popular “system programming languages” which means Swift, Rust, Go, C++, C. These are languages that well-suited to build more than applications, but also servers, database engines and so forth. They all offer ahead-of-time compilation, which makes the performance easier to reason about.

    I think there will be no disagreement that C and C++ and system programming languages. Whether Go and Swift qualify is somewhat subjective. Would you write an operating system in Go? I would not. But Wikipedia seems to be happy to consider them all as system programming languages.

  • I am going to ignore assembly because I can’t imagine using it all the time. It is simply not practical unless you are working on a tiny code base (maybe on embedded software).
  • I am going to ignore Rust, D and Nim as they have too few users. Programming is a social activity: if nobody can use your code, it does not matter that it runs fast. I think that they are very interesting languages but until many more people start coding in these languages, I am going to ignore them. Fortran is out for similar reasons: it still has important users, but they are specialized.
  • I am not including non-system languages such as JavaScript, PHP, Matlab, Ruby, R, Python because though they can be fast, they are simply not generally well suited for high performance. They are typically “fast enough” for specific purposes, but they also have hard limitations. For example, JavaScript and Python appear unable to support natively multithreaded execution. (Spawning new processes does not count.)
  • Java and C# are interesting cases. They have been both described and used as “system programming languages”. I think that the evidence is overwhelming that Java can be used to build significant systems that perform very well. However, I think that running in a virtual machine (JVM, CLR) is a hard limitation: you can never be “bare metal”. I lump Scala with Java.
  • There is a countless number of niche languages that I have not mentioned. Most of them will never pick up any amount of users.

Let me run through a few desirable features for high-performance programming along with the current languages that fare well.

  • Operating systems are built in C or C++. Moreover, many important libraries have C interfaces. This means that being able to call C functions with minimal overhead is important in some instance. While Go allows you to call C functions, there is considerable overhead in terms of performance. All other programming languages can call C with minimal overhead.
    • Obvious winners: C, C++
    • Good players: Swift
    • Loser: Go
  • Though this is controversial, I think that a language with manual memory management may have the upper hand. In modern systems, managing memory is often a major overhead. C and C++ have manual memory management. Swift has reference counting which is a form of automated memory management, but with the advantage of being purely deterministic: when your data goes out of scope, the memory is reclaimed. However, reference counting does have an overhead, especially in a multithreaded context. Go has generational garbage collection (like Java or C#) which means that “it stops the world” at some point to reclaim memory, but the Go engineers have optimized for short pauses. That’s a good thing too because “stopping the world” is not desirable. I don’t know which approach I prefer: Swift or Go.
    • Winners: C, C++
    • Losers: Go, Swift
  • Our computers have many cores. Parallel programming is important. We would like our languages to support elegantly multicore programming. I think that Go has clearly the upper hand. Of course, for example, it is possible to do everything Go does from C as far as multicore programming, but not as easily. As far as I can tell, Swift has one standard way to use multiple cores: Grand Central Dispatch. It also has Operation/OperationQueue for higher level multi-threading.  It might be fine, but it does not look on par with what I find in other languages.
    • Special mention: Go
    • Winners : C++, C
    • Loser: Swift
  • For high performance, a good programming language should have “easy to reason about performance”. This means that when looking at a piece of code, you should have a good appreciation for how fast it will run. Honestly, this has gotten very hard over time, forcing us to rely more and more on benchmarking. However, simpler languages like C and Go makes it easier to reason about performance. Simply put, the more expansive the language, the harder it gets to compile it in your head.
    • Winners: C, Go
    • Losers: Swift, C++
  • Our processors have fancy new instructions, like SIMD instructions (AVX2, Neon…). These should be readily available from the programming language. No programming language is easy to program with something like SIMD instructions, but C and C++ clearly come on top. In Go, you can write functions in assembly and call them from the language, but it comes with a discouraging performance overhead. Swift is barely better (there is a simd package, but it only works under macOS).
    • Winners: C, C++
    • Losers: Go, Swift
  • It has been decades and there is still no universal way to manage dependencies in C. There is also no standard build mechanism. Sure, your favorite IDE can make it easier to add a dependency and build your program, but having something built into the language makes life much easier. In Go and Swift, there is a standard, cross-platform way to build programs, run tests and add dependencies.
    • Winners: Go, Swift (special mention for Go)
    • Losers: C, C++

Let me summarize my analysis with a table:

C C++ GoSwift
Call C Yes Yes SlowFair
Bare metal memory Yes Yes No No
Cores Yes Yes Great Maybe (GCD)
Simple syntax Yes No Yes No
Safety Minimal Minimal Yes Yes
SIMD Yes Yes Slow Maybe (macOS only)
Tools None None GreatGood

If I were hired to build high-performance programming, I’d be happy to be “forced” into any one of these languages. They are all fine choices. They are all improving at a fast pace. I think that Go, for example, has improved tremendously since I first learned it.

30 thoughts on “Best programming language for high performance (January 2017)?”

  1. I really like this way of reasoning about languages. I have, however, a bone to pick with your reasoning about no particular cross-platform build system for C/C++. While it is true that there is no cross-platform build and test system as defined by the standard, CMake comes really close to such a system. With the new C++11–17 standards that abstract away many of the OS-specifics such as multithreading and file systems, C++ with CMake gets as cross-platform as it could possibly get.

    1. @Skand

      Have you tried Go tools? Ok. We have this project in Go. You know what you need to do to get it and all its dependencies? And then run tests to make sure that it works?

      go get -t github.com/RoaringBitmap/roaring
      go test
      

      That’s it. And this pretty much works with any Go project.

      There is simply nothing like it in C++.

      I don’t know what fraction of C++ programmers use CMake. I estimate that it is actually quite small. For one thing, users of Visual Studio won’t touch CMake and I would not be surprised if they made up more than the majority of users.

      While CMake can call your tests, it does not really provide a testing framework, you have to roll your own. If there are dependencies, you have to provide them on your own. If users need to resolve dependencies, they have to figure it out on their own.

      With Go, you can just pick up any project and you have a pretty good idea how to build it, no matter where you are.

      That’s not to say that Go is perfect. It still has major faults. But CMake is nowhere close.

      1. Visual Studio 2017 ships with CMake. They are actively working on CMake integration and working nicely with the C++ community to figure out how best to make things work in real production environments. So your intuition about Visual Studio is completely off.

        And CMake’s support for Visual Studio is not the best, but it does work. After some digging, I can usually get what I want done in a cross-platform manner. The rub is that I’m dealing with decades-old legacy 3rd party libs and brand spanking new libs. Nothing will ever, EVER, cover these builds/configs in one simple way. CMake makes it so that I can work with these disparate technologies and still have a flexible build system.

        The main problem with your analysis is that I think it’s unfair to compare the build tools and say “CMake is nowhere close” because CMake does so much more than the Go tools have to. For large, cross-platform C and C++ projects where they do dev work with Visual Studio and things like CLion, CMake is the way to go. And if you look up projects that actually use CMake, I think you’d be surprised at the list.

        1. @Caleb

          So we are clear, I use CMake all the time, almost daily. E.g., see https://github.com/RoaringBitmap/CRoaring

          Visual Studio 2017 ships with CMake.

          Ah! That’s interesting. So I was wrong to underestimate Microsoft. I am happy that my pessimism was unwarranted. Thanks for educating me on this issue.

          The main problem with your analysis is that I think it’s unfair to compare the build tools and say “CMake is nowhere close” because CMake does so much more than the Go tools have to. For large, cross-platform C and C++ projects where they do dev work with Visual Studio and things like CLion, CMake is the way to go. And if you look up projects that actually use CMake, I think you’d be surprised at the list.

          I don’t think I am being unfair. CMake is a major contribution. I do not underestimate the difficulty.

          But the truth remains that CMake solves a fraction of what the Go or Swift tools solve.

          You point is that I am being unfair because neither Go nor Swift has to support “decades-old legacy 3rd party libs”. I agree with your wording, but I think I am being fair. Hear me out.

          When the C and C++ came about, nobody had the idea that testing, building and managing dependencies had to be part of the language. So it was built separately, by different people, and it evolved without any standard. It is too late today to really solve this problem. It is like retro-engineering a Chevy Impala so that it runs on solar energy. I mean, in theory, it could be done, but you just know it won’t get done.

          So CMake is a lot better than autoconf/automake. No question about it. It solves hard problems… ok. But it is only so incredibly hard because we are retro-engineering.

          But let me go back to my Go example. Realize that what I described, running tests, downloading dependencies, and so forth… that can all be done without any configuration file. With Swift, there is a configuration file, but it is maybe 5 lines at the most. Rust is similar.

          So Swift, Go, Rust… they have this idea that the language needs to come with its own high-quality tools, by default. I submit to you that it is an innovation. They did not invent this idea… I mean “Turbo Pascal” came with its own IDE. So did Visual Basic and so forth. But you have to give credit to the folks involved: they knew enough to realize that languages today need to be a lot more than just syntax and a compiler.

          If we are going to move forward in software, we have to learn from our collective mistakes. C and C++ are great, but if we had to do them over, we could do a lot better.

    2. If world were ideal then you would be right. 🙂 But C++ build-world is a mess, and nothing will change. In the wild you find: custom makefiles, autotools, Microsoft’s VC files, even Boost has own build system. There are too many libraries, too many eco-systems (unix, windows, mac) and incompatible compilers. I work with C++ and every time I need to compile an external library it is a pain in neck.

      C++17 would be an answer if it were came 10-20 years ago, now it’s too late, there are billions of lines written in C++98, C++03 or C++11.

  2. Ada with the Ravenscar profile would likely hit all your points with flying colors. Its syntax is quite simple to read, and GNAT is pretty much all you need.

  3. I don’t think you can ignore Rust in such a comparison. It was designed specifically to address many of your criteria. While it’s certainly too early to tell if it can win the hearts of c/c++ programmers such as myself or if something better will come along first, it is gaining momentum in certain circles. Personally I still find the language overly difficult to work with, but really interesting system level projects such as https://github.com/jwilm/alacritty are starting to pop up which are quite intriguing.

    1. I definitely agree. After all, Mozilla is actively working on implementing parts of Firefox in Rust right now, along with a number of companies and organizations (albeit mostly smaller ones) who already have production software based on Rust.

    2. I have put a time stamp in the title of my post because I expect that my current opinion might change drastically in a year or so. Maybe Rust will grow.

      I think that my rationale (“coding is a social activity”) makes a whole lot of sense. It is not simply how many people use the language, of course… but also, very much, how many people “like me” use the language.

      I don’t know how much trust you can put in those things, but TIOBE puts Rust at position 41, below Haskell and Logo. In contrast, Swift and Go are ranked at position 13 and 14. Redmonk has roughly the same ranking with respect to these languages.

  4. A Lisp family language that “transpiles” to C would be optimal, if it did a good job. Chicken and Gambit are in that category, if I remember correctly, but they don’t seem mature/polished/stable.

    I would like to have a good language for efficiency, e.g. in a raspberry pi, but there’s no perfect language, and if I have to concede on something, efficiency seems like a good candidate to give up on it. Expressiveness is much more important, and much more than that the libraries, frameworks, and tools available for the language.

  5. Even if Java was already take out of the question, the Garbage Collector is configurable, so that one can avoid the FullGC and ‘stopping the world’.

    1. the Garbage Collector is configurable, so that one can avoid the FullGC and ‘stopping the world’

      Not in any realistic sense. Sure, you can choose a different GC configuration, but all of them stop the world for significant lengths of time.

  6. One language that you should consider that is probably a very good fit for your specific requirements is Delphi. If you’ve never tried it, try it. You’ll be surprised to see how fast is is to code with, how fast the compiler is, how fast and efficient the code that is produced is, how good the memory manager is, how well multi-threading is supported, and how you can embed assembly inline in the few situations where you might want to use special cpu instructions.

    1. After Basic, I learned Pascal (Turbo Pascal). When I moved to Windows, I started using Delphi in the 1990s.

      I have nice memories of Pascal. Free Pascal is alive and well. Performance is still good. Pascal got many things right.

      However, I think it is fair to say that Pascal is, at best, a niche language. And there is little chance that it will ever become popular again. I am not exactly sure why it faded.

      1. Every language is niche. That’s why we have so many of them and why they are so very different. Delphi is becoming popular again despite the fact that it is also expensive and not “free” like the “popular” languages. But, sometimes you get what you pay for. If it’s been 20 years since you’ve touched Delphi, I would suggest that you look into it again. If your not sure why it faded, I would offer a perspective: maybe it didn’t fade away, rather, maybe programmers that once touched it faded away from it. Also curious to note, it’s #11 in 2017 on the TIOBE list as shown in the link above by a previous comment. It’s ranked higher than Swift and Go, so maybe it’s not as faded as you might think. Every programmer I’ve ever met (including myself) gets stuck in their ways. We choose a language or technique usually based on what we know and what we are comfortable with. We rarely take the time to investigate the possibility of using a different tool even if it might be a better tool for the job than the one we are familiar with. One final note: I do like your thought process on this blog post. It feels like it has an authentic tone of objectivity (which is very rare these days).

        1. Also curious to note, it’s #11 in 2017 on the TIOBE list as shown in the link above by a previous comment.

          That’s an interesting observation. I had not realized that Delphi remained so popular throughout the years.

    1. @Neil

      Swift does have a simd package, but it is macOS only for now. It is not a critical issue because Swift can call C without overhead, so you can design your own SIMD functions if you want.

  7. Interesting post. A couple of nitpicks:

    You say that you narrow it down to a list including Rust, but then later you say you’re ignoring Rust, but then within the same paragraph imply that you’re talking about two languages instead of three. I’m not sure if I’m misreading or there’s a typo somewhere there, but it seems slightly off. Either way, I got the impression you wrote off Rust, which I personally would not have.

    > … and Python appear unable to support natively multithreaded execution

    I’m not sure exactly what you mean by “natively multithreaded,” but Python’s multiprocessing module *does* let you use multiple cores if that’s where you were going with this statement. Having said that, I agree with you that Python is unsuitable for systems programming.

    Overall, nice article though 🙂

    1. You say that you narrow it down to a list including Rust, but then later you say you’re ignoring Rust (…)

      I first narrow it down to system programming languages, and I include Rust in this list… but then I remove Rust because I fear it is not yet popular enough. I had a misleading “both” that I removed. As I have written elsewhere in the comments, this may change if Rust becomes a lot more popular. I don’t think “I wrote off Rust”. I specifically mention it has a valid system language. But look… one has to be realistic… Rust is below Haskell, Scheme, Lisp, Logo in the TIOBE ranking. We hear that Mozilla (the organization that promotes Rust) might use Rust for some things. It is not a great validation. Swift has the whole might of Apple behind it. Go has… well, Google… Docker, Netflix, Dropbox…

      For all I know, Rust is the future… but I’d like to have hard data to back this up.

      I’m not sure exactly what you mean by “natively multithreaded,” but Python’s multiprocessing module *does* let you use multiple cores if that’s where you were going with this statement.

      I distinguish “process-based parallelism” and “multithread execution”.

  8. From my post…

    Go has generational garbage collection (like Java or C#) which means that “it stops the world” at some point to reclaim memory, but the Go engineers have optimized for short pauses. That’s a good thing too because “stopping the world” is not desirable.

  9. Your headline is misleading because high performance is far from the only criterion you are using, otherwise assembly language would be at the top of your list.

    As long as you are using other criteria, you might consider that C and C++ are security nightmares, safe languages are much easier to program, and consequently, less and less programming is being done in C and C++.

    Perhaps the future of C and C++ is like assembly today, where the great majority of programs are written in other languages that might call into a small assembly language stub when it really makes sense.

    More here: http://trevorjim.com/c-and-c++-are-dead-like-cobol-in-2017/

    1. Your headline is misleading because high performance is far from the only criterion you are using, otherwise assembly language would be at the top of your list.

      In the real world, you want to produce the best/fastest software possible given a time budget.

      Even so, I would not be able to consistently or even generally beat a C compiler on performance in assembly. I need tools to get the job done and programming languages and compilers are a must.

      At a higher level, if you don’t put enough effort into it, it is far from certain that your Java program will run slower than your Java program. And there are many cases where you just don’t have that much time. If I had to write very quickly some high-performance backend, I am not sure that C/C++ would win out.

      As long as you are using other criteria, you might consider that C and C++ are security nightmares, safe languages are much easier to program, and consequently, less and less programming is being done in C and C++.

      I relative terms, there is no denying that C and C++ are less popular than ever… but I am not sure what the picture is in absolute terms. I submit to you that there are probably more C++ programmers in 2017 than ever in history.

      Perhaps the future of C and C++ is like assembly today, where the great majority of programs are written in other languages that might call into a small assembly language stub when it really makes sense.

      Yes. This makes sense to me.

    2. I think it’s bizarre that you miss such a major topic as compile time dispatch. In C you are frequently stuck using function pointers which can’t get inlined. Or passing an array with fixed compile time size as a pointer + size into a function. This can easily lead to introducing indirection where it’s not necessary. This is very avoidable in C++ and D, mostly so in Rust (and afaik Swift), and very hard to avoid in Go and C.

      I also wouldn’t put safety at C++ as “minimal”, on par with C. While you can shoot yourself in the foot in either, RAII and the standard library definitely encourage you to write modern C++ quite safely.

Leave a Reply

Your email address will not be published. Required fields are marked *