Transcript
Page 1: Python Performance Profiling: The Guts And The Glory

Python Profiling:

A. Jesse Jiryu Davis

@jessejiryudavis

MongoDB

The Glory&

The Guts

Page 2: Python Performance Profiling: The Guts And The Glory
Page 3: Python Performance Profiling: The Guts And The Glory

“PyMongo is slower!compared to the JavaScript version”

MongoDB Node.js driver:!88,000 per secondPyMongo: ! ! ! ! ! ! ! ! ! 29,000 per second

Page 4: Python Performance Profiling: The Guts And The Glory

“Why Is PyMongo Slower?”

From:[email protected]!To:!! [email protected]!CC:!! [email protected]

Hi Jesse,!!Why is the Node MongoDB driver 3 times!faster than PyMongo?!http://dzone.com/articles/mongodb-facts-over-80000

Page 5: Python Performance Profiling: The Guts And The Glory

The Python Code

# Obtain a MongoDB collection.!import pymongo!!client = pymongo.MongoClient('localhost')!db = client.random!collection = db.randomData!collection.remove()!

Page 6: Python Performance Profiling: The Guts And The Glory

n_documents = 80000!batch_size = 5000!batch = []!!import time!start = time.time()

The Python Code

Page 7: Python Performance Profiling: The Guts And The Glory

import random!from datetime import datetime!!min_date = datetime(2012, 1, 1)!max_date = datetime(2013, 1, 1)!delta = (max_date - min_date).total_seconds()!

The Python Code

Page 8: Python Performance Profiling: The Guts And The Glory

What?!

The Python Codefor i in range(n_documents):! date = datetime.fromtimestamp(! time.mktime(min_date.timetuple())! + int(round(random.random() * delta)))!! value = random.random()! document = {! 'created_on': date,! 'value': value}!! batch.append(document)! if len(batch) == batch_size:! collection.insert(batch)! batch = []!

Page 9: Python Performance Profiling: The Guts And The Glory

duration = time.time() - start!!print 'inserted %d documents per second' % (! n_documents / duration)!

The Python Code

inserted 30,000 documents per second

Page 10: Python Performance Profiling: The Guts And The Glory

The Node.js Code

(not shown)

Page 11: Python Performance Profiling: The Guts And The Glory

The Question

Why is the Python script 3 times slower than the equivalent Node script?

Page 12: Python Performance Profiling: The Guts And The Glory

Why Profile?

• Optimization is like debugging• Hypothesis:

“The following change will yield a worthwhile improvement.”

• Experiment

• Repeat until fast enough

Page 13: Python Performance Profiling: The Guts And The Glory

Why Profile?

Profiling is a way togenerate hypotheses.

Page 14: Python Performance Profiling: The Guts And The Glory

Which Profiler?

• cProfile • GreenletProfiler • Yappi

Page 15: Python Performance Profiling: The Guts And The Glory

Yappi

By Sümer Cip

Page 16: Python Performance Profiling: The Guts And The Glory

Yappi

Compared to cProfile, it is: !

• As fast • Also measures functions • Can measure CPU time, not just wall• Can measure all threads • Can export to callgrind

Page 17: Python Performance Profiling: The Guts And The Glory

Yappiimport yappi!!yappi.set_clock_type('cpu')!yappi.start(builtins=True)!!start = time.time()!!for i in range(n_documents):! # ... same code ... !!duration = time.time() - start!stats = yappi.get_func_stats()!stats.save('callgrind.out', type='callgrind')!

Same code as before

Page 18: Python Performance Profiling: The Guts And The Glory

KCacheGrind

Page 19: Python Performance Profiling: The Guts And The Glory

for index in range(n_documents):! date = datetime.fromtimestamp(! time.mktime(min_date.timetuple())! + int(round(random.random() * delta)))!! value = random.random()! document = {! 'created_on': date,! 'value': value}!! batch.append(document)! if len(batch) == batch_size:! collection.insert(batch)! batch = []!

The Python Code

one third

of the tim

e

Page 20: Python Performance Profiling: The Guts And The Glory

for index in range(n_documents):! date = datetime.now()!!!! value = random.random()! document = {! 'created_on': date,! 'value': value}!! batch.append(document)! if len(batch) == batch_size:! collection.insert(batch)! batch = []!

The Python Code

Page 21: Python Performance Profiling: The Guts And The Glory

The Python Code

• Before: 30,000 inserts per second • After: 50,000 inserts per second

Page 22: Python Performance Profiling: The Guts And The Glory

Why Profile?

• Generate hypotheses• Estimate possible improvement

Page 23: Python Performance Profiling: The Guts And The Glory

How DoesProfiling Work?

int callback(PyFrameObject *frame,! int what,! PyObject *arg);!

int start(void)!{! PyEval_SetProfile(callback);!}!

Page 24: Python Performance Profiling: The Guts And The Glory

PyObject *!PyEval_EvalFrameEx(PyFrameObject *frame)!{! if (tstate->c_profilefunc != NULL) {! tstate->c_profilefunc(frame,! PyTrace_CALL,! Py_None);! }!! /* ... execute bytecode in the frame! * until return or exception... */!! if (tstate->c_profilefunc != NULL) {! tstate->c_profilefunc(frame,! PyTrace_RETURN,! retval);! }!}!

Page 25: Python Performance Profiling: The Guts And The Glory

int callback(PyFrameObject *frame,! int what,! PyObject *arg)!{! switch (what) {! case PyTrace_CALL:! {! PyCodeObject *cobj = frame->f_code;! PyObject *filename = cobj->co_filename;! PyObject *funcname = cobj->co_name;!! /* ... record the function call ... */! }! break;!! /* ... other cases ... */!! }!}!

Page 26: Python Performance Profiling: The Guts And The Glory

A. Jesse Jiryu Davis

@jessejiryudavis

MongoDB


Recommended