Five Myths used in “Golang vs Node.js” debate

 
Author:  Follow: TwitterFacebook
Job Title:Sarcastic Architect
Hobbies:Thinking Aloud, Arguing with Managers, Annoying HRs,
Calling a Spade a Spade, Keeping Tongue in Cheek
 
 

Recently, I’ve read quite a bit of comparisons of Node.js with Golang. Unfortunately, Google search on this subject is dominated by, ahem, not so informed opinions – with lots of outright ridiculous claims; and while repeating ridiculous claim over and over doesn’t make it less ridiculous – it certainly can form a completely wrong opinion among developers, and even worse – among decision-makers. To help dealing with it, I made a collection of the most popular/most ridiculous myths about Node and Golang – to bust them here.

Myth #1: Node.js is single-threaded, so it doesn’t scale

Hare thumb up:if programmer does want to go multicore, all she has to do is to use goroutines (in Golang) , or cluster module (in Node)Probably the most popular deadly wrong point touted by competitors of Node.js is that, as [Sathananthan]1 proudly writes, “Node.js is single-threaded, which means that the execution instructions are performed in sequence.”.

Truth #1: If programmer does nothing to exploit multi-coring in her program, both golang and Node.js are single-threaded. However, if programmer does want to go multicore, she has to use goroutines (in Golang) , or cluster module (in Node). All one might want to argue that golang’s approach is better – however, this is at least arguable (more on it in Myth #3 and Myth #4 below).


1 an article which got #1 placement and Google “feature snippet” on “Node.js vs golang” search, is an article by an undisputed authority in the field a guy writing his 3rd-semester project. Bravo, Google!</sarcasm>

 

Myth #2: Node.js is interpreted

Numerous sources discuss allegedly interpreted nature of JavaScript; see, for example, [Harkushko]: “But since JavaScript is an interpreted language, it takes more time to execute code written in Node.js than it does to execute code written in Go.”

Truth #2: While performance of Node.js is indeed usually lower than that of Go, it has nothing to do with the language being interpreted (JavaScript as a specification doesn’t  need to be interpreted or compiled; it is particular implementation which is interpreted or compiled, and Node.js v8 engine does have a JIT compiler). This is corroborated by an observation that performance difference between the two is relatively small (see Myth #5 below).

Myth #3: Goroutines is The way to scale

Hare wondering if you are crazy:mutex-based stuff is inherently untestableIt is very often mentioned that Golang is designed for scalability; just as one example, the very same ultra-high-rated-by-Google [Sathananthan] says “Golang was really designed for scalability and concurrency, without too much hassle.”. This usually comes along with some wording about goroutines-based concurrency, at least implying that scalability can/should be achieved with goroutines.

Truth #3: In fact, goroutines have a potential Really Big Problem(tm) when it comes to scalability – as goroutines DO allow accessing non-const global variables, this not only opens the whole can of worms reliability-wise (heck, mutex-based stuff is inherently untestable!2), but also limits scalability to one single box (moreover, Shared-Memory systems DO suck scalability-wise even on single box; this has lots of reasons ranging from Amdahl’s law to unnecessary cache invalidations due to mutex-induced thread context switches). Granted, current golang practices DO encourage channel-based Shared-Nothing concurrency – which indeed DOES scale, but to the best of my knowledge, (a) it is NOT enforceable in Go, and (b) golang run-time (unlike, say, Erlang/OTP runtime) doesn’t support/detect “pure” nonconst-global-access-free goroutines (which could be moved even to another box if necessary). BTW, Node.js is Shared-Nothing by design – enforcing the only scalable model out there (and also saving us from LOTS of next-to-impossible-to-find bugs as a side benefit).


2 and Go’s runtime race detection provides only a palliative care to this Big Fat Problem(tm)

 

Myth #4: Concurrency in Golang is easy

Judging hare:concurrency (leave alone efficient concurrency) is never easy[Cheney] says “Go’s headline feature is our simple, lightweight concurrency model. As a product, our language almost sells itself on this on feature alone.”.

Truth #4: Golang or not, concurrency (leave alone efficient concurrency) is never easy. To illustrate it, let’s take a look at an excellent article [Deleplace] on an optimization experience with Golang. Long story short – first, supposedly “good”3 Golang code was made 2.5x faster by removing fine-grain concurrency and making the whole thing sequential(!!). Then, when adding a coarse-grain concurrency, wall-clock time did improve further, but number of goroutines going into 120K proved to be too much for Go runtime (causing 12 cores to perform merely 1.5x better than single-core version – that’s about 8x inefficiency CPU-wise, energy-wise, and CO2-footprint-wise)4, so final implementation (the most efficient one, gaining whopping 23x improvement over original “good” code) was based on 12 workers. But hey – not only this kind concurrency optimization struggle is very similar regardless of the programming language, in addition I cannot help not to mention that 12-workers for 12-cores would be the way we’d scale our Node.js implementation in the first place! In other words –

efficient concurrent implementations happen to be strikingly similar in Golang and Node.js

There is no magic out there – and while costs of goroutines are somewhat lower than that of threads, common techniques such as going coarse-grain and limiting the number of outstanding tasks are still necessary.


3 at least some Golang developer wasn’t shy to put it to the Github as an explicit example of a “good” code
4 effectively busting yet another myth of Golang being ok with millions of goroutines

 

Myth #5: Golang performance is similar to that of C/C++

[Sathananthan] says that Golang has “Similar performance characteristics as with C or C++”.

Truth #5: if we take a look at IMO-the-best-inter-language-benchmark-results-available ([Debian1][Debian2]), we’ll see that Go lost all 10 benchmarks to GCC C, losing from 20% to 5x (with a median around 2.5x). Moreover, if speaking about performance of Node.js ([Debian3]), it happens to be between 20% faster and 5.9x slower than Golang (with a median of 1.4x). In other words,

Performance-wise, Golang is closer to Node.js than to C/C++

Bottom Line

Surprised hare:I do NOT feel that either Node.js or Golang is 'better' per seMyth busting aside, I do NOT feel that either Node.js or Golang is “better” per se; in a sense, they’re strikingly similar (especially if speaking about Go with channels instead of mutexes), so all the attempts to push one of them as an “inherently better” one are pretty much pointless. The only field where Golang has a realistic edge, is that 1.4x performance advantage, but this is going to change with an advent of Node.cpp, which will enable C++-like performance for those-Nodes-which-need-it.

 

Don't like this post? Comment↯ below. You do?! Please share: ...on LinkedIn...on Reddit...on Twitter...on Facebook

Acknowledgement

Cartoons by Sergey GordeevIRL from Gordeev Animation Graphics, Prague.

Join our mailing list:

Comments

    • "No Bugs" Hare says

      I’d agree – but as now Node gets Typescript (with classes – which Golang is still lacking), the comparison becomes less obvious.

  1. says

    Go brings some nice things that Nodejs lacks as far as I am aware. Some crossing my mind:
    – fast cross compilation cpu arch and os wise to static binaries
    – easily have no C/C++ dependencies and everything written in Go or assembly only
    – Go tests, language server and linter seem to run slightly quicker than node ones in my experience.
    – No huge node_modules required to run in production, just a ~10MB static binary

    Then Nodejs with Typescript is quite nice especially for running tasks involving Javascript such as server side rendering for example, where Go might not be suitable. And there are many more Javascript developers than Go developers too.

    Anyway, thanks for the great article!

    • "No Bugs" Hare says

      Sure, thanks! The point of this article was NOT to compare Node.js to Golang; the point was to exclude quite a bit of popular BS (which Google pushes to the top of “node.js vs golang” searches), so a fair comparison can be made.

      That being said, I have to note that for any major OS it is next to impossible to “have no C/C++ dependencies” (system calls are 100% C for {Linux,Win32,Mac OS, Solaris, AIX, …}, so at least _some_ C code will be normally required to call system-level stuff – well, unless Go implements C ABIs but this would be a long stretch).

    • says

      Instead of using huge ‘node_modules’ you can always use some packaging software to put the entire code into one 10 MB ‘.js’ file and keep sourcemaps for debugging. Thanks to that you can use minified/packed code in production and still have the possibility to find the exact file and line of original code while debugging.

      I think Node.js and Go can be complementary. Some things are easier to code in Node.js, some are easier to do in Go. Node.js seems to be more practical with webapps since it’s much easier to write isomorphic code (reuse code in the browser and on the server side like render React.js views server side and use the same views on the frontend). The closer to the OS specific features the more practical Go seems to be.

      It might be a good choice to make a ‘frontend server’ in Node.js that renders views, serves assets, css etc (there is a huge JS ecosystem with ready to use solutions that do those tasks) and separate ‘api server’ in Golang that serves pure API responses, does authentication and communicates directly with the database.

  2. a-square says

    Go has this great green thread engine that makes it very easy to write concurrent code (I don’t mean write efficient concurrent code, I mean write concurrent code at all). In Node.js, you have to use closures for this, which is a more difficult programming model to work with. So if one’s writing a service that queries a couple more services or data sources, tosses JSONs around and returns a result, Go is a better choice IMO, especially for inexperienced developers.

    What do you think about Deno BTW?

    • "No Bugs" Hare says

      ” In Node.js, you have to use closures for this,” – or in recent Node, we can (and IMO SHOULD) use await, which is IMNSHO a FAR superior model compared to BOTH lambdas and goroutines (see http://ithare.com/eight-ways-to-handle-non-blocking-returns-in-message-passing-programs-with-script/ ), especially if we do need to work with stateful Nodes (and being stateful is in turn a prerequisite if we want to avoid pushing all the changes onto DB, which does not scale at all – see, for example, Uber’s problems with migrating from MySQL to Postgre and back – though, as we discussed with late Herbert Matthews, the whole thing could have been solved by in-memory state spending orders of magnitude less efforts).

      Bottom line – we COULD argue about goroutines-vs-lambdas, but argument about goroutines-vs-await doesn’t have any realistic ground.

      As for Deno – feelings are mixed:
      CON: Rust is overhyped – both safety-wise and performance-wise (my own C++ will run in circles around it – well, it runs in circles even around Node’s C-based libuv 😉 );
      PRO: Typescript is good
      PRO: permission system looks ok
      CON: loading modules from URLs? Seriously? In production? Gimme a break!

  3. Roman Leventov says

    A major consideration is ecosystem maturity (libraries and tools). I don’t have experience with Go, but moving from Java to Node, I was depressed by the state and the quality of libraries (the majority of them are practically not supported at all, publish-once code) and tools (IDE, build tools)

  4. Pseudonym says

    If Alex Stepanov were he, he would point out that Java, Go, and Node.js have similarly fast performance, but only if your program is valid Fortran.

  5. Daniel Garcia says

    Completely off-topic, but to this day I don’t understand why this site is not served over HTTPS when there are completely free and easy solutions out there. Completely ironic to find that on a blog that posts about Internet security.

    • "No Bugs" Hare says

      Most of HTTPS solutions out there is a big profanation, creating nothing but a false sense of security. OTOH, one of security maxims says “you need only to increase the cost of the attack well beyond the cost of the damage it will cause” – this is already the case here.

  6. JD says

    No matter how Excellent Node.js is, as a 10 years Java and 6 years Node.js developer, after one year Golang development, I haven’t found any reason to go back to Node.js and Java. If you want a relax life, choose Go not Node.js.

    • "No Bugs" Hare says

      Golang has a half-baked concept of “goroutines” which brings back all the nightmares of multithreaded programming (granted, mutexes are discouraged, but they are still supported, which opens the door for undebuggable bugs, and prevents some all-important optimizations).

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.