Why Python Is Slow: Looking Under the Hood (2014)
jakevdp.github.io
jakevdp.github.io
The following may also be of interest:
- Why Python is Slow: Looking Under the Hood [0]
- Fast Python, Slow Python by Alex Gaynor [1]
[0] https://jakevdp.github.io/blog/2014/05/09/why-python-is-slow....
* the reference implementation is maintained by a team of contributors, very few people are paid full time to work on cpython. State of the art JIT is hard to come by when you can only afford working a few hours / week.
* contrary to JS, python had a huge set of packages available for a very long time, making "minor" language breaks unworkable.
* also contrary to JS, python had from the start a strong story to interoperate w/ the C ABI, which tightened a large subset of python libraries with the details of cpython implementation. Because of its "niche", JS never really had that problem. If you remove libraries w/ large C extensions, you remove a lot of what make python popular today (e.g. scientific usage is now the 2nd largest area of application of python after web development, before automation, testing, etc... which used to be where python shined).
I'm not considering Python's C extensions there. I don't know if you'd consider that part of the 'language' or 'ecosystem'. Pyston is trying to make Python fast, including the C extensions, which is not as easy.
So the number of features doesn't matter, only whether or not they are all possible to implement efficiently, and the lack of a standard doesn't matter, as with infinite money you could test comprehensively.
If there are just more difficult-to-implement features that is interesting as well (although it would be nice to know which ones).
That's what people mean by a 'standard'. Something at least trying to be somewhat semi-formal. If you mean that the implementation of CPython is a standard, well then I suppose that's arguable, but it's really not what anyone means.
I'm not sure why Armin is so categorical, but perhaps one way to rephrase what he said is that ES6 has had more opportunity to flesh out the spec/edge cases than Python, due to Javascript's popularity and healthy competition between implementations.
The CPython crowd apparently made some change between CPython 3.4 and CPython 3.5 which changed how Windows DLLs are called. This broke Py2exe, which no longer works for the current release of CPython. [1]
CPython's approach to C extensions usually requires having the same Microsoft C compiler version used to build Python. This is now a problem for Python 2.7, because the Microsoft compiler version used to build it is no longer available. Microsoft recently changed their API for the C runtime, and CPython has been changed accordingly.
CPython's "C interoperability story" isn't really that good on Windows.
There is a CMake build system for 2.7 that works great with VS2013-2015:
https://github.com/python-cmake-buildsystem/python-cmake-bui...
Note that the problem you mentioned w/ python 2.7 is gone: MS has made a .msi available free of charge to build C extensions for 2.7, indefinitely (thanks to Steve Dower): https://www.microsoft.com/en-gb/download/details.aspx?id=442...
When that is not enough, you can still recompile manually if need be, so long as source for the module is available (which it is in most cases).
On the other hand, native modules are such a big part of the Python ecosystem, that not supporting the API and ABI at all confines any alternative implementations to a very small niche.
This is less problematic on other OSes where GCC dominates and clang is compatible, but it can bite on HPC systems that use PGI compilers.
But on the web there is a point. Unless you can use HTML alone, you have to use JavaScript -- and you can't just plug in a module that you wrote in C.
But you can not buy time.
Developing python applications in many cases is much quicker than developing comparable applications in C++, C or Assembler. Therefore it makes a lot of sense for many companies to choose tools with quick development time to get the product running and care for performance later.
Since there may be so much code be written in python, it cannot simply be converted into a faster language since most critical parts are already accelerated using the proper libraries provided by the language ecosystem. Porting a complex application to another language in many cases is a really hard problem and might even not be possible to be done in smaller steps.
Therefore we need every bit of performance we can squeeze out of python. The whole world will gain from that. If we manage great strides like JavaScript it would be glorious!
Go's biggest problem is a lack of generics, which means that in some cases, you have to fall back to duck typing. So the worst case is no worse than Python's best case.
The next-step isn't Go. It's Elixir. Or Rust if you need a systems language.
This is especially true in the last 5 years, which contrary to your post has seen a shift away from multicore task-parallel hardware (core counts are not increasing) and toward data-parallel hardware (wider SIMD lanes, better on-board GPUs, APU architectures, etc.)
For me, explicit pthreading should never be done except with systems software. In which case you're going to be using a systems language like C, C++ or Rust anyway. Not the oddball middletier stuff that isn't high or low level like Go, Java and C#.
> In the case of an I/O bound Web server, I don't think that Go is going to appreciably result in "leaving money on the table". Python releases the GIL on I/O, which is where your time is largely spent.
GIL doesn't matter, the OP said he was writing single threaded application. This means he's doing async (unlikely) or blocking the entire application process (not just the current request) on I/O.
> Linux is very fast at spawning threads. When you're doing I/O, it doesn't matter.
Not as fast as goroutines, and spawn speed isn't the only (or even the most interesting) measure of efficiency--you can run several orders of magnitude more goroutines on a Linux box than you can threads. Of course, none of this is relevant to this conversation, since the OP is talking about running a single-threaded Python process per core.
> Go's concurrency is not "fundamentally different" from any of the other languages. It's just an implementation of thread-per-connection in userspace. This is an implementation detail, and for a typical Web server I think it doesn't matter much.
You're conflating Go's concurrency model with its webserver implementation. Go's concurrency model (movable M:N coroutines with implicit yielding and a synchronous abstraction over async IO) is fundamentally different than the other approaches (threads and vanilla coroutines with some blend of sync and unabstracted async I/O).
Since you mentioned thread-per-connection, Go's "threads" are much lighter than even Linux threads, so you can service many more simultaneous connections (by many orders of magnitude in extreme cases). This is especially true if you're not using async I/O, and blocking I/O is the default for most of these other languages.
> this is more efficient than spinning up an OS thread per request
Linux is very fast at spawning threads. When you're doing I/O, it doesn't matter.
Go's concurrency is not "fundamentally different" from any of the other languages. It's just an implementation of thread-per-connection in userspace. This is an implementation detail, and for a typical Web server I think it doesn't matter much.
Everything leaves some performance on the table. Everything also leaves some productivity on the table too.
Python is in my estimation 20-50% more productive as a language than Go. I run my web stuff on PyPy/gunicorn/Nginx which within reason, can't be leaving much to be wanted performance-wise.
I've given Go a good-go, but I haven't found anything that strikes that balance of ~25% more productive than Go, but still grant similar performance like PyPy does for me.
Python is slow because it is dynamically typed rather than statically typed? No. In most cases, type inference is good enough. See Javascript. Python is slow because it is interpreted rather than compiled? That is not really a limit in the long run. Insert a JIT compiler. Java was interpreted as well initially.
Python's object model can lead to inefficient memory access? No we are getting somewhere. Scripting languages in general tend to overuse hashmaps. That gives you a lot of flexibility and monkey patching. It is also a lot harder to optimize than dynamic dispatch.
Another problem of scripting languages is the inability to control your memory. Python forces you to make a lot of memory allocations and there is little control over the garbage collector. Even Java suffers from this, but the JVM engineers invested a lot to fix it. There are no value record types (struct) in Java.
It now matches how the .Net CLR handles unicode. Which is a shame since Linux is kind of increasingly popular.
There are some recent developments where Microsoft is investing through the core dev surrogates into Python3 and speed with Pyjion. That is promising but it's a framework where it requires CPython3 to use a JIT, which strikes me as a way to enforce control- a response to PyPy and alternatives taking the mantle when they abdicated the throne with Python3. But it remains to be seen how it works out, and PyPy already is likely to remain the leader on faster-Python.
To understand the appeal of dynamic languages, it's critical to consider the era they came from. Your statement is not true of the general programming landscape ten years ago. It's not really true of the general programming landscape today, for that matter, which is still by sheer mass C++ and Java. But 10 years ago, there weren't hardly any choices of statically-typed languages that weren't a bit of a bear to use. 1990s C++ is a monstrous nightmare. A lot of this monstrous nightmare was arguably the programming environment (Windows, Xwindows, UNIX, everything was a bear to program in back then), but when you took battle-scarred C++ Windows programmers and showed them
print "Hello world!"
they weren't in a mood to analyze exactly which differences between Python/Perl and C++ were essential and which were accidental; they were too busy drinking in the joy.I think there's a general trend towards languages that either make the static typing easy enough to use that you don't feel like you're losing out on much, or make the static typing pay for itself better (Rust), and I think that will continue to be the story for the next ten years on the developing language front. I think it may be the case that if they weren't already entrenched, what I call the 1990s-style dynamic languages (because it's not just that they're "dynamic", Perl, Python, Javascript, and Ruby are almost the same language, compare with the "dynamic" Erlang to see a major difference) wouldn't succeed if someone wrote one for the first time this year. In fact they'd probably take a beating for the performance issues it would have. But they are entrenched and have over a decade of refined libraries, support documentation, tutorials, and of course, the big hitter, existing code. So they aren't going anywhere any time soon.
The other thing is tooling. There are lots of hardcore developers to whom even syntax highlighting is too much distraction, let alone error checking, auto completion, etc.
Well... To say the least I am not one of them. In fact to me, a large part of the simplicity of a language resides in the interaction with the IDE, the feedback it gives you on what's legal and what is not, where you may have a bug or an ambiguity, what can I do from there, etc. Dynamic languages defeat advanced and proactive tooling. And to me it makes them actually quite hard to work with, not self-discoverable, and all-in slower to develop. The time not spent writing a class or a return type ends up being spent on the reference page of the library (after you checked this is the correct version) checking what are the arguments of a function, etc.
First, there aren't really any compiled languages where the syntax is similar in ease of use to Python (or any other popular dynamic language I'm familiar with).
All the compiled languages with which I'm familiar require a significant amount more work to accomplish the same thing; C apps are full of boilerplate to detect error cases, allocate memory, check buffer lengths, etc.; dynamic languages handle this for you so the total amount of code tends to be lower.
Writing a simple program to test an algorithm or proof of concept is generally significantly less code, the code/test/debug loop tends to be significantly shorter, and you can often get to a solution significantly quicker while avoiding the typical bugs that come with lower-level languages.
Python also has a pretty substantial standard library which is easy to learn and use and very consistent, meaning much less copypasta code or external dependencies.
On top of that, a great deal of use-cases for languages like Python aren't CPU-bound, but rather I/O bound (disk or network). In cases where your app is CPU-dependent, it can often be trivial to replace that inner loop with optimized native code written in C or other languages, while still keeping the rest of your application easy to read and maintain.
Lastly, for complex algorithmic work, there's a lot of packages which implement standard data structures and algorithms in native code and provide usable, idiomatic interfaces in Python, which means that for a lot of things you can work in native code with the ease of Python, at a minuscule performance hit but a dramatic increase in programming efficiency.
Now if you are not doing something that requires performance, I think there is still a good case for static typing: advanced tooling. Dynamic language limit a lot the ability of the IDE to help you finding bug, making the language and the API self discoverable, auto completing the code, providing advanced analysis (who calls what) or refactoring (rename a property). Some people do not need that, but to those who rely on that, dynamic languages must really be easier to write by an order of magnitude to end up being faster to prototype.
If I was suggesting a language to a beginner, I would push for a language with a strong IDE.
With a dynamic or duck type system, though, you can avoid a lot of writing out interfaces or making class hierarchies. You end up with something less structurally uniform, but with a much shorter time to get results.
If you care about type mismatches, use a statically typed language. If you just want to get something working, use a dynamically typed one.
Swift looks promising in this regard.
I'm not saying Python is the best language of all, but dissecting Python bytecode is kind of missing the point. Python's strong point is its ease of use.
(I agree with your observation, though.)