Frameworks Round 3
techempower.com
techempower.com
1) I am shocked how slow EC2 is and how expensive. A m1.large is $0.240/hr or ~$175/month. And it's 7-10x slower than a $350/mo dedicated box. You would be spending $1500/mo to equal one dedicated box (not including bandwidth and S3 fees). A reserved instance is cheaper of course.
2) The multiple queries test would seem to be the best one to really simulate real-world usage. JSON-serialization is mostly testing the language.
This test pretty much puts most of the interpreted languages on a full stack framework together at the bottom. Although django seems to come out at the bottom of the pack for some reason.
Then come the "raw" tests and JIT languages running full stack frameworks. And at the top are the compiled languages. Not really surprising there.
3) I'm surprised php-raw did so well. And that go did so poorly.
Financially and performance wise you're probably better off on dedicated hardware with some over-provisioning but first you need to know how many machines you're going to need.
"Select random number from MySQL"? I'd like to see the stochastic control for that one.
Note that after the warm-up, MySQL is essentially guaranteed to have the entire table in its own cache, so the exercise is testing the ORM (if applicable), database connection pool, and the database driver.
Honestly, assuming none of the implementations are using an expensive random number generator (e.g., a random number generator intended for cryptographic use), I don't believe there would be different results if it were feasible to sequentially retrieve rows without the need to contend with concurrency.
[1] https://github.com/TechEmpower/FrameworkBenchmarks/issues/11...
I have a feeling it's ORM that's killing these frameworks in the DB portion.
I'm the user who contributed symfony 2 to the benchmark. My intention was to get as much php frameworks in the next round as possible (Symfony 2, Codeigniter, Laravel etc.), so I didn't pay much attention on the possible optimisations of the frameworks. On top of that I'm not a php guy, so it's possible that every php framework I contributed to the benchmark runs on breaks. ;o)
In Symfony's case I posted on the mailing list in order to get users with symfony experience to improve the performance, but this post was sadly not activated/moderated.
Also, with most frameworks, they're usually quite complex; how else are you going to be everything for everyone? Complexity comes at a cost, most notably in framework land this takes the form of code with very tall inheritance structures and/or tons of dependencies.
You need Foo? Ok, Foo inherits from BaseFoo, which inherits from CoreBaseFoo, which inherits from the Widget class, which itself is a BaseWidget, etc. Let's implement a few interfaces too, and now load in various dependencies... pretty soon that simple, 5-line class that you implemented now requires 100+ classes.
Things like op-code caching can greatly reduce this per-request penalty, but it doesn't change the fact that it still happens.
Typically php frameworks follow a java-style model of unserializing data into objects, loading the corresponding class files on-demand, calling API's on the objects, and then reserializing. It is _much_ faster if you treat the data as a stream, cutting it up and transforming it as it passes through your code, without ever building up an object representation, and not doing any more deserialization than a simple json_decode (which is really fast). This is in fact the original PHP model, transforming a stream of annotated HTML.
Doctrine is slow, and this is not how you would use it in the real world. Throwing a cache on top of everything will speed it up considerably.
[1] https://github.com/TechEmpower/FrameworkBenchmarks/issues/13...
Additionally you are not bootstrapping the Symfony cache correctly, you need to run "php app/console cache:clear --warmup --env=prod --no-debug" before running the tests, it might cause cache slams in your benchmarks.
metadata_cache_driver: apc
Else you are benchmarking the annotation and DQL parsers.
Also, for big PHP frameworks you have to make sure that APC's SHM size is large enough.
apc is no-config, no-cost php accelerator :)
Also, minor typo: the link to Erlang from the Environment tab is not the right URL. You probably meant to reference http://www.erlang.org/, not http://www.erlang.com/
For example, the last time I checked their php-raw code, it was executing a database query and appending the raw results to an array. Then, it took that array and passed it to json_encode which I'm pretty sure is implemented in C (PHP's built-in functions are in C, yes?). Anyway, comparing that to something like Rails' ActiveRecord, the work is quite different. With Rails, it's going to go through and call the accessor methods on the objects to get the data because we often override the default data stored in the database for the representation we want on an object level. So, they aren't doing the same thing. Rails at least used to have a way to get back an array of hashes rather than getting the AR objects, but I haven't been able to find it recently. That could be an interesting comparison.
So, in that case, it just isn't the same functionality.
Similarly, it's hard to know exactly how they're setting things up. Django will perform significantly better if you set up something like pgBouncer for connection pooling. Rails includes connection pooling, IIRC. Without knowing something like that, it's hard to gauge whether it reflects real-world usage (I mean, I'd argue that defaults matter, but if you're looking to scale Django, this isn't an onerous addition).
Similarly, with process-based concurrency, it's important to get settings that maximize CPU usage while not overloading the RAM. Since they don't talk about memory or CPU usage, it's hard to figure out whether they've dealt with this. It might be in their repository, but I haven't been able to go through it yet.
Heck, the multiple-query test in go seems really odd. I mean, coming in last place, serving less than half the requests of Rails? Similarly, Play 1 hits a nice 28% on EC2 and then can't serve a single request on the dedicated hardware.
Plus, the tests really stress JSON performance and don't use things like a framework's templating system. One doesn't need to argue that JSON performance is useful, but so is template performance and that just isn't being tested.
So, yeah, it would be hard to make conclusions off of this data except possibly that benchmarking is hard. None of this comment is meant as a dig against the people doing these benchmarks. They're improving them, they're making their code open, they're doing the kind of stuff that allows good, open questioning on their results. But I think it's still early to consider these to be really meaningful. I think a test that combined a bunch of different types of requests with database access, JSON, templates, etc. would be interesting and it seems like they might go there as they have more time.
The php-raw test, as with all tests that have the "raw" suffix is not using an ORM. The servlet-raw test uses raw JDBC. The tests without the "raw" suffix are assumed to be using an ORM or something ORM-like. There is a separate PHP test (named just "php") that uses PHP ActiveRecord.
To be clear: without the "raw" suffix, we expect the test to be exercising the framework's preferred ORM or something analogous to an ORM. For example, several but not all of the Java tests are using Hibernate.
I am of the opinion that it's of great value to include the raw tests alongside the ORM tests for comparison. Later versions of the results view will allow filtering of the results (e.g., filtering out the "raw" tests or filtering out all Java frameworks) [1] [2].
Django is being used with MySQL and does not (presently) have a connection pool [3]. We'd gladly accept a pull request that adds a MySQL connection pool. Separately, we aim to eventually add Postgres tests.
As for frameworks that use process-based concurrency, we have attempted to configure each according to the capacity of the hardware. For example, for a given process-concurrency framework on EC2 large, with two virtual processors, we may use two workers; on i7 with eight HT cores, we may use eight workers. You can review the configuration details in the repository and submit pull requests if they are wrong.
We aim to add server-side statistics capturing in a later round [4]. However, for the time being, I can say that anecdotal observations show that some frameworks do not saturate all CPU cores, but we have not observed any running into out-of-memory situations.
Regarding the curious Play1 and Go database test results, see elsewhere in this thread [5]. In both cases, the communities have offered to help address the problems the tests are running into. The Play community has already fixed the Play1 database test and we expect it to perform in line with Play1 + Siena in Round 4.
I agree that testing more components is desirable. The next test will include some minor work with collections and server-side templates [6]. We have started implementing Test 4 on a few frameworks and have been very pleasantly surprised to see the community has already started submitting implementations in their favorites as well.
Thanks again for your detailed thoughts and we look forward to continuously improving this project for as long as we have the time to do so. Please feel welcome to join in the conversation on any of the Github issues or create new ones.
[1] https://github.com/TechEmpower/FrameworkBenchmarks/issues/15...
[2] https://github.com/TechEmpower/FrameworkBenchmarks/issues/12...
[3] http://www.techempower.com/benchmarks/#section=motivation
[4] https://github.com/TechEmpower/FrameworkBenchmarks/issues/10...
[5] https://news.ycombinator.com/item?id=5590132
[6] https://github.com/TechEmpower/FrameworkBenchmarks/issues/13...
That said, there are still a lot of rough patches with Yesod, and I'm sure there are lots of opportunities to improve performance in various small ways. The main Haskell compiler (ghc) is constantly improving and many of the changes allow for smarter automatic optimizations to be applied by both the compiler and the runtime. With continued refinement I feel it's definitely possible for Yesod to match, and even exceed the performance of any of the JVM based languages, but as I said, there are a lot of performance optimizations baked into the JVM and it's probably going to take a while before Yesod (and WAI which it's built on) have similar levels of optimization in them.
Please reply to let us know if you have any comments, questions, or criticisms. We'd love to hear your feedback.
[1] https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...
Edit: I've edited the Environment Details page accordingly.
With client session disabled, I'd expect it to be close to the top along with some of the micro frameworks.
[1] https://groups.google.com/forum/?fromgroups=#!topic/play-fra...
[2] https://groups.google.com/forum/?fromgroups=#!topic/golang-n...
Are many websites running into speed or scalability issues?
I see programmers in other areas spend a lot of man-hours optimizing code that isn't a meaningful bottleneck. Is the same thing happening here?
This is great work, though. Thank you for doing it -- and double thank you for taking submissions from each of the communities so they can try to submit their best shot. That really helps make sure the tests are as ideal as possible.
Java should be the conservative choice for your web framework's language (rather than PHP). Scala, Clojure, Nodejs, Erlang, Lua, and Haskell should be in your list of workable yet "cool" languages (rather than Ruby or Python).
What language should be the conservative choice based on the many factors and ignoring the specific case for the general case?
So you dont even know what you are talking about?
If you're using something like Symfony2, there'll be more abstractions and layers than using something like Silex - but they still don't come close to what Java looks like.
A framework is simply a pre-packaged organization of your code. In the case of the mini php frameworks, they provide little more than a router.
I have worked on many projects - those with and without frameworks. I will choose code on a framework any day of the week.
C++ is welcome here too.
https://github.com/TechEmpower/FrameworkBenchmarks/issues/92
PHP has to live and die upon each request, but Java culls all of its classes together on the first request and from then on it is running from RAM.
Many PHP deployments, and most serious ones, will opcode cache in addition to database and page cache. You could rerun these tests with opcode cache only, leaving page and database caches for your "cache" round of tests. That will at least approximate the dramatic differences in architecture between Java and PHP.
http://www.techempower.com/benchmarks/#section=environment
If this can be improved, please let us know. A pull request would be ideal.
As someone is said to be working on .net benchmarks, you may get your wish.
Also, the "extra cost" doesn't even figure into a benchmark. Grab a 120 day trial ISO from Microsoft and install.
Industry standard network/admin/deployment tools don't work as well, if supported. Until a couple of years ago, the only viable way to manage it was from a GUI... they are still figuring out how to run it headless. It's not always compatible with open standards. Shall I continue?
Meanwhile Unix (and others) began life as time-sharing systems that became the original nodes of the internet, a scalable model that Windows has come back to forty years later. That's not to say that it doesn't have any strengths or isn't improving.
The extra cost includes per minute charges on EC2.
You're spouting off about ancient history...and that somehow translates to Windows is (currently) "clumsy" on the network? Gee, I wonder why it only takes me 10 minutes flat to setup a headless ASP.Net server on EC2? And I can do that even with the GUI version of Windows because there's this little thing called RDP - maybe you've heard of it.
Oh and ASP.Net also runs on Linux. It didn't a few years ago though, so maybe it won't work for you since apparently you are living in the past.
rdp is no substitute for real deployment tools. That you've spent years working around the issues and recently got headless working seven months ago is not impressive. Not when its been mature elsewhere for decades, for free. Nor is your defensive tone.
The original point I made was that there are impediments to using Windows and many still exist whether you believe them or not. If they didn't Windows Server wouldn't be moving closer to the Unix model with every release.
I suspect that the most significant reason has to do with fragmentation in knowledge. Linux is a huge knowledge domain. Windows is also a huge knowledge domain. It's rare to find people (or even teams) that are well versed enough in both to deliver meaningful results on both.
My understanding is that there are ASP.NET community members stepping up to help bring ASP.NET benchmarks to the table for the next round.
I guess it's time I consider switching from CI.
[1] https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...
From our first benchmarks post: http://www.techempower.com/blog/2013/03/28/framework-benchma... "Why include this Gemini framework I've never heard of?" We have included our in-house Java web framework, Gemini, in our tests. We've done so because it's of interest to us. You can consider it a stand-in for any relatively lightweight minimal-locking Java framework. While we're proud of how it performs among the well-established field, this exercise is not about Gemini. We routinely use other frameworks on client projects and we want this data to inform our recommendations for new projects.
If you spotted things that seem off you should contribute to the repo or let them know.
As n1c points out, if there is something wrong with any of the configuration, please submit a Github issue or pull request.
It's important to remember that database connections are a killer in most web apps. This is why a lot of people use connection pooling (in the postgresql world, for example, you have pgbouncer and pgpool). When you can pool and persist connections, you'll have a faster web app (assuming it interfaces with a db, as many of their tests do).
Last time I checked, Django does NOT provide built-in connection pooling, whereas Rails does. This could be part of the reason that Rails outperforms in most of the tests. That says something ... but it would be cool to see both frameworks strutting their stuff on an identical connection scenario.
Also note that the MySQL connection limit for nodejs is set to 256 connections, whereas Rails set to use a pool of 5. Since these tests are hitting the DB, I'd like to see some more consistency with the way connections are setup. Not that 256 connections is better than 5, but if all those connections are open, it's no wonder that an async framework can hammer the DB so much faster than Ruby chugging along on a pool of 5.
In the Node examples, it's important to note the difference between the mysql and mysql-raw tests. And to remember this when comparing to things like Django and Rails which use ORM's like ActiveRecord. In the raw msql test, node is really quick because there is no overhead there for mapping a row to a model.
Also, a lot of the viewers of these benchmarks might just look at the big bar for X framework and think, "fuck it, that's my next project". Sure, go for it. But also remember that the type of app you're building comes into play a lot. These tests do not have any state. That's why these bare metal frameworks like Gemini are doing so well. Node does great here because it's async. But certain apps (like your future project) isn't going to fit into the mold of an async hello world test. I'm not knocking the framework tests here, they're awesome, but just realize this.
One area where Django outperforms Rails is in the realm of JSON serialization. I'm asusming here that Python's native JSON engine is quicker than Ruby's. I learned this with Rails the hard way. With an out-of-the-box Gemfile, you're gonan have a bad time. In Ruby-land there are a few ways to boost your octane here. You start with MultiJSON and then take your pick of the many JSON engines in the world today. MultiJSON being an adaptor that lets developers put the choice of a JSON engine in your hands. Popular ones are yajl-ruby (ruby bindings for yajl, which is C) and oj. If you're doing JRuby, I hear that Jackson is super effing fast so that's nice. I'd love to see the JSON tests with various JSON parsers (at some point these guys are going to get funding for being the benchmark company, having every framework with every possible configuration on display)
Django Protip: read up on select_related and prefetch_related. Similarly, in Rails land, you'll want to learn about eager loading with things like includes() and joins(). This will let you do one or few queries up front, rather than doing a bunch of subqueries inside of a big loop. One big query is usually better than a billion tiny ones.
A reason why the PHP apps are getting murdered is because in a typical configuration your application initialized on every single request. Opcode caching can help here since that reduces that boot period. This why in a production environment you can modify a PHP file and instantly see the results. Versus (depending on the application) a Ruby or Python app, where the app is booted initially and until that worker or process is restarted, is running the code it started out with. This is for speed. This is also why, for example, a big Rails project (or better yet, a Java project) has a significantly longer warm-up period. It's loading the entire app. This is a big reason the PHP frameworks are suffering here, since they have a lot of overhead to load for each request. Whereas the raw PHP doesn't have that overhead.
PHP protip: Roll with opcode caching (APC is a favorite of mine, it's very simple to install and configure, and provides a handy web gui for viewing various details of things like cache hit so you can optimize it). Also, please throw apache and mod_php away if you can and run with Nginx + php_fpm ... although if you're an HN reader then you're probably already doing this.
The JVM is balls-quick because, again, unlike something like PHP, it gets booted up and then does it's thing. The JVM is infamous for it's slow startup process (which really varies based on a billion things) but there is a reason for that: the JIT. The slow startup is due to bytecode generation, I believe. It churns that bytecode into machine code and during that process it can make optimizations like unrolling loops. I'm no Java pro though. I don't like working with the language (the verbosity kills me, and XML is painful), but I have an immense deal of respect for its performance.
To be honest I am really going to take a solid look at the Play framework for my next side project. It's got a lot of the pragmatism of Rails but it runs on the JVM and comes in a Scala variant. These are things that I am stoked about dipping my toes into.
Finally, my hat is off to the folks running these. Great work!
Indeed, database connections are a huge pain point for web applications. In our tests, Django does not yet use a connection pool. We hope that is addressed soon either (a) by usage of a MySQL connection pool or (b) by implementation of a Postgres suite of tests, which are planned. We don't have an ETA on either of those, but option (a) could be completed by a pull request if anyone has time.
We've attempted to be consistent with database connection pooling. The Rails test runs in production mode and uses 256 connections in its pool [1].
Gemini is our in-house closed-source framework. We have been debating internally whether we are comfortable open sourcing it. It is not documented sufficiently for third-party use and we do not have the capacity to support its use by third-parties. On the other hand, we believe open sourcing it is the fair thing to do because we have included it here and other frameworks' fans should be able to examine it. We're still hemming and hawing over that. All that said, it is not a bare-metal framework [2].
My very strong opinion is that Node is not doing well because of its support for asynchronous application design, but rather because the V8 runtime that Node executes on is simply fast. I say that because the client-side concurrency is already sufficient to saturate the CPU cores using most frameworks (the exceptions being those that appear to have some lock or resource contention preventing full CPU utilization). Server-side concurrency fan-out strategies such as asynchronous evented loops may help superficially for our database tests, but pale in comparison to simply having a fast platform. I've written my opinions on that matter on my personal blog [3] (it's a later section of that blog entry).
None of the JSON tests are asynchronous. All respond immediately within the handler code.
I agree that it would be still more valuable to have additional dimensions for attributes such as the particular JSON serializer, the particular ORM, the particular database driver. With enough time, I'd love to have that breadth. The project has been steadily growing in scope, but handling that level of minor variations I fear remains quite a ways out. The priority right now is introducing new tests that include more representative functionality such as working with collections and server-side templates [4].
I'm not sure if your comment about PHP opcode caching is meant to suggest that we are not using it, but based on feedback prior to Round 2, we enabled APC for PHP, and that has remained enabled in this round [5].
I am not a part of the Play project, but I agree with your sentiment that Play deserves a good look by anyone starting a new web application. I've been especially impressed by their community. They have embraced this benchmark project and are helping us make sure Play performs as well as possible--with a fair and reasonable production-grade configuration. Their fans have given us some good advice and constructive criticism.
Finally, thanks so much for the kind words at the end of your comment. It means a lot to us that the community finds value in this project. We've been thrilled with the response. On the other hand, we've been absolutely murdered by the moderators here. This item had a massive HN Slapdown score, and we weren't even responsible for posting it. :)
[1] https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...
[2] https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...
[3] http://tiamat.tsotech.com/rps-vs-connections
[4] https://github.com/TechEmpower/FrameworkBenchmarks/issues/13...
[5] http://www.techempower.com/benchmarks/#section=environment
I guess it was about sessions and auth hitting the db?