views:

164

answers:

3

I've been playing around with a timing decorator for my pylons app to provide on the fly timing info for specific functions. I've done this by creating a decorator & simply attaching it to any function in the controller I want timed.

It's been pointed out however that decorators could add a fair amount of overhead to the call, and that they run 2-3x slower than an undecorated function.

Firstly, I would expect that executing a decorated function would take a smite longer than an undecorated one, but I would expect that overhead to be in the thousandths of seconds & be negligible compared to a SQL insert call. The decorator itself does simple simple timing calculations using time.time() & some very simple aggregation.

Do decorators add significant overhead to a system? I can't find anything to back that up.

+5  A: 

The overhead added by using a decorator should be just one extra function call.

The work being done by the decorator isn't part of the overhead as your alternative is to add the equivalent code to the decorated object.

So it's possible that the decorate function takes twice as long to run, but that's because the decorator is doing some important work that takes roughly the same time to fun as the undecorated function.

gnibbler
+3  A: 

What is important to know is that a decorator has a simple effect:

@decorator
def f():
    …

is just syntactic sugar for

def f():
    …
f = decorator(f)

Thus, if decorator does not do anything, you do not have any overhead when calling the decorated function (the call decorator(f) does take a little time, though), like in

decorator = lambda func: func
@decorator
def f():
    …

If the decorator does anything, you only get whatever time overhead the decorator involves. This typically include an additional function call (that of the decorated function), as in

def decorator(func):
    def decorated_func():
        print "Before calling function", func  # Some overhead (but that's normal)
        func()  # This will be a second function call, after the call to decorated_func()
    return decorated_func

Thus, in itself, decorating a function does not add much overhead for what you want to do: the only obvious overhead that you could in principle remove would be to not call func() in the decorated function but instead copy its full code, but the legibility of the code would suffer (legibility and flexibility are some of the reasons decorators do exist in the first place).

EOL
-1 Function call overhead in Python is not insignificant. And to suggest that one replicate `func()`'s code into the decorator defeats the whole point of a decorator in the first place. If anything, I'd prefer to replicate the decorator's code into `func()`, but again, if we do that, the simplicity of using the decorator is defeated and `func()`'s code is polluted with the decorating behavior. You really can't say "if decorator does not do anything, you do not have any overhead". Decorators are useful for code tracing, etc., but they come at the cost of an added function call at runtime.
Paul McGuire
@Paul: Indeed, function call overhead is relatively significant in Python (never said otherwise!). And I agree with the fact that putting `func()`'s code in the decorator defeats the whole point of a decorated (as I wrote). We therefore fully agree! I was just giving key points that let the original poster understand exactly where the overhead of a decorator comes from.
EOL
My main quibble was with your statment "if decorator does not do anything, you do not have any overhead," which sounded like you were overlooking the extra function call. I went to remove my downvote, but SO won't let me unless you make some edit to your answer.
Paul McGuire
@Paul: Thank you, I see. I made the answer more precise in order to account for your remark.
EOL
+1  A: 

Do decorators add significant overhead to a system? I can't find anything to back that up.

They add almost no measurable overhead. Zero.

It's important to note that the decorator runs once to create the decorated function. Once.

The decorated function has two parts to it.

  1. whatever decoration was added. This is not overhead.

  2. plus the original function. This is not overhead.

There's no real overhead at all. You might -- with some care -- be able to measure the overhead of one extra function call-and-return as part of a decorated function, but that's almost unmeasurably small. And it's probably far less than an alternative design that doesn't use decoration.

S.Lott