Python Performance: Single-threaded, multi-threaded, and Gevent

Post on 06-May-2015

2,503 views 4 download

description

A smatter of performance tips for Python programs.

transcript

What I Know About Python Performance

A. Jesse Jiryu Davishttp://emptysqua.re

MongoDB

Agenda

• Single-threaded

• Multi-threaded

• Gevent

Single-Threaded

Why is Python slow? • Everything’s an object • Lookup

Everything’s an object

def f():! i = 2000! i += 1!

Everything’s an object

import dis!dis.dis(f)!

Everything’s an object

0 LOAD_CONST 1 (2000)! 3 STORE_FAST 0 (i)! 6 LOAD_FAST 0 (i)! 9 LOAD_CONST 2 (1)!12 INPLACE_ADD!13 STORE_FAST 0 (i)!

def f():! i = 2000! i += 1

Everything’s an object

0 LOAD_CONST 1 (2000)! 3 STORE_FAST 0 (i)! 6 LOAD_FAST 0 (i)! 9 LOAD_CONST 2 (1)!12 INPLACE_ADD!13 STORE_FAST 0 (i)!

malloc!

malloc!

def f():! i = 2000! i += 1

Everything’s an object

12 seconds for 100M numbers:

total = 0!for i in longs:! total += i!print float(total) / len(longs)

Everything’s an object

1 second for 100M numbers:

print float(sum(longs)) / len(longs)

Everything’s an object

0.2 seconds for 100M numbers:

print numpy.mean(array)

Lookup

n = 1!!

def f():! print n!

0 LOAD_GLOBAL 0 (n)! 3 PRINT_ITEM! 4 PRINT_NEWLINE

hashtable lookup

n = 1!!

def f():! print n!

array offset

n = 1!!

def f(n=n):! print n!

0 LOAD_FAST 0 (n)! 3 PRINT_ITEM! 4 PRINT_NEWLINE

Profiling

Profiling

import yappi!!yappi.start(builtins=True)!my_function()!yappi.stop()!stats = yappi.get_func_stats()!stats.save('yappi.callgrind', type='callgrind')!

KCacheGrind

KCacheGrind

Multi-threaded

Multi-threaded

When is the GIL released?• Every 100 bytecodes• Acquiring other locks • Network or file operations

Multi-threadedBecause:

socketmodule.c

Py_BEGIN_ALLOW_THREADS!outlen = recv(s->sock_fd, cbuf, len);!Py_END_ALLOW_THREADS!

Multi-threadedBecause:

socketmodule.c

Py_BEGIN_ALLOW_THREADS!outlen = recv(s->sock_fd, cbuf, len);!Py_END_ALLOW_THREADS!

release the GIL!

Multi-threadedThis makes sense:

threads = [! threading.Thread(target=download_a_url())! for _ in range(10)]!!for t in threads:! t.start()!!for t in threads:! t.join()!

Multi-threadedThis makes no sense:

threads = [! threading.Thread(target=do_calculations())! for _ in range(10)]!!for t in threads:! t.start()!!for t in threads:! t.join()!

Geventfrom gevent import monkey!monkey.patch_all()!!threads = [! threading.Thread(target=download_a_url())]!!for t in threads:! t.start()!!for t in threads:! t.join()! actually a greenlet

GreenletProfiler

Links

http://bit.ly/pythonperformance