Brian Lovin
/
Hacker News
Daily Digest email

Get the top HN stories in your inbox every day.

byroot

Lies, damn lies and benchmarks.

The first benchmark is pretty much an empty request, so it measure each framework "overhead". That shows Rails+Puma at `0.25ms` overhead per request, and rage+iodine at `0.013ms`. That is significantly less yes, but it the overwhelming majority of case it absolutely don't matter, and this difference mostly come from Rails providing extra features.

As for the second one, it's entirely irrelevant. It's benchmarking a purely IO workload with Puma caped at 5 threads vs Iodine without a concurrency cap. You can increase "Rails" throughput about as much as you want in that benchmark by raising the number of Puma threads or switching to Falcon.

But also, not capping concurrency like this is risky. The second some CPU heavy work is mixed with this pure IO work, latency will suffer terribly. It's fine for a micro-service where you know it's really all IO though.

Disclaimer: I'm a member of Rails, but I welcome alternative frameworks, I'm just very annoyed at how terrible most benchmarks are.

boundlessdreamz

Also this doesn't show how database access is handled which is the hard part. If you are not touching the database, you can run Rails on falcon and get fiber based concurrency.

If you run falcon on rails and access database, then you have to explicitly checkin/checkout a connection to be safe. Details here - https://github.com/rails/rails/issues/42271.

andai

So what would be a better benchmark? Perhaps a "standard" "real world" app, like https://github.com/gothinkster/realworld

> The RealWorld Demo is a large community-backed open source project that showcases all web frameworks client or server-side through a building an example app that is much more substantial than the typical TodoMVC toy demos or the stripped out scenarios you find in benchmarks. As Conduit, a Medium clone of sorts, your application has to handle real issues like authentication, routing, and async data loading. It has a standardized specification [...]

Or something simpler?

byroot

> So what would be a better benchmark?

It all depends on what you are trying to demonstrate. The current benchmarks would be fine if properly contextualized and presented a bit differently.

e.g. for the first one, instead of representing it in request/second, do a breakdown of average/p50/p90/p99 latency over X requests. That's both much more interesting data, and much less misleading.

Even the second one could be fine if used to demonstrate why an event loop can be the appropriate thing to use for IO heavy workloads.

But here they are just included with no explanations, implying a blanket "we're 25 times faster than Rails".

whalesalad

TechEmpower has a few different classes of benchmark. https://www.techempower.com/benchmarks/

Off the top of my head:

- json serialization

- fetching random objects from an actual mysql/psql database

- cached queries

- performing mutations / data updates

writing "hello world" as a response is naturally going to do 75k per second

ricardobeat

> It's benchmarking a purely IO workload with Puma caped at 5 threads vs Iodine without a concurrency cap

It appears the benchmark sets the Iodine thread count to 1 as the legend says. Do you think that’s not the case?

Facil.io uses an event loop to handle high concurrency in a single thread, results are not out if line with code that used LibUV.

byroot

That is precisely my point. One has concurrency capped at 5, the other has no cap (because it uses an event loop).

The Puma process is likely 99% idle in that benchmark, you could crank up the thread count to 50 and get (not quite) 10x better results.

An event loop is definitely more efficient than a thread pool for such a work-load, I'm not denying that, but in this specific instance Rails+Puma is configured (granted it's just the defaults) with ball and chain.

I'm not saying the benchmark is dishonest, I'm saying it makes no sense.

ricardobeat

That objection makes no sense. The fact that Puma is idle most of the time reflects a disadvantage. The event loop makes much better usage of a single thread, and that shows in the benchmark.

It's like saying comparing Node to Rhino, or Vertx vs Spring Boot is unfair. Better performance per thread / core is the whole point.

You will not achieve the same level of performance with threads except in low-volume benchmarks, and _especially_ in any kind of cloud environment where you usually only have a single core and a shared kernel.

chitinbottler0a

I totally see how you might find these benchmarks irrelevant or "unfair". But there are no lies - I didn't make those numbers up.

These are pretty standard benchmarks showing the overhead of a framework and how it behaves with certain types of operations.

alberth

Slightly off topic: what is the preferred way to deploy Rails these days?

Is it to use Puma? Unicorn, etc?

xiljin

There are several options but Puma seems to be the most popular at the moment.

A derivative of Unicorn named Pitchfork is also in the works from Shopify - https://github.com/Shopify/pitchfork

PikachuEXE

Puma works fine most of the time (config it accordingly of course

No idea what other options are out there though

compumike

We use Puma in production.

neonsunset

There is certainly a lot of speculation in Techempower benchmarks and top entries can utilize questionable techniques like simply writing a byte array literal to output stream instead of constructing a response, or (in the past) DB query coalescing to work around inherent limitations of the DB in case of Fortunes or DB quries.

And yet, the fastest Ruby entry is at 274th place while Rails is at 427th.

https://www.techempower.com/benchmarks/#hw=ph&test=fortune&s...

rajaravivarma_r

Except, increasing the threads increases the memory usage proportionally. From what I have seen, the memory won't be released back and it keeps increasing until the process gets OOM'ed. At least this was the case with Ruby 3.0 and 3.2.

PH95VuimJjqBqy

My whole thing is that ruby and performance don't mix, choose something else.

I'm not saying don't bother optimizing something written in Ruby, I'm saying Ruby has inherent limitations in terms of performance. If performance is your goal you're using the wrong language.

krmbzds

> API-only - the only technology we should be using to create web UI is JavaScript. Using native technologies is always the most flexible, scalable, and simple solution in the long run. Check out Vite if you don't know where to start.

Thanks. I'll pass on that one. Nevertheless, both rage and iodine seem like pretty cool projects FWIW.

KronisLV

>> API-only - the only technology we should be using to create web UI is JavaScript.

> Thanks. I'll pass on that one.

I don't know, there's definitely some merit in separating your front end from your back end - at least that's the impression I got after struggling on JSF/PrimeFaces Java projects for a few years, because at least with a well defined API and no magic behind the scenes everything becomes more observable and easier to reason about, with fewer bugs. Some might prefer TypeScript or something else to JavaScript, but I think the point still stands.

In addition, this lets migrate or change everything piece by piece, as long as the interface isn't broken:

  - different frameworks (let's say from Spring to Spring Boot, or from the old ASP.NET to the new variety that runs on Linux, or Rails to Rage or vice versa)
  - different languages (if someone decided to take their PHP Laravel solution and migrate to Ruby with Ruby on Rails, or Python, or anything else)
  - deprecated technologies can also be handled gradually (e.g. supporting an EOL AngularJS solution while developing a new front end in Vue/React/Angular/whatever)
As for the reason for those migrations, sometimes code just rots due to neglect, other times you need features that aren't available in your solution of choice (for example, pre-made UI components, like PrimeVue, PrimeReact and PrimeNG have; or maybe needing to have responsive maps in the app). In addition, you can also deploy your front end and back end separately, have different rules for the reverse proxy in front of them, suddenly have a very clear view of where your static assets are, might have all of the benefits of a SPA and PWA (if needed), anyone can integrate with your API if needed/allowed because you've hopefully built it to actually be usable and so on. Plus, your front end can talk to multiple different APIs, so if you have multiple teams working on those, suddenly that is a solved issue for you as well.

That said, it's also a lot of work because SPA and adjacent approaches bring a lot of complexity into the mix and something like Vue/React/Angular and its ecosystem will bring its own pain points and things you have to learn. If all you need is a bunch of forms, then pretty much anything will do fine and SSR is a good choice. If you need an "app" on the web, then HTML/JS/CSS isn't a very good choice to begin with (historically not built for that; though I don't think there are actually good choices, same as with desktop software), but those SPA solutions will see you pretty far.

Draiken

Use HTMX and you'll be surprised how much you don't need JS for (or to be more accurate, very little JS).

Rails especially is very good for this with partials. Use ERBs (or Haml, Slim, etc) and it's an absolute bliss with the added benefit of easy caching.

Stop duplicating state. Stop duplicating logic. Maybe in the future when your app needs 5 different clients, it makes sense to only have an API. But let's not pretend that should be the default!

I still cry every time I see a new application using GraphQL to have literally one client which is simply serving HTML with a bloated JS framework. The whole premise of it was to be flexible for multiple frontends. Come on folks, YAGNI.

egeozcan

I used HTMX since the intercooler days [0] but the stuff you can make is rather limited. Also you still need the JS to deal with a11y things like expanded state (or hyperscript, apparently).

If you have a lot of components to implement, everything requires thinking. Example: You want to add a repeat field to a form. Easy, get the input from an endpoint, and append. Done? No, how do you remove a field? Add a line of JS, simple! Drag and drop if the order needs to change? Let's add a dependency! [1]

I really love it for simple applications though. Resist implementing a complicated menu, live notifications, an editable data-table and such non-web-native things and you can create the fastest CRUD app ever.

And you will need another client (don't YAGNI me on this one, burned too many times by people not caring about endpoint design), but that's not really an issue if your view model does not contain non-public data (it shouldn't), as you can convert it to JSON at the same endpoint (respect the accepts header) and call it an API.

[0]: https://intercoolerjs.org/

[1]: https://htmx.org/examples/sortable/

ecshafer

Rails with turbo and stimulus is amazing. Very similar to htmx and phoenix liveview. Stimulus lets you make a really minimal web framework for ui elements that are mostly defined as ruby components, and turbo lets you to use erb without whole page reloads. Whole responsive web apps with a dozen lines of js, its great.

_heimdall

> I don't know, there's definitely some merit in separating your front end from your back end

This is 100% true and the biggest loss in web development in my opinion.

SPAs actually end up making this worse in the long run though. The major SPA frameworks are all moving to an RPC model of all things, entirely coupling your frontend and back end together. The original SPA approach did offer the ability to separate frontend and backend, but the frontend scaled poorly and brought in too many complex considerations like routing, caching, error handling and recovery, etc. The new approach is basically abandoning the benefits of an isolated back end in favor of tight coupling simply because the existing frameworks are too far down the wrong path.

If your goal is ever to isolate the frontend and backend you'll want to consider three environments - backend, web server, and frontend. Ironically that's exactly where we were when SPAs broke out onto the scene

pmontra

> The major SPA frameworks are all moving to an RPC model of all things, entirely coupling your frontend and back end together.

I'm not familiar with this development. Do you mean that for example React has grown a backend running on a server, querying the database, etc?

I know that backend frameworks like Rails and basically all the major ones added a JS layer running inside the browser and obeying to the backend. It listens to messages from the backend with changes to the UI elements and that saves full page reloads.

krmbzds

I think DHH makes a good argument in Rails World 2023 Opening Keynote; in particular "Fed Pauses Rate Hikes But Stiffens Long-Term Outlook" slide.

Check it out: https://youtu.be/iqXjGiQ_D-A?t=665

jhfgloria

Came here just to say your first 2 sentences.

Draiken

Yeah they lost me there. That's so silly. Absolute takes like that are why we're in this mess we are in today.

Don't make me think! Just follow the trend.

brunojppb

> the only technology we should be using to create web UI is JavaScript

Quite an interesting idea, but stating that JS is the true way of creating Web UIs misses the mark by miles. You would be surprised by how far you can get with HTML and CSS alone. You will ofc need JS for more dynamic interactions, but ditching the server entirely and delegating everything to JS will just take us to the SPA mess that most of have been burned with.

ecshafer

Especially now that rails is shipping with turbo, hotwire and stimulus. Using no (additional) javascript you can make awesome performant reactive webpages.

efields

I want to believe. Really I do.

Most recent app I worked on used Mantine for a base component library. Having such a large collection of drop-in components made the app come together very quickly. Performance matters but a well built react front-end for a solid MVP is performant enough.

I haven’t seen a vision like this for the stimulus/htmx world. How do you import components?

ecshafer

My team is usually view components in our application. Basicslly ruby erb renders the view component in a turbo frame. Turbo allows async updates without redrawing. Then we use stimulus to call small js component that are put into the view components.

kshahkshah

I think it's assuming a static site

felixding

> the only technology we should be using to create web UI is JavaScript

No.

stefcoetzee

What do you prefer?

(I quite like ClojureScript.)

pocketsand

HTML, the language on the server

revscat

I prefer not to use JavaScript unless forced to do so. It's burdensome and almost always unnecessarily complicates the stack. With the rise of tools such as Turbo[1] and HTMX[2] the need for JS frameworks like React and Angular are greatly reduced, to the point that they are (almost?) unnecessary.

[1] https://turbo.hotwired.dev

[2] https://htmx.org

egeozcan

I'd always prefer React for when you have dedicated Front-end developers but for small go projects, gomponents with its htmx add-on does wonders.

treyd

We've been using the Dioxus library for Rust at my company and it's worked well. There's other Rust WASM web UI frameworks as well.

namtab00

Blazor

yes, I am THAT guy...

Scarbutt

That's worse than using plain JS.

multiplegeorges

Uhhhh, HTML?

Alifatisk

Flutter

Alifatisk

This was unexpected but welcome, finally something new and fresh to compete with what we currently have. Since it's Rails-compitable? Does that mean I can connect Avo, Bullettrain, Rack-attack aswell?

Can't wait for this to popup on techempower benchmarks! But I would've hoped that there was some proper benchmarks done instead of these unrealistic ones.

westoque

Most of the speed I believe is from using the server iodine https://github.com/boazsegev/iodine which is a wrapper around facil.io https://facil.io that is built using C.

Great project either way!

tomku

Is facil.io maintained? Last commit was 2 years ago, which is a bit concerning for something written in C that handles a bunch of security-sensitive things including a from-scratch JSON parser.

todd3834

I wonder if the name comes from how funny “RageController” is to read in the code

Fire-Dragon-DoL

I was thinking the other day, what exactly is the purpose of this. It seems to simulate controllers, but activerecord is a big part of rails.

I still love the idea, but I don't foresee a migration path since activerecord is still necessary for a migration path

pantulis

I am also a little bit confused, if you are touting Rails compatibility, how can you be that much faster? Something has to be left.

But it's sooo interesting! I would love to see it merged with Rails core like Merb on its day, one can dream!

asok86

It seems that you can use whatever ORM you'd like. ActiveRecord included.

benbonnet

[dead]

MattyMc

“… the only technology we should be using to create web UI is JavaScript.”

Well, the Rage team certainly is opinionated!

tebbers

DHH++

thiago_fm

Great idea, wish it would be merged with Rails itself, but without the opinionated lines, like "the only technology we should be using to create web UI is JavaScript"

It's a really stupid line and whoever wrote it is embarrassing themselves.

Also, a more real/demanding benchmark would be great.

Keep it up!

Spivak

They're right and they should say it. You would think I was insane if I made a CLI tool worked by fetching and running dynamically generated pre-compiled binaries from a server for each subcommand command and yet that's what we do with erb template style apps.

The API + static versioned self-contained html/css/js asset bundle is the way for anything you would reach for erb for.

jack_riminton

Interesting. I'd like to see a full app's code to find where the skeletons are hidden.

I don't mind the opinionated take on JS, each to their own

rajaravivarma_r

This is great. I had been long looking for something like this in Ruby eco-system. I worked on a Rails app which interacted with a bunch of micro-services and I missed the async-await so much. The problem was, when the app was waiting on API requests, the throughput suffered a lot.

We couldn't increase the number of threads a lot, since it increased the memory usage a lot which eventually led to OOMs.

I tried to put something together using async-http [0], but it was not very stable.

One question would be, how does scheduling work with existing psql and myself db gems. Should the calling code wrap the database calls with `Fiber.await { Fiber.schedule { ... } }`?

A framework like this would be very useful when the tech-world is moving towards micro-services, for the good or bad. [0](https://github.com/socketry/async-http)

chitinbottler0a

> Should the calling code wrap the database calls with `Fiber.await { Fiber.schedule { ... } }`?

Nope. Scheduling works seamlessly, thanks to Ruby core.

Daily Digest email

Get the top HN stories in your inbox every day.