Zero runtime impact tracing

Steven D'Aprano steve at pearwood.info
Sat Jul 30 06:51:22 EDT 2016


On Sat, 30 Jul 2016 06:32 pm, Johannes Bauer wrote:

> Hi group,
> 
> I'm using CPython 3.5.1. Currently I'm writing some delicate code that
> is doing the right thing in 99% of the cases and screws up on the other
> 1%.
> 
> I would like to have tracing in some very inner loops:
> 
> if self._debug:
>    print("Offset %d foo bar" % (self._offset))
> 
> However, this incurs a hefty performance penalty even when tracing
> disabled.

Without seeing your code, it's hard to say. My guess is that you can
optimize this slightly:

class X:
    def method(self):
        debug = bool(self._debug)  # force a bool, just in case
        for value in sequence:
            # critical loop
            if debug:
                print("Offset %d foo bar" % (self._offset))
            ...


That saves an attribute lookup and *potentially* a bool conversion each time
around the loop.

If that's still too slow, just about the fastest thing you can do is Python
is an "is" comparison against None. Try this:

class X:
    def method(self):
        if self._debug:
           sentinel = None
        else:
           sentinel = object()  # Anything except None. 
        for value in sequence:
            # critical loop
            if sentinel is None:
                print("Offset %d foo bar" % (self._offset))
            ...


Of course, it's even faster to NOT do a comparison:


class X:
    def method(self):
        if self._debug:
            for value in sequence:
                # critical loop
                print("Offset %d foo bar" % (self._offset))
                ...
        else:
            for value in sequence:
                # critical loop
                ...


although that duplicates code. If the body of the loop is only small, say
two or three lines at most, you might not mind that it is duplicated.


> What I want is that the if clause completely disappears during bytecode
> compilation if self._debug is not set. Is there any way that I can tell
> the optimizer that this variable will either be set once, but never
> change during runtime and that it can go ahead and completely remove the
> code when self._debug is False?

No. However, you can get a similar result using the magic dunder variable
__debug__ (that's TWO leading and trailing underscores):


class X:
    def method(self):
        for value in sequence:
            # critical loop
            if __debug__:
                print("Offset %d foo bar" % (self._offset))
            ...


In that case, the byte-code generated will be equivalent to the critical
loop:

        for value in sequence:
            # critical loop
            print("Offset %d foo bar" % (self._offset))
            ...

with no test, unless you pass -O as a command-line option to Python, in
which case both the test and the call to print will be completely removed:

        for value in sequence:
            # critical loop
            ...



-- 
Steven
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list