5 ms·
The dynamic aspect of Python is once again probably the major penalty here. One major difference here is that in Python you can overwrite min(), so it has to a
by winrid 2y ago
The dynamic aspect of Python is once again probably the major penalty here.
One major difference here is that in Python you can overwrite min(), so it has to access this function through more abstraction, but not in PHP. I bet PyPy is close to PHP here however.
- JohnKemeny 2y agoWith pypy, the code runs in 0.226s on my computer. So yes, the comparison of OP is CPython vs PHP.
- mg 2y agoYes, it is CPython. Added data on the PyPy performance to this comment now: https://news.ycombinator.com/item?id=41199431 https://news.ycombinator.com/item?id=41199431
- mg 2y agoYou might be right about the min() call being the major penalty. When I avoid the min() call like this ... min2.py: i = 10_000_000 r = 0 while i>0: i = i-1 if i<500: r += i else : r += 500 print(r) I get a speedup of about 50%: time python3 min2.py 4999874750 real 0m1.338s Still 4x slower than PHP though. With PyPy I get an amazing speedup of 96%: time pypy3 min.py 4999874750 real 0m0.088s 4 times faster than the PHP version! I'm not sure what the state of things is regarding PyPy and running a web application. Doing a bit of googling, it seems that calling C functions from PyPy is slow, so one would have to check if the application uses those (for example to connect to the database).
- eesmith 2y agoFWIW, if I place the code in a function, so that variable lookups are constant-time rather than a dictionary lookup in the module globals, then (on my Mac, with Python 3.12) I get a two-fold speedup. That is: def spam(): t1 = time.time() i = 10_000_000 r = 0 while i>0: i = i-1 if i<500: r += i else : r += 500 t2 = time.time() print(r) print("Time:", t2-t1) reports 0.87 seconds outside a function and 0.43 seconds when inside a function. (Using the shell's time, of course, also includes the Python startup time. When I use it I get 0.94 and 0.49 seconds, indicating a 0.06s startup penalty.)
- mg 2y agoInteresting! Same here. Also with pypy3, putting it in a function about doubles the speed: time pypy3 min_in_function.py 4999874750 real 0m0.045s
- eesmith 2y agoBack in Python 1.x, function locals used a dictionary, just like module globals. This made it possible do to: def f(): from math import * return cos(3.0) https://peps.python.org/pep-0227/#import-used-in-function-scope https://peps.python.org/pep-0227/#import-used-in-function-sc... notes "The language reference specifies that import * may only occur in a module scope. (Sec. 6.11) The implementation of C Python has supported import * at the function scope." Python 2.1 added static nested scopes (see the PEP 227 link), which enabled a much faster locals lookup. With that one case excluded, all of the locals can be determined at parse/byte-compile time, stored in a simple vector, and referenced by offset: >>> z = 0 >>> def f(x): ... y = x + z ... return y * 2 ... >>> import dis >>> dis.dis(f) 1 0 RESUME 0 2 2 LOAD_FAST 0 (x) 4 LOAD_GLOBAL 0 (z) 14 BINARY_OP 0 (+) 18 STORE_FAST 1 (y) 3 20 LOAD_FAST 1 (y) 22 LOAD_CONST 1 (2) 24 BINARY_OP 5 (*) 28 RETURN_VALUE The "LOAD_FAST" uses offset 0 to store "x" and offset 1 to store "y", while "z" required a LOAD_GLOBAL to find z in the globals() dictionary.
- mg 2y agoYes, I stumbled across this quite recently: https://x.com/marekgibney/status/1817495719520940236 https://x.com/marekgibney/status/1817495719520940236 I was surprised that you cannot access dynamically created local variables even though they exist. Looks like the local dictionary still exists. Just that whether accessing a local variable looks it up in the local or global dictionary is determined at compile time.
- 2y ago
- khgrfas 2y agoFor a web application PHP is likely much faster, because it does templating in a sane manner, while you need huge frameworks for Python.
- mg 2y agoI think you still need to do the templating in PHP, because you usually want to escape html chars. And because you want to seperate code and layout. Having "Hello <?=$name?>" in the template is usually not what you want. It is also harder to read than "Hello {NAME}". Let's test templating in Python and PHP! template.py: def main(): t = 'Number {NR} is cool' i = 10_000_000 r = 0 while i>0: x = t.replace('{NR}', str(i)) r += len(x) i = i - 1 print(r) main() template.php: <?php $t = 'Number {NR} is cool'; $i = 10_000_000; $r = 0; while ($i>0) { $x = str_replace('{NR}', $i, $t); $r += strlen($x); $i = $i - 1; } print($r); Results: time python3 template.py 218888897 real 0m2.854s time php template.php 218888897 real 0m0.994s time pypy3 template.py 218888897 real 0m0.795s So for this use case, PHP is 3x faster than CPython and PyPy is 20% faster than PHP.
- tripflag 2y agoI've tested a particular python webserver in PyPy and saw roughly the same performance as CPython on average. My guess at the time was that PyPy has a bigger overhead for network sockets than CPython. You would likely see a gain from using PyPy if the webserver did heavier processing in pure python than what mine does.
- rbanffy 2y agoI’m not sure it’s worth to invest too much effort in such optimisations unless they can yield perceived performance improvements or cost reductions. For Google, a 0.1% decrease in power consumption pays for itself in hours, but for mere mortals (and regular companies) it often doesn’t. It’s a fun sport though. I love it.
- tripflag 2y agoI just wanted to make sure I wasn't passing up on a free 400% boost or something :-) but it was already running plenty fast, so it was entirely out of curiosity.
- pjmlp 2y agoSmalltalk, SELF and Common Lisp are just as dynamic, with great JIT implementations, their research lies the foundation for all modern JITs in dynamic languages, one of those implementations grew up to become Hotspot. Those systems are image based, at any time, you can eval code, break into the debugger, change code and redo the interrupted code, affecting everything that the JIT already improved on the whole image.