I've actually written a highly optimized asyncio-based Python web server in the past. I meticulously optimized every component, using all the standard CPython optimization techniques (heavy use of stdlib, minimizing method lookups).
In the end, my implementation was nowhere near nginx. I even ran it under PyPy and it fared no better. Then I realized the oxymoronic nature of writing an optimized web server in Python.
If you wanted to run in PyPy, you should've written your code to be PyPy-friendly from the beginning. Standard CPython optimizations don't usually fare well in PyPy, and you can end up with even slower speed if you follow those. Still, you didn't mention profiling the hotspots of your server, nor using Cython to optimize them.
I don't see how writing an optimzed web server in Python becomes oxymoronic -- it can still be the fastest performing python server, and have a valid use case for those that want to work in Python.
Your PyPy point is fair. A PyPy-oriented optimization effort could have improved performance. N/s by what magnitude though.
I did extensive benchmarking until there were no more hot spots, didn't help. Dropping into C or Cython was a non-goal. For dev an embedded Python web server is convenient but it doesn't have to be fast. When it comes to performance, it always makes more sense to use a native-code web server in production.
I mentioned profiling and benchmarking because they were missing. Totally agree with you, Python implemented servers are usually used in dev ops, and then you move to a "standard" http-server (be that nginx, apache, whatever).
I still think that a Python server can have it's use, and there's nothing wrong in trying to make it as fast as posible.
What does "not even close" mean in this context? I find it a bit interesting that the most obvious conclusion from the benchmarks in the story, and the linked one - is that uvloop and go are high-performance and consistent low-latency, high performance python is on par with nodejs - but in general the real jump up is towards go and c++(?).
It's great to see an order of magnitude leap on the python side - but on the face of it I'm not sure the leap really is enough to enable a different class of services in python? Perhaps I'm being too pessimistic - I know I'd be happy to be able to "ignore" node, and only consider eg: Python for most things and go for some things. Just to limit my tech stack.
But where does nginx with lua fit - is it another order of magnitude above go for dynamic content?
Performance is not a magical inherent property of a language-and-webserver combination, and will vary wildly with the application. In my experience practical performance has much more to do with architecture, algorithms and "the other bunch that makes things efficient" -- not so much with a language.
It's also only one facet. And usually not a very important one, either.
> Performance is not a magical inherent property of a language
well it is. however most performance characteristics of a language are well understood.
mostly python/ruby is slower than a lot of other languages.
> practical performance has much more to do with architecture
well sort of not every case does well with these kinds of languages.
however in most cases it's just fine to use them.
my company changed one product from python to scala. everything was slower in python, however in 90% of our use case that didn't even matter. however we were thread and calculation (i.e. shuffling/changing large lists/maps in memory) where python was just slow. I guess we could've written a library for these kind of transformations in C. however another problem was also PDF generation, which was really really slow in python (for bigger pdf's, slower one's we just fine).
we are happy to use scala, but I didn't found python bad or weak. you are pretty fast and the tooling is just amazing.
also the ORM's in python are superior to everything i've seen on the JVM world. (Django ORM and SQLAlchemy) I guess they are even the best ORM's out there.
if I would be developing more towards a cloud architecture I would probably use python again. you could just do more if you have room for a "infinite" amount of servers
still python is a really great and well designed language. I would everybody encourage to look into it.
If you wanted to create a static files server, Python is just a bad choice.
Python is a good choice if you need custom, complex logic or access to non-file data stores. And if you need that, Nginx isn't a choice (yes you can use modules -- eventually you recreate the same problem).
So yes, don't try to replace Ngnix or HAProxy or PostgreSQL or such with Python. But the gaps not covered are good to be written in Python. And it's nice if they're fast.
> unless you explicitly use something optional like Lua
I've actually at one point considered writing some light web apps as nginx modules, in Lua. In the end though, it's usually enough to pluck the lowest hanging fruit, which (as a rule is) caching... Cache as much as possible (including when you have a python web app behind nginx).
Really excited about these efforts. Python is my main programming language, but for high volume endpoints it's sometimes necessary to switch to other languages. In an ideal world I'd just use Python for everything. It will take a while before that's possible though. These performance gains don't mean much if your DB connection is still blocking.
How does your benchmark compare against GO, Elixir and Node?
Extrapolating from Techempower and from the OP's benchmarks, I got:
137% as many req/sec as Node
83% as many req/sec as Go
I'm it sure where this goes when you factor in async IO, but my guess is that Node gains a little and Go loses a lot.
When you add asyncpg to the mix my money is on Sanic winning by a long shot, in a benchmark that includes e.g., 3 queries and returning the results as JSON.
It's interesting how so many of these microframework+app server type things spread across many languages have converged on a very, very, similar set of conventions and practices. The example code looks strikingly like every other recent JavaScript/Perl/Python/Ruby microframework+server that I've tinkered with lately (Express, Koa, and Hapi in JS, Mojolicious in Perl, and Flask in Python). Aside from the obvious language differences, the concepts are all so similar.
I wonder if this is a convergence on a local maximum for writing web apps, or if it's just a situation where the most popular ones did it this way, so everyone does it this way. Not that it matters too much, in the general case. The route and app setup plumbing is a small part of most applications, so it's rarely going to be ruinous if you spend an extra hour or two getting it working right. But, since I've been evaluating a bunch of different ways to build web app backends lately, I've just noticed how similar they all are, no matter what language you choose. Most of the ones I've been looking at are asynchronous, so this also has that similarity to most of the others.
While one could argue that it's obvious that they would look the same...but, web development didn't always look this way. The first 10-15 years of web application development I did looked very different, in fact (CGI or mod_perl or PHP, which has some quite different conventions).
I've had this blog post in my head for a couple years now that some day I'll get around to writing. It's titled "Toward a Common API", and it's basically about this - the convergence of best practices, naming conventions, patterns, etc across languages and frameworks that (IMHO) is a wonderful thing in that it reduces ramp up time when coming to something new.
It came to me after playing with Rails for several years and then taking a look at Laravel, which is clearly Rails inspired in many ways but made some different decisions in the naming of a few things that are present in both frameworks.
Ah, yes, global minimum does fit better as a descriptor.
> you're observing what is near the minimum API required for mapping a web request to a single function.
I mean, sure, it is functionality needs to be there across the board...but, it could be named differently or used differently. An "app" object is seemingly universal everywhere, a run method is super common; router configuration is very similar across several frameworks, though in this and Flask it is a decorator while it's a method in Express or Mojolicious, but still look a lot alike.
Anyway, I just think it's nice. It means that when I poke around in projects I've never seen before in frameworks I'm not familiar with, it isn't terribly difficult to get up to speed on at least the basics of what it's doing and how to change minor things about it. Building a big project from scratch still requires some time to get up to speed, but I think it's likely to provide developers more mobility across projects and companies, which is a good thing. Standards are nice, even when they're de facto and just came about because "everybody's doing it".
I haven't had a chance to tinker with the async elements of this, but I'm betting it's similarly transferable knowledge across all of the frameworks that are async.
Anyway, none of this is really an amazing revelation, I guess for people who've been working on modern web apps for a while. But, I'm catching up from doing it in a variety of old ways (for examples of doing it very different ways, just take a look at some Drupal or WordPress or Mediawiki or old school Perl web application code...these projects are still very heavily used and have thousands of developers working on them, and they look literally nothing like the model we're talking about, and often don't really even look that much like each other). My point is that there's been convergence, and it really hasn't always been obvious how it should be done. Knowledge of Drupal, WordPress, or Mediawiki, does not readily translate to anything else. I have to get back up to speed every time I dive into any of them (and I still do have to do so now and then). There are tons of projects that are like that.
Also, interop is getting a lot better. I don't feel like it would be crazy to stick a Mojolicious app alongside an Express app, working with the same data sources and even talking to the same web/mobile frontends, for example. Making those old projects interact nicely with others is often an exercise in frustration.
Just to clarify, Koa can be considered an evolution of Express. It's not "based on Express" per se - there's no dependency there. It has been written by authors of Express - in that sense, conceptually, "it's based" on good ideas behind Express.
This is a great project. I am going to be watching this!
One question: Why default to ujson? I understand that if you're always dealing with small, simple json objects it's quite fast and safe. But with larger json payloads, its performance and compatibility start to break down. I haven't touched it in the last 8 months, but I had to switch to Python's built-in json module (which is quite fast in 3.5) for compatibility when serializing and deserializing large json objects.
Curious to see how this fares against something like uWSGI with aiohttp support (haven't tried either, but uWSGI brings so much more to the table than speed that I'd like to be able to make an informed choice).
Running asyncio-based servers on uwsgi is very stable though, but may not achieve 100 % of the performance native asyncio in uWSGI may have. Even then, Tornado is "fast enough" for us (ie. we run out of network bandwidth before we run out of CPU).
But the main advantage - that you're still using uwsgi as a "unified application server" of sorts - persists, which was the main point for us (Django, Flask, Tornado on uWSGI). This simplified deployment and administration quite a bit.
Re. topic: It would be really interesting to see it compared to Tornado.
Yeah, I use uWSGI for a lot of things (check out http://github.com/rcarmo/piku, for instance), so am more interested in figuring out if I can get a little extra mileage off it.
I noticed that the past several Python-related threads on HN haven't contained the usual cynical comments against adopting Python 3. Are we finally at a point where Python 3 has been accepted as the gold standard?
Yes. I think the async syntax in 3.5 was the tipping point for the stragglers. It finally looked like a serious upgrade, not just a bigger number with a load of things to fix.
I know there are people who are stuck on old versions, but the community has recognised how much damage this dispute was doing to Python's image. They know they'll have official Cpython support to 2020, so it's a non-issue until then.
I've mostly heard this from people who don't use/know Python in the last ~2 years. Imho this has been a trope for some time:
PHP is such a bad language hue hue
Java. AbstractCommentPostingRequestSerializerInterface hue hue
Python. You know Python 3 right? hue hue hue
command.exe windows suxxx hue hue
... countless others with varying stupidity
By my gut feeling this happened this summer. I haven't found any project which wasn't available in Python 3 in the last three months and therefore stumbled upon some which don't support 2 anymore. Also newer answers on SO now usually start with the version for 3 and might have an appendix "if you are still on ...".
Looks like a nice step up from the raw aiohttp I've been writing. Would be interesting to see this wrapped with a forking module. That would provide an nginx-style deployment--select the number of actual threads to run and then run async within each thread.
The hard dependency on uvloop looks weird. It's just a pluggable loop for asyncio, anyone can switch to it in two lines of code, no reason to depend on something that's a native extension.
> Also why not just improve aiohttp's performance…
Have you read aiohttp's code? there have been some discussions about improving its performance, there is basically no immediate bottleneck, the whole thing just performs poorly. I hate to say it but sometimes it's just better to start from scratch.
This is awesome, Python is my number one, Node.js already has much larger ecosystem for async i/o. If we need something faster for heavy computations Go is a good choice.
But maybe in near future Python will grow significantly, who knows.
I think it's fine to use an external library such as Faye for websockets. That comes with the benefit of having many client libraries available. (JS, iOS, Android)
When did the principal benefit of Websockets (send and receive whatever instead of HTTP primitives) become a matter of client and server library availability?
I want to trace this profound moment in WebDev history. Amazing.
The principle of sending whatever is important, but in reality most modern consumption literally follows the ethos of 'go fast'. Perhaps dropping Websockets helps out; it's a freedom of choice to pick speed over compatibility.
You almost never want to use average as your metric when dealing with time. For time, the 95 or 99 percentile latency is going to be measure of how many requests out of a hundred a less than your metric. Esp when the thing you are measuring is a piece of larger system, which is the crux, because of how a resource in turn requests k-more resources. Each request has an equal chance of being over the latency percentile. Even at 10 requests, only 90% of sessions will be under the 99% latency.
I honestly don't understand why you would do this when nginx with Luajit is so fast. I think Python is great but nginx is so robust, widely used and Luajit is so impressively quick.
Probably because even though Lua is super a nice language, writing code in a config file is not that much fun? App servers spitting out HTML written in the language of your choice are so much more flexible than developing on a single platform/stack such as Apache + mod_php|mod_perl, or OpenResty. Of course nothing stops you to proxy them through Nginx and serve the static content directly with Nginx in production.
In the end, my implementation was nowhere near nginx. I even ran it under PyPy and it fared no better. Then I realized the oxymoronic nature of writing an optimized web server in Python.