0

How can I speed up python function calls. I know I can use pypy to speed it up by a lot, but would like to see how fast I can get by sticking to regular python interpreter.

Creating functions per tasks makes code cleaner and easier to read, but seems to slow down the code by a lot, especially if these functions are being called millions of times.

Take a simple example below. Of course in reality these functions are more complicated, but I just used a simple function for each just to show how much additional overhead a function call can have. The first example uses functions, where get_type is called 50 million times, do_even is called 25 million times, and do_odd is called 25 million times. The second example removed all these function calls, and placed the logic directly. This looks ok for this example, but for real life examples with more complicated logic and to have clean code, this may not be achievable. The third example utilizes generators, which is a bit faster than using functions, but not as fast as not using functions.

import datetime


TOTAL = 50000000


def timer(func):
    def wrapper(*args, **kwargs):
        start = datetime.datetime.now()
        res = func(*args, **kwargs)
        print("{} took: {}".format(func.__name__, datetime.datetime.now() - start))
        return res
    return wrapper


def get_type(num):
    if num%2:
        return "even"
    else:
        return "odd"


def do_even(counter):
    counter["even"] += 1


def do_odd(counter):
    counter["odd"] += 1


@timer
def with_functions():
    counter = {"even": 0, "odd": 0}
    for num in range(TOTAL):
        type = get_type(num)
        if type == "even":
            do_even(counter)
        else:
            do_odd(counter)


@timer
def no_functions():
    counter = {"even": 0, "odd": 0}
    type = None
    for num in range(TOTAL):
        if num % 2:
            type = "even"
        else:
            type = "odd"

        if type == "even":
            counter["even"] += 1
        else:
            counter["odd"] += 1


def g_evens(counter):
    while 1:
        counter["even"] += 1
        yield


def g_odds(counter):
    while 1:
        counter["odd"] += 1
        yield

@timer
def generators():
    counter = {"even": 0, "odd": 0}
    g_even = g_evens(counter)
    g_odd = g_odds(counter)

    for num in range(TOTAL):
        if num % 2:
            next(g_even)
        else:
            next(g_odd)


with_functions()
no_functions()
generators()

Output in python 3 (linux):

with_functions took: 0:00:18.344544
no_functions took: 0:00:09.707193
generators took: 0:00:12.016994

Output in pypy3 (linux):

with_functions took: 0:00:01.562282
no_functions took: 0:00:01.513855
generators took: 0:00:02.496888

In pypy3, with functions and no functions does not seem to have much of a difference. Is there a way to do this in regular python interpreter? Or should I not really worry about it, since in reality the more complicated logic can take longer and the 9 seconds extra is really nothing. But here since the example is so simple and quick, the 9 seconds seems a lot (~50%) of the total execution..

user1179317
  • 2,693
  • 3
  • 34
  • 62
  • Perhaps this will answer your question. https://stackoverflow.com/questions/3033329/why-are-python-programs-often-slower-than-the-equivalent-program-written-in-c-or – Nicholas Hunter Apr 04 '21 at 02:27
  • Thanks for that. Sorry for the confusing question. I kind of knew that already and removed the question of `why`, really want to focus on `how` to speed it up – user1179317 Apr 04 '21 at 02:41
  • 1
    CPython function calls are expensive, I think that's the main thing you are seeing here. PyPy's JIT can get rid of the calls by in-lining them – shuttle87 Apr 04 '21 at 02:45
  • If you want to stick with the regular interpreter with the regular package, no you cannot do much more to prevent that. This is exactly the reason why the PyPy team created PyPy after all... If you are Ok to use some additional package, then you can improve this (with some constraints). More specifically, I am thinking about using Cython, Numba, Numpy, etc. – Jérôme Richard Apr 04 '21 at 10:42

0 Answers0