Live data from Hacker News

Why Python Is Slow: Looking Under the Hood

jakevdp.github.io

141–150 of 156 posts

Re: Why Python Is Slow: Looking Under the Hood

#141
post #22

Earlier quoted context omitted.

That's a lossy short answer. LuaJIT with JIT turned off still runs circles around CPython.

Even normal Lua is fast compared to Python, if the shootout games are any bit accurate. http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...

You are told how the measurements were made -- Are the benchmarks game measurements accurate?

I think these questions are more to the point: Were the Python programmers as skilled as the Lua programmers? Do the programs do anything like the things your programs do?

Re: Why Python Is Slow: Looking Under the Hood

#142
post #20

Earlier quoted context omitted.

There are several compiler-only Lisp implementations which can execute source from text. Nobody there would call them interpreter because of that. In Lisp we call them interpreter, when the implementation traverses the source code during execution. If it compiles the source to some byte-code or machine code, we call in Compiler. The incremental nature of a compiler, then does not make it that we would call it an Inte…

> There are several compiler-only Lisp implementations which can execute source from text. Why not? I would.

Oops. My "Why not?" above is in reference to this:

> Nobody there would call them interpreter because of that.

Re: Why Python Is Slow: Looking Under the Hood

#143
post #77

Earlier quoted context omitted.

For a general-purpose problem, Perl won't be particularly fast. For a Perl-type problem (scanning and parsing big files), Perl is very fast. Doing a Perl-type problem in a general-purpose language would be considerably slower. However, Python or others will perform much better in the "can I read my own code six months later" benchmark.

> For a Perl-type problem (scanning and parsing big files), Perl is very fast. I think it's a matter of what you're comparing it to. Compared to using Perl for a general-purpose problem, Perl for scanning/parsing is fast. Compared to scanning/parsing with C, Perl is not fast. $ ruby -e '1.upto(1000000) { |n| puts "This is line number #{n}" }' > file $ time perl -ne 'print if /number 12345/' I gave Perl every possible…

I recall anecdotal reports that Perl was faster than egrep in some cases. I never tested it myself and that was a long time ago—could be a bug that is long since fixed.

Re: Why Python Is Slow: Looking Under the Hood

#144
post #7

Earlier quoted context omitted.

What you propose sounds like it would be a pure headache for all code which otherwise expects a uniform memory API. Consider a C extension which takes an object and appends it to a list. If small integers did not have a refcount then that extension would have to have special code, like "if object is not a small integer, then increment the reference count".

I have experience turning a Lisp dialect with refcounted integers into supporting non-refcounted integers, identified by a type tag field in the "value cell" type. > If small integers did not have a refcount then that extension would have to have special code, like "if object is not a small integer, then increment the reference count". Easily implemented in one place in the "increment_refcount(obj)" inline function:…

Those are excellent points. In the context of CPython, Guido van Rossum made the design decision to not use tagged objects, based on experience with ABC implementation, which did.

See https://mail.python.org/pipermail/python-dev/2004-July/04614... .

I just realized though that since the patch to support tagged integers is small, a closed system like the CCP Games distribution of Python, which has no extensions they don't control, might be able to use this idea.

Re: Why Python Is Slow: Looking Under the Hood

#145
post #77

Earlier quoted context omitted.

For a general-purpose problem, Perl won't be particularly fast. For a Perl-type problem (scanning and parsing big files), Perl is very fast. Doing a Perl-type problem in a general-purpose language would be considerably slower. However, Python or others will perform much better in the "can I read my own code six months later" benchmark.

> For a Perl-type problem (scanning and parsing big files), Perl is very fast. I think it's a matter of what you're comparing it to. Compared to using Perl for a general-purpose problem, Perl for scanning/parsing is fast. Compared to scanning/parsing with C, Perl is not fast. $ ruby -e '1.upto(1000000) { |n| puts "This is line number #{n}" }' > file $ time perl -ne 'print if /number 12345/' I gave Perl every possible…

I'm always wary of these kinds of sub-second benchmarks because more often than not you've only accidentally measured just the compilation and startup times.

I might have some bias though from speeding up a crusty old Perl CGI web apps with multi-second request times down to less than 100ms simply by keeping the perl processes persistent with mod_fcgid or whatever.

Re: Why Python Is Slow: Looking Under the Hood

#146

Earlier quoted context omitted.

> For a Perl-type problem (scanning and parsing big files), Perl is very fast. I think it's a matter of what you're comparing it to. Compared to using Perl for a general-purpose problem, Perl for scanning/parsing is fast. Compared to scanning/parsing with C, Perl is not fast. $ ruby -e '1.upto(1000000) { |n| puts "This is line number #{n}" }' > file $ time perl -ne 'print if /number 12345/' I gave Perl every possible…

I'm always wary of these kinds of sub-second benchmarks because more often than not you've only accidentally measured just the compilation and startup times. I might have some bias though from speeding up a crusty old Perl CGI web apps with multi-second request times down to less than 100ms simply by keeping the perl processes persistent with mod_fcgid or whatever.

> I'm always wary of these kinds of sub-second benchmarks because more often than not you've only accidentally measured just the compilation and startup times.

I increased the iteration count by 10x and observed exactly the same pattern:

    $ time perl -ne 'print if /number 12345/' 

Re: Why Python Is Slow: Looking Under the Hood

#147

Earlier quoted context omitted.

I'm always wary of these kinds of sub-second benchmarks because more often than not you've only accidentally measured just the compilation and startup times. I might have some bias though from speeding up a crusty old Perl CGI web apps with multi-second request times down to less than 100ms simply by keeping the perl processes persistent with mod_fcgid or whatever.

> I'm always wary of these kinds of sub-second benchmarks because more often than not you've only accidentally measured just the compilation and startup times. I increased the iteration count by 10x and observed exactly the same pattern: $ time perl -ne 'print if /number 12345/'

Cool, thanks for entertaining my superstitions :)

Re: Why Python Is Slow: Looking Under the Hood

#148

Earlier quoted context omitted.

> There are several compiler-only Lisp implementations which can execute source from text. Why not? I would.

Oops. My "Why not?" above is in reference to this: > Nobody there would call them interpreter because of that.

Why would you call an 'compiler' an 'interpreter'?

The following is Clozure Common Lisp. Every single input is compiled directly to machine code. Feed it source text and everything is compiled. Each and every expression.

Why should I call a direct native code compiler an 'Interpreter'? Makes no sense.

    ? (let ((f (lambda (a)
                 (+ 1 a))))
        (disassemble f)
        (funcall f 2))


    ;;; (lambda (a) (+ 1 a))
    L0
        (leaq (@ (:^ L0) (% rip)) (% fn))       ;     [0]
        (cmpl ($ 8) (% nargs))                  ;     [7]
        (jne L57)                               ;    [10]
        (pushq (% rbp))                         ;    [12]
        (movq (% rsp) (% rbp))                  ;    [13]
        (pushq (% arg_z))                       ;    [16]

    ;;; (+ 1 a)
        (leaveq)                                ;    [17]
        (testb ($ 7) (% arg_z.b))               ;    [18]
        (jne L39)                               ;    [22]
        (addq ($ 8) (% arg_z))                  ;    [24]
        (jo L32)                                ;    [28]
        (repz)
        (retq)                                  ;    [30]
    L32
        (jmpq (@ .SPFIX-OVERFLOW))              ;    [32]
    L39
        (movl ($ 8) (% arg_y.l))                ;    [39]
        (jmpq (@ .SPBUILTIN-PLUS))              ;    [44]

    ;;; #
    L57
        (uuo-error-wrong-number-of-args)        ;    [57]

    3 ; result

Re: Why Python Is Slow: Looking Under the Hood

#149
post #77

Earlier quoted context omitted.

For a general-purpose problem, Perl won't be particularly fast. For a Perl-type problem (scanning and parsing big files), Perl is very fast. Doing a Perl-type problem in a general-purpose language would be considerably slower. However, Python or others will perform much better in the "can I read my own code six months later" benchmark.

> For a Perl-type problem (scanning and parsing big files), Perl is very fast. I think it's a matter of what you're comparing it to. Compared to using Perl for a general-purpose problem, Perl for scanning/parsing is fast. Compared to scanning/parsing with C, Perl is not fast. $ ruby -e '1.upto(1000000) { |n| puts "This is line number #{n}" }' > file $ time perl -ne 'print if /number 12345/' I gave Perl every possible…

What you are seeing is different regex engines and capabilities, and grep's focus on pure speed and optimization of a common case and Perl's focus on versatility.

I see very similar results between Perl and grep, and you can see this by also including egrep, which allows slightly more complex expressions:

    [root@stats ~]# time perl -ne 'print if /number 123456/' 
But what happens if we use a slightly more complex expression?

    [root@stats ~]# time perl -ne 'print if /number [1]23456/' 
The difference becomes much less pronounced. What if we make the expression just a bit more complex?

    [root@stats ~]# time perl -ne 'print if /number [1]23456[0-9]*/' 
So, now we have the Perl regex engine fairly static across extra complexity while grep and egrep are seeing order of magnitude time increases, and are much slower than Perl at this point. I suspect your first benchmark was the result of a specific optimization grep has that Perl doesn't, or it may be that grep was able to switch to using a DFA regex for that first case, while Perl doesn't both with a completely different regex implementation for special cases like that.

Anecdata: I needed to process a large amount of XML a while back, to the point where a week spent testing and optimizing XML parsing libraries in Perl was worth it, because it could shave weeks or months off the processing time. The winner? A regex that captured attributes and content and assigned name/value pairs directly out to a hash. This was only possible because the XML was highly normalized, but it was actually over 10 times faster than the closest competitor for XML parsing I could fine, and I checked all the libXML libXML2, and SAX libraries I could get my hands on.

In the end, it was something as simple as the following approximation:

    while (my ($doc) = $xml =~ /$get_record_xml_re/) {
        my %hash = $get_record_xml =~ /$record_begin_re$capture_name_and_value_pairs_re$record_end_re/;
        process_record( \%hash );
    }

Re: Why Python Is Slow: Looking Under the Hood

#150

Earlier quoted context omitted.

> For a Perl-type problem (scanning and parsing big files), Perl is very fast. I think it's a matter of what you're comparing it to. Compared to using Perl for a general-purpose problem, Perl for scanning/parsing is fast. Compared to scanning/parsing with C, Perl is not fast. $ ruby -e '1.upto(1000000) { |n| puts "This is line number #{n}" }' > file $ time perl -ne 'print if /number 12345/' I gave Perl every possible…

What you are seeing is different regex engines and capabilities, and grep's focus on pure speed and optimization of a common case and Perl's focus on versatility. I see very similar results between Perl and grep, and you can see this by also including egrep, which allows slightly more complex expressions: [root@stats ~]# time perl -ne 'print if /number 123456/' But what happens if we use a slightly more complex expre…

Your results are interesting and I'd be curious to know why the grep degrades so badly on that last regex.

But the original benchmark was ridiculously biased in favor of Perl by not actually doing anything in Perl.

If Perl is actually being competitive in the unfair benchmark, the benchmark should be made more fair by actually putting some logic in Perl, and writing the equivalent logic in C. At that point, you would start to see C win again (modulo any inherent inefficiencies in grep's regex engine).

> This was only possible because the XML was highly normalized

Another way of putting this is: your regex wasn't actually an XML parser. Things that are actually XML parsers were slower. This is not too surprising.

A 10x slowdown does surprise me somewhat. It doesn't surprise me that you beat libXML or any library that builds the XML structure into a complete DOM before you can process the first record. It does surprise me that you beat SAX by 10x. SAX does have some inefficiency built in, like how it turns attributes into a dictionary internally before giving them to the application. That would probably mean that SAX bindings for Perl to a C parser would have to take a full SAX attribute hash and turn it into a Perl attribute hash. Still, 10x is pretty bad.

Post reply on HN