4 ms·
The claim that pypy uses less memory than Go seems...rather extraordinary. I worked with a fairly complex http api app that ran as a rather svelte wsgi framewo
by stock_toaster 11y ago
The claim that pypy uses less memory than Go seems...rather extraordinary.
I worked with a fairly complex http api app that ran as a rather svelte wsgi framework under gunicorn, and we saw at least 10x or more increase in memory usage than cpython when we switched to pypy, once the jit was fully warmed up (memory usage seemed to hit steady state after about an hour). pypy has also historically (in my experience) been a bit more "lazy" (deferring GC of individual objects longer) than cpython when an object falls out of scope.
My general rule of thumb for pypy has historically been that you trade memory (requires more of it) for speed (faster) when compared against cpython.
Maybe the reimplementation itself was just far more efficient? Hopefully the talk itself clarified that point. I would be very interested in hearing more about that particular aspect.
- windlep 11y agoThis was before Go 1.4, which dropped memory use per goroutine from 8kb -> 2kb. They're fairly close now (minus my leaking goroutines). ;)
- DasIch 11y agoPyPy's JIT requires quite a bit of memory. CPython doesn't have a JIT so it doesn't share that problem. However PyPy also has optimizations specifically to keep memory usage down. Especially if you have a lot of objects, it's certainly possible to consume less memory than CPython. PyPy using less memory than Go does seem weird but depending on how much the GCs differ and how they are configured it could simply be that Go's GC doesn't give up memory as freely to the OS.
- dbaupp 11y agoI don't know the details, but the memory difference was almost certainly due to the different approaches to concurrency. Python coroutines just need to save their exact stack frame while a Go goroutine will spawn a "massive" stack that likely has much more space than needed. The first case might only need tens or hundreds of bytes for a dozen local variables, while the latter case is a fixed overhead of several KB (8 previously, but 2 with Go 1.4 according to the slides). The JIT memory is constant at runtime (proportional(-ish) to the amount of code, which is fixed) while it is desirable to have the number of coroutines be as large as possible.
- thwd 11y agoYour theory regarding PyPy's JIT is disproved rather easily. Python's grammar has been shown to not be context free. This implies that in the best case, the parser is supralinear in both time and space. Just the parser. Now add the rest of the JIT.
- dbaupp 11y agoMy hand-waving is "disproved" even more easily than that, by passing a dynamically generated string to `eval`. However, that's missing my point (and my "-ish"): the JIT is a constant overhead. Assuming they're being careful to not dynamically generate code for handling each coroutine (seems like an obvious thing to avoid, and a reasonable assumption), the JIT'd code is shared across all coroutines, so as you accept more and more connections, requiring less memory for each coroutine will eventually outweight the memory cost of the JIT.
- DasIch 11y agoThe memory used by the JIT will at some point remain constant but before that happens, it will grow as the JIT considers more and more traces to be worthy of compilation. It can take a very long time until the memory consumed by the JIT actually remains constant. If you do continuous integration and deploy several times per day, your application might never reach that point.
- dbaupp 11y agoThe fact it may be less than the long term result isn't so relevant: there's an upper bound so the memory use of the JIT is O(1) with respect to the number of coroutines.