The typical response to these types of posts is "oh your /toy/ server doesn't account for x, y, z in my use case, like ddos, network issues, etc. But how many people actually handle those cases in your production application? I can say for the majority of the applications I've written at large companies handling significant traffic API compatibility was far higher on the priority list than the cases that people often…
Old box, dumb code, few thousand connections, no big deal
251–260 of 288 posts
Re: Old box, dumb code, few thousand connections, no big deal
#252Earlier quoted context omitted.
If you get stuck in read(), you can't do neat things like waking up when it's time to kick a client for being idle, doing other housekeeping, or cleanly shutting down the whole thing in a timely fashion. When I ^C the server, it sends the same wake condvar-poke but it twiddles the flags so the worker shuts down instead.
> If you get stuck in read(), you can't do neat things like waking up when it's time to kick a client for being idle Totally possible with another thread acting as watchdog timer and sending a signal which causes the read to return with EINTR which can then check a flag whether it should retry or abort. And that's for file IO. For socket IO you can just set it to non-blocking.
An alternative to putting the watchdog timer in another thread is to use alarm(2) and use the kernel's built-in watchdog timer, and the default behavior for SIGALRM is probably adequate. This might be easier than non-blocking I/O.
Re: Old box, dumb code, few thousand connections, no big deal
#253Earlier quoted context omitted.
I'm glad someone else sees this the way I do. What the blog writer did was tinker with something. They didn't engineer it. I was a mechanical engineer prior to switching to software. As a general rule, the things we do in software are very distant from engineering.
I think the analogy to mechanical engineering is something like this: A seasoned engineer notices that everyone is only building suspension bridges all of a sudden. They point out that you can span a stream with some bricks or rocks and a bit of mortar, and are ridiculed. Next, they build a highway overpass out of concrete pylons, and stress test it to 10x the necessary engineering load, and point out it cost 10% as…
Re: Old box, dumb code, few thousand connections, no big deal
#254Earlier quoted context omitted.
The only reason to use Python for anything more than few hundreds lines worth of utility is if you're working on a codebase that's already in Python, and even then it's debatable. There simply isn't an excuse for using Python for any infrastructure. It does nothing particularly well - or even right - other than very purpose-specific scripting. It can tie things together well enough. And your codebase becomes a liabil…
So you always get downvoted to hell whenever you post this and you've decided everyone else is the problem? Can you even understand how you sound? I cringed with sympathetic embarrassment just from from reading this. Seriously, rethink your life choices man. This coming from someone who has literally never written a line of python in his life.
People expressing unpopular opinions and defending them with evidence is how we find out when the popular opinion is wrong, and how, when our discourse is functioning properly, we can gradually change the popular opinion to be less wrong. You, and the people downvoting the comment, are throwing a monkey wrench in those works.
Re: Old box, dumb code, few thousand connections, no big deal
#255Earlier quoted context omitted.
Which I read as "I have no experience whatsoever with modern python, and async is something that catches water". Async is typically less prone to error and complexity than thread code, and also typically faster/lighter for io (in python). I can't see a reason for this not to be the case in other languages too.
Note: these opinions are mine In terms of error-proneness, I would say the hierarchy is this: Actors In terms of "getting started" difficulty, I would put the order like this: Async In terms of first-order maintainability in large projects I would put the order like this: Actors >> CSP ~ Threads > Async Async is on its face easy to grok, and saves you a ton of problems with locking and the like, and you can run it on…
I can't even say the same for synchronous Django. Sometimes it's just the quality of the tool you're using, not the higher-level concept it implements.
Re: Old box, dumb code, few thousand connections, no big deal
#256Whether intended or not, there's an undercurrent of "you're all so dumb for using Python" (or Ruby, or PHP, or other similarly performant language) here. I want to surface that and question it a bit. It's totally reasonable for a company to choose the Python/Gunicorn option if they already have a bunch of people who know Python and they don't need to serve tons of requests per second. Even if they do need to serve to…
The only reason to use Python for anything more than few hundreds lines worth of utility is if you're working on a codebase that's already in Python, and even then it's debatable. There simply isn't an excuse for using Python for any infrastructure. It does nothing particularly well - or even right - other than very purpose-specific scripting. It can tie things together well enough. And your codebase becomes a liabil…
— ⁂ —
In 2000 or 2005 Python was a simple, consistent, practical language with a policy of strict error handling that was very useful for producing reliable code: "Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it." Its runtime cost was significant but bearable, about a factor of 20–40: if you wrote your code in C instead of Python, it would run 20 to 40 times faster, and that was all the machine could do.
In 2020 Python is an overcomplicated, inconsistent, slow, unreliable language with a persistent schism resulting from the core developers' poor choice to make backwards-incompatible changes to simplify the language. It has metaclasses, superclass method resolution order linearization to enable mixins (two different ones, in Python 2), a lazy sequence construct that has been gradually Frankensteined into a general coroutine construct (with an additional lazy sequence construct added on top), two different incompatible language constructs to compensate for the lack of block arguments or full-fledged lambdas (decorators and context managers — I'm excluding generators here since they're more powerful than block arguments), and on and on. The reference documentation for "import" alone is 20 pages, and that's in Python 3, the simplified version of Python.
Python's performance cost has not increased in absolute terms — in fact, it's even improved a bit — but it's increasingly painful. In 2000 we could rest easy knowing that whatever we wrote in Python would be sped up by Moore's Law and Dennard scaling, roughly a doubling in speed every 18 months, so in three years it would be four times as fast, and in three more years it would be 16 times as fast. That, together with a little judicious implementation of inner loops in C, was a small price to pay for getting things done sooner and not having to open core files in a debugger.
But then Dennard scaling slammed into a wall around 2006 and Moore's Law sank into a swamp around 2016. Meanwhile, manycore meant that without multithreading, or at least multiprocessing, your program suffered an additional order of magnitude slowdown. Even US$40 hand computers now feature quad-core CPUs. Today, the gap between what the machine can do in absolute terms and what it can do when saddled with Python is a gap of 1000 or 10,000, not 20. If you can cope with the limitations of PyPy (it supports Numpy now! Since 2017) then you can get up to the speed of single-threaded C, which is about 3% of what your computer is capable of. But it's not going to get faster just because hardware progressed: computers will maybe be twice as fast in five years, at best, and maybe not. If it's too slow today, it'll probably be too slow then too.
But that's not the worst part. Python's completely botched Unicode handling introduces bugs into most Python programs that handle strings from the outside world, latent bugs that only surface once those strings contain non-ASCII characters — similar to the situation with bash scripts and filenames containing spaces, although that can be detected by purely local analysis (missing doublequotes around a $var, red alert!). Plan 9 had already demonstrated one correct way to handle the situation (the one used in Golang and Rust) and Markus Kuhn's UTF-8B proposed another, one which was eventually partially implemented in Python as PEP 383 ("surrogateescape") but turned off by default. I've had bugs in on-orbit satellite control software that I couldn't track down because Python generated a UnicodeDecodeError when it tried to log the stack trace.
— ⁂ —
At the same time, other alternatives got a lot better. Java grew into a mildly reasonable language, and Kotlin and Clojure are outstanding ones. Haskell, defying everyone's expectations, became practical. Microsoft started trying to embrace and extend free software, so now we have F# on Mono, which is almost OCaml — almost as convenient and concise as Python, but enormously less bug-prone. Mike Pall, a superhuman intelligence from the future, wrote LuaJIT, which gives you performance on par with C in a language as friendly as Python — not modern overcomplicated Python, old Computer Programming For Everybody Python. 100 million people, including little kids, program computer games in Roblox using Lua. (It's bug-prone as hell, though. Lua has a footgun for each toe, as Sean Palmer says.)
Even C++ has been tamed somewhat. And of course we have Rust and Golang. Golang is only a little bit uglier to program in than Python, and both of these new systems-programming languages make it a lot easier to take advantage of manycore, though not SIMD.
Switching from Python to Golang is as easy as switching from Perl to Python, but with a lot more benefits. And that's a big reason why a lot of the important infrastructure software written over the last decade has been written in Golang.
On the horizon, we have things like arcfide's Co-dfns APL compiler, Matt Pharr's ISPC, and GLSL to show us how massively parallel programming, including SIMD, can become accessible to mere mortals. They aren't yet practical options as alternatives to Python (except that GLSL is practical for its original purpose, of making nice graphics), but they might be pointing the way to something that is.
— ⁂ —
So I think it's eminently defensible that Python should now be consigned to "a few hundred[] lines worth of utility". Python is great for scripting TensorFlow, and it's a far superior substitute for MATLAB. But writing infrastructure in Python in 2020 is like writing infrastructure in Perl in 2005.
...which I was also doing. I think I probably owe an apology to a lot of folks at Aruba Networks who are maintaining that code today.
Re: Old box, dumb code, few thousand connections, no big deal
#257Earlier quoted context omitted.
1) .NET APIs change very frequently. 2) C# is a very verbose language, that requires a lot of typing. 3) F#, the best language in .NET, is largely ignored by the .NET community.
C# doesn't require all that much typing if you're using Visual Studio, and even less if you have Resharper.
Re: Old box, dumb code, few thousand connections, no big deal
#258Earlier quoted context omitted.
They got aquired in 2014. They hit 400M users in 2013. By then it was already THE messaging app for several European countries. I know amount of users doesn't equal users/second. But we shouldn't pretend like they weren't able to handle high traffic before Facebook aquired them.
400M in 2013 globally and millions per second does not equal. A simple math says 4x10^8 / 24 / 60 / 60 = ~4.7k users / second. Kind of 3 orders of magnitude lower. Even if you say most users are not spread evenly over those 24 meridians due to Earth being mostly water it will still not get to those millions per second.
Re: Old box, dumb code, few thousand connections, no big deal
#259Earlier quoted context omitted.
> single-threaded nonvectorized C wastes on the order of 97% of your computer's computational power Can you elaborate on what this means exactly? For example, is there some reasonable C code that runs 33 times slower than some other ideal code? In what sense are we wasting 97% of our computer's computational power?
8 cores times 4 SIMD lanes is a 32× speedup; that's where "97%" comes from, as explained in the note I linked to. It's pretty variable: some things we haven't figured out how to speed up with SIMD, sometimes we have a GPU, sometimes we can get 8 or 16 SIMD lanes out of SSE3 or AVX128 or 32 of them out of AVX256, sometimes you only have four cores, sometimes make -j is enough parallelism to win you back the 8× factor…
It's also not reasonable to expect to vectorize everything; for example a web server is unlikely to benefit from vectorization.
Re: Old box, dumb code, few thousand connections, no big deal
#260Earlier quoted context omitted.
Gunicorn is just a WSGI server, basically used to spawn a pool of webserver processes for your python backend. A python webserver is more optimal when used in multiprocess configuration (as opposed to multithreaded configuration, where python sorely suck at), and gunicorn will do that for you automatically, routing each http request to available worker process in the pool.
> Gunicorn is just a WSGI server, basically used to spawn a pool of webserver processes for your python backend. Or a pool of asynchronous "green threads" using any of a number of libraries (gevent is the one I'm most familiar with). The thing to avoid is mixing the two (multiple processes and green threads). > A python webserver is more optimal when used in multiprocess configuration For CPU bound worker tasks, yes,…