5

I noticed by chance that a simple program generating a class from a large datafile ran a lot faster in Python 2.7 vs. 3.5. I read here that the use of "infinite precision" integers was to blame for slowdown in simple enumeration, but even when I tried a simple test instantiating this class I found that Python 3 was significantly slower:

class Benchmark(object):
    def __init__(self):
        self.members = ['a', 'b', 'c', 'd']


def test():
    test = Benchmark()

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

I thought perhaps it was something to do with the size of each class instance, but the Python 3 instance was smaller than 2 (56 vs. 64)

$python3 benchmarks.py
0.7017288669958361
$python benchmarks.py
0.508942842484

I have tried many variations on this theme, including with 3.4 on a different machine, and still get the same results. Any ideas what's going on?

Community
  • 1
  • 1
jonwells
  • 213
  • 2
  • 7
  • The linked duplicate is about Python 3 being faster than Python 2, while this question is about the other way around. Voting to reopen. – jwodder Nov 06 '15 at 17:44
  • 1
    What do you get if simply run `timeit("Benchmark()", ...)`? It sounds like you don't really want to time the function call, the variable assignment, etc. And what if you only time the list creation? – shx2 Nov 06 '15 at 20:08
  • I get the same results as you until I pare this down to just the class. I did just `class A: pass` then changed your `timeit` call to `timeit("A()", setup="from __main__ import A")` and Py2 was twice as slow as Py3. Whatever the initial results, I doubt this has to do with object instantiation. If I make that `class A(object): pass`, the results are indistinguishable. – Two-Bit Alchemist Nov 06 '15 at 20:14
  • Is instance creation really a bottleneck for any real code? I'd guess that any other work you do with your class is going to be orders of magnitude slower than the class creation, so I wouldn't worry about how long it takes. If you've profiled your code and see that this is really an issue, then sure, you should investigate it, but I doubt that's the case. – Blckknght Nov 06 '15 at 21:42
  • I'm more just curious, quibbling over 20secs to run something vs 5 seconds for Py2, but does seem a little odd that Py3 should be slower. I stripped it down to just timing instantiation of a completely empty class and I'm still finding 3 slower than 2, on both macbook pro and work machine running ubuntu with i7-4790K 4.00GHz processor and 16gb ram, both versions installed in same place and everything. – jonwells Nov 07 '15 at 19:43
  • Have you run a profiler on your python3 code and seen where it's taking up the time? – Daenyth Dec 23 '15 at 16:52

1 Answers1

3

You are not measuring class instantiation time, you are measuring class instantiation, plus assignment, plus list creation, ...

Here's a correct benchmark:

$ python -m timeit -s 'class C(object): pass' 'C()'
10000000 loops, best of 3: 0.0639 usec per loop
$ python3 -m timeit -s 'class C(object): pass' 'C()'
10000000 loops, best of 3: 0.0622 usec per loop

As you can see, Python 3 is sightly faster.

Andrea Corbellini
  • 17,339
  • 3
  • 53
  • 69
  • In addition, Python 3 is saving more memory than you think; the total size overhead of an instance is the `sys.getsizeof` for the instance plus the `sys.getsizeof` for its `__dict__` (unless `__dict__` was suppressed at C layer or using `__slots__`). [Python 3.3 introduced shared key dictionaries](https://docs.python.org/3/whatsnew/3.3.html#pep-412), so in addition to the object structure shrinking a little (8 bytes on x64), the size of `__dict__` for single attribute instances drops by a factor of ~3x (on my Linux x64 machine, from 280 bytes to 96 bytes). Big savings for lots of instances. – ShadowRanger Dec 23 '15 at 17:12
  • Andrea: You might want to have the test assign a single compile time literal member; while list construction, function calls and item assignment aren't properly part of a good test, assigning at least one instance attribute in `__init__` should be part of a reasonable test. – ShadowRanger Dec 23 '15 at 17:35
  • @ShadowRanger: if you mix too many things in benchmarks, you'll never find out what's wrong. This question is a clear-cut example: the OP thinks class instantiation is slower while it's not. – Andrea Corbellini Dec 23 '15 at 17:43
  • Yeah, all I'm saying is that for most people, initializing the members is part and parcel of "class instantiation". Just use a literal that can be expressed with a `LOAD_CONST` in the byte code (e.g. empty `tuple` `()` or `int` `0`) and you'll limit additional confounding factors, while still testing a fairly important part of instance creation. Shared key dictionaries may slightly slow instance creation, though the benefits in other cases (lower memory use, often higher speed in use) is usually worth it. – ShadowRanger Dec 23 '15 at 17:59
  • Yeah I realised that after shx2's comment on the original post. I'm still getting the same issues, comparing python3.5 with 2.7.1. `$ python -m timeit -s 'class C(object): pass' 'C()' ` `10000000 loops, best of 3: 0.0896 usec per loop ` `$ python3 -m timeit -s 'class C(object): pass' 'C()' ` `10000000 loops, best of 3: 0.106 usec per loop` – jonwells Dec 24 '15 at 16:51
  • @jonwells: how were py2 and py3 compiled? – Andrea Corbellini Dec 24 '15 at 16:53
  • well that where I reckon the difference lies, py2.7 was bundled with OSX, whereas py3 would have been (if i remember right) have been installed using the installer provided with the release – jonwells Dec 24 '15 at 16:56
  • @jonwells: I suspect that your py3 were compiled with less optimizations than your py2, but I have no experience with OSX, so I can't be sure. Try running other benchmarks: is py2 always faster than py3? – Andrea Corbellini Dec 24 '15 at 20:34