The just in time compiler in PyPy is a complicated piece of software. It can run some programs very fast, while others it can't yet or it'll never be able to. This is a non-exhaustive list of good and bad practices when writing code targeting PyPy, so it'll run fast.
Simple is better than complex. JIT is not very smart, you have to let it find out what your code does. The simpler your code is the better it'll run. Also is applies to using well-known, but unwritten coding practices. There are many optimizations that optimize common usage pattern against an uncommon usage pattern.
Attribute access for new style classes is very fast if attribute names are constant. For example:
x.a = y
setattr(x, 'a', y)
will be much faster than a dynamic version:
setattr(x, 'a' + some_variable, y)
New and old style classes
New style classes are faster than old style classes. Both are much faster than classes that inherit from both new and oldstyle classes. Avoid the latter at all cost.
Calling and passing arguments
The simpler your calls are the better. Passing normal arguments should not pose big performance hit, while always passing a very general
**kwargs might. The actual depth of the call stack does not matter that much, since a lot of it might end up being inlined and generating no extra code at all.
sys.exc_info() work, but they give performance penalty that can be huge, by disabling the JIT. Use only for specialized use cases (like a debugger) where performance does not matter.
One unobvious usecase where this is used is the logging module. Don't use logging module if you want things to be fast.
Things that are known to be slow, but shouldn't be
As of now (Feb 2011) generators are usually slower than corresponding code not using generators. Same goes for generator expressions. For most cases using list comprehension is faster.
As of now (Feb 2011) using
cStringIO is faster than
''.join(list). This should be fixed at some point.
As of Nov 2012, the above no longer seems to be true, at least joining a list that is already created (tested with PyPy 2.0 beta 1). However, if you are creating a list on-the-fly (for-loop that appends to a list, then join everything at the end), then cStringIO is still faster in PyPy.
sys.settrace disables the JIT.