Hacker News new | past | comments | ask | show | jobs | submit login
ExpressJS vs. Actix-Web: performance and running cost comparison (medium.com/maxsparr0w)
66 points by dunnock on March 1, 2020 | hide | past | favorite | 53 comments



Is there any surprise here? Anecdotally Node seems fast for interpreters, but of course a compiled systems language like Rust blows it away. That’s a known trade off you make when picking a technology like Node; there are plenty of other reasons to use it.


Even if you think a compiled language will be faster, it might still be interesting by roughly how much (5%, 10x or 100x?). This can help make the trade off decision.


I can only think of ecosystem size and language familiarity. Authors addressed development speed and found that Rust's static typing and compiler checks reduced development time vs JS. What other reasons did you have in mind?


I’m skeptical of this finding out at least it’s generalizability. I know (and like) Rust a fair bit better than JS, but I would be surprised if I could build a nontrivial web app in Rust faster than Node. And that’s without using TypeScript. YMMV, but I’d be curious to hear from others with experience with both languages.


I've used Rust for two years and still regularly get stumped.

Recently, I wanted to maintain a map of routes to futures so that multiple in-flight requests could await the same future while it fetches from disk.

The Javascript version took about one minute.

Using Rust doesn't give you free performance wins, especially not when working with async code where lifetimes actually get hard. Any time you're carrying around your own event loop / cpu pool while trying to avoid blocking requests, that's also another bombshell in itself. Similar challenges when using something like NIO/Netty, especially towards the beginning.

Add things like sinking a stream of lazy futures into a response and it can be very hard to understand what your actual performance looks like and where things will block, especially with parallel requests. The only way you could say it's a free lunch is if you're a Rust expert who has done it before, which doesn't apply if you're considering Rust vs JS/TS anyways.

Not to say nobody out there should be writing Rust, that would be silly. But someone upstream is seriously asking what advantages JS could have over Rust and difficulty is one of them. And it's not a small one.


My first attempts with anything in Rust are also challenging. Second attempts, though, are straightforward as long as I have an example to work from and haven't forgotten about it. It would take me a lot longer to build your router example in Node than it did you because I haven't worked with Node but once I know how, maybe it would take me a minute, too.

What did you finally come up with for your router impl in Rust?


I'm fluent in both Rust and JS. IMHO first attempts are much faster in JS than in Rust. Rust is much easier to maintain in the long term, and for large scale codebases it excels once the groundwork has been laid.


JavaScript isn’t the only way to write Node.

I know Rust alright, and I like it. I’d rather write TypeScript for most web applications and I write good and solid TypeScript a lot faster than I do Rust. (Helps that I can easily write the same language and use many of the same libraries on my frontend, too.)


A common language across front and backend systems is a huge benefit. I find it extremely productive to have a backend in Typescript on NodeJS, and a frontend in ReactJS, talking over a GraphQL layer where I'm autogenerating Typescript types from the GraphQL typedefs. Means I can share many libraries and tooling across the front and back ends, but most importantly it means devs feel much more comfortable digging into code on the other side. If a front end dev has an issue with a backend API call, he can just pull down the code and instantly inspect it and submit a PR of necessary without feeling "out of his element".


How you generate the types from graphql. I noticed most of the generators create types with all optional keys. Do basically you need to check all the time...


If they're creating types with optional keys it's probably because that's how you're defining your types in GraphQL. E.g. with

type Foo { bar: String }

bar would be optional in the Typescript type because it's specified as optional in the GraphQL type. You would need to do the following to make it required:

type Foo { bar: String! }


I mean... All keys can be optimal in reality, but in real world you can't put all non optional because defeats the purpose of graphql.


Development speed is one. If the author were more familiar with JavaScript (e.g. knowing what const does) it might have gone faster. Using TypeScript should erase the development time delta due to static types.

If you're creating a frontend web app to go along with it there are others, such as reduced context–switching and using fewer tools.

Frankly, a sample size of one is incredibly weak evidence that it's faster to develop using one technology than another. If you glance through the author's Twitter/GitHub it's also clear that he works with Rust significantly more than he does with Node, which makes this particular sample even more suspect.


Indeed I am working primarily on Rust now, though I used to write Node 2 years ago and this is one of pitfalls where I would like to rely on compiler. It was just one issue which could been easily detected. Some debugging time was spent aligning SQL query parameters and mapping selected fields to the output type - the problem is that when there is some misalignment node does not raise exception, rather just passing undefined value. That's what takes time, to realize that there is a problem and then find the issue. Agreed that in general expert in node probably won't hit such issues, though how many experts do we have. Type safety checks, including runtime checks, are actually making technology more adoptable. It does not seem that typescript can do that well though.


Another upside: you'll never be stumped implementing something in Javascript/TS. There is no lifetime management. And the entire ecosystem is async-everything. You aren't stuck in an async-ready subecosystem like you are in Rust, Python's Twisted, Ruby's Event Machine, etc.


Well it’s rust and actix so ya no big surprise. But take a look at es4x. It actually blows away Nodejs and many other compiled languages/web frameworks. And it’s in JS! They are in the most recent benchmark of TechEmpower.


No one has ever been deciding between express and actix-web


I have, because I did a bunch of Express+TypeScript, always wanted to learn Rust and wanted to bootstrap a business by optimizing costs.

I liked this article since I always wanted to see concrete numbers or at least a general idea.

It's not uncommon for business to move from Ruby/Python/JavaScript to Go as they grow. I personally preferred learning Rust over Go because of its depth.


"... wanted to bootstrap a business by optimizing costs" -- The famous quote "premature optimization is the root of all evil" comes to mind. :-) As a fellow tech entrepreneur, thinking long and hard about my planned startup's technology stack, I was also guilty of initially leaning toward a scalable, safe, enterprise-y and fancy, but less mature, stacks, like C# on .NET Core and Julia, correspondingly. However, upon my extensive research and thinking, I'm now leaning toward using Python and Flask for API-focused backend and Vue for frontend. After all, unless you're the next Google (or, at least, Dropbox), costs of your infrastructure would be negligible compared to business costs associated with people (due to salaries) and lost time-to-market advantage (due to higher complexity and learning curve). Should a startup become wildly successful and scale-driven cost optimization would become an issue, it most likely would make much more sense from the strategy perspective to ask a team to rewrite the platform(s) - or, rather, its relevant performance-critical parts - into a highly-performant language, like Rust. Having said that, I still think that the containerized applications (not necessarily microservices!) approach might be preferable, despite corresponding increase in extra complexity, learning curve, efforts and costs, because IMO the above-mentioned extras are less significant (and, thus, more affordable for a bootstrapping startup) than those associated with attempting to use new to founders or relevant team or not mature enough language and corresponding technology stack.


I 100% agree with the point you are making.

A concret example of that is in a comment in this thread: https://news.ycombinator.com/item?id=22458176

That said you partially quoted me, if it wasn't for learning Rust I would stick to Express+TypeScript. If it's bound to fail, I might as well learn and have fun doing it.


I'm sorry about partially misinterpreting your words - it was not intentional. I'm getting myself a cup of coffee to improve my level of attention for the rest of the day. :-) Anyway, I'm glad that we're on the same page on the core issue discussed.


are you sure Maxim Vorobjov didn't make the decision at some point?


It calls expressjs minimalist, but it has 52 dependencies.

I really feel like we really need to reconsider what "minimalist" means in the js ecosystem.


A real minimalist web framework for Node is Koa. Basically anything besides the bare minimum is a separate dependency, so you only install what you need.

That said, it probably means minimalist in a usage sense. Compared to things like Nest.js (which I love, don't get me wrong), express is a lot more straightforward and basic.


Koa is better but still has 43 dependencies...


23 according to npm, with a size of 83kb. Sounds pretty minimal to me.


NPM only counts direct dependencies, so the reality is almost always higher.


I think the author wants to say that it’s like at the opposite of frameworks like Adonisjs, Sailsjs or Rails.


Can someone ELI5 the node code sample?

> full = full == "true";

Umm.. Why?

> if (!!limit

Not not limit? Is this due to some corner case conversion for JS to make 100% sure this gets into a boolean..?

I'm sure both samples have its reasoning, I'm just not aware. Any help please?


They are both ways to convert values to booleans. The top line would convert a string to a boolean. !! is a standard JS idiom to convert any value to its boolean equivalent, which can be somewhat complex due to the notions of "truthy" and "falsey" in JS.


Here is another hack we lazy devs used a lot back in the old days: double tilde operator[1]

[1]: https://stackoverflow.com/questions/5971645/what-is-the-doub...


“!!” is a way to convert any “falsy” value to a Boolean false. E.g. if you have a flag “enableFoo” that can take the values of undefined, null, numerical zero, NaN, empty string, and actual Boolean false, all these values will be converted to false with “not not”. All other values, including - ironically - “false” as a string, will be converted to Boolean true.


I even once got a `!!!someVariable` in a codebase I inherited. It's what convinced me that the previous devs weren't doing weird things to work around a bad backend: they just didn't know what they were doing


> full = full == "true";

That's basically a way to convert a stringified bool to a bool. Not the way I'd do it, and "assignee_name, summary, limit" are all declared as reassignable just to be able to reassign "full".

> if (!!limit

That's pretty common in js, not usually in if statements, but in other cases. No idea why that was done that way.


> full = full == "true"

When you send ?full=true in a query, your req.query object is { full: "true" }. It's not going to assume it can deserialize arbitrary strings.


A lot of Rust advocating here lately. But most miss the point. Nobody doubts the performance, efficiency, packaging solution or language design of Rust.

A more interesting question is:

As a CTO: Why would or wouldn't you choose Rust over Java, Node or Go?

Rust is not a new language (started in 2006, 1.0 Release in 2015). Why is its adoption kind of disappointing compared to its technical value?

The answers are not even more benchmarks or another even more wicked zero cost abstraction async runtime.

I'd like to read stories about some real world adoption. Can your average Java or Go devs become proficient in Rust in a reasonable amount of time? Is the code really mostly maintaince free, once it has compiled? Would it have been cheaper to just add some more servers to handle the load? How about large teams working on large domains (Java land)?


They are running Node with only one worker and are instead tuning the DB connection pool in that one worker... So the whole benchmark is bogus.


Author addressed this on a Reddit comment thread: https://www.reddit.com/r/rust/comments/fbu5tt/expressjs_vs_a... . The results aren't significantly different; Rust is still very much more efficient than NodeJS especially when considering memory utilization (and the hosting costs associated with that memory footprint).


I really don't think the results would be significantly different.

I tried to make changes and run his benchmarks, however the DB seeding is in Rust and it fails to compile on my machine....


It's worth recalling DHH's report that Ruby code only accounts for 15% of the cost of running Basecamp: https://m.signalvnoise.com/only-15-of-the-basecamp-operation...


[flagged]


“Safety talibans” is flamebait which will only distract from the topic


I understand that using Actix really highlights the speed advantage of Rust... but I think it's irresponsible to encourage rewrites in Rust using a framework that's dead because the developer decided to quit instead of fix security issues[0]. The last thing we need is a bunch of new Rust devs rewriting their perfectly fine Node code using an insecure framework.

Rust is really fast, especially compared to Node. This is an easy benchmark to win. So why not use a framework that's actually secure.

0. https://news.ycombinator.com/item?id=22073908


It's the only web framework in Rust that has thus far reached maintenance mode because it became feature complete, was heavily vetted by many teams, and is architecturally mature. It's not just really fast but has all of the bells and whistles. Further, people are actively contributing to the project. Replacing remaining sound yet unsafe blocks with fully safe rust, without taxing performance, is the remaining, ongoing effort at hand.

Your claims about being dead are wildly exaggerated and misleading.

You're in NYC as am I. The Rust NYC meet-up is a great way to connect and learn more.


> Your claims about being dead are wildly exaggerated and misleading.

I’d say they’re outdated. The project after the original author was frustrated with some in the community, found a way to allow the project to continue, which is overall good, but still a very disappointing episode.


Yes, indeed, but calling it dead and accusing the OP of acting irresponsible requires attention.

What happened to Nikolay could happen to anyone who stands up for one's craftsmanship and refuses to change out of principle. You, or anyone, can wind up being targeted.


I agree. I wanted to make sure it was clear why people might have the misunderstanding that the project development isn’t continuing. I think it’s worth directly correcting that perspective.


Express.js active development seems to stop. The last commit on v5 branch was from October 2018. We are still waiting for http2 (2 years) or better support for promises (3 years).

  https://github.com/expressjs/express/tree/5.0
  https://github.com/expressjs/express/pull/4196
  https://github.com/expressjs/express/issues/2761
If someone is doing a new project in node I would recommend:

  https://github.com/fastify/fastify
  https://github.com/hapijs/hapi
In the above benchmark, fastify would probably narrow the gap.

  https://www.fastify.io/benchmarks/


I feel like Koa JS deserves a mention.

https://koajs.com/


Koa is great and makes the two obvious improvements to Express: first-class promises, and bubbling up a representation of the response instead of having your routes directly send the response. So you now have middleware instead of just Express' "beforeware".


If it is a small project I'd recommend just using the built in http server. It is what all these projects use anyway, and if it's just a few routes it's usually easier to just use that instead of pulling in more dependencies.


Better support for promises? Koa 2 since 4 years ago.


The project seems active: https://github.com/actix/actix-web


Yes, it is active with many recent contributions. The goal was to show how much opportunity is there for making things more optimal from running perspective. Imagine how much of cpu power and electricity wasted




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: