if not DEBUG: log = null_log

S

Simon Burton

Hi,

I'm after a no-op command, so that i can redirect
logging commands in performance critical code.

Something like this:

def log(*args): print args
def null_log(*args): pass
if not DEBUG: log = null_log

is unacceptable because of the overhead of calling
functions in python.

log ("about to slip into python feature request mode.")

Maybe this is what the PEP 336 guy was thinking of (Make None Callable).
Obviously we don't want None to be callable, but
what about a "Null" [1] that's callable, with any args ?

But I guess what I am really asking for is something on the bytecode
level that tells the VM to "do nothing".

Here's an idea: make "pass" into an expression (a value) that is callable,
with any args, and returns None.

log ("finished with python feature request mode.")

I recently discovered "pyc" [2], but i don't quite see how i can
use it while maintaining python source compatability.

bye!

Simon.

[1]: http://occs.cs.oberlin.edu/~jwalker/nullObjPattern/
[2]: http://students.ceid.upatras.gr/~sxanth/pyc/

--
Simon Burton, B.Sc.
Licensed PO Box 8066
ANU Canberra 2601
Australia
Ph. 61 02 6249 6940
http://arrowtheory.com
 
S

Steven D'Aprano

Hi,

I'm after a no-op command, so that i can redirect
logging commands in performance critical code.

Something like this:

def log(*args): print args
def null_log(*args): pass
if not DEBUG: log = null_log

is unacceptable because of the overhead of calling
functions in python.

Excuse my skepticism, but I'd like to see the profiling that you did that
shows this is a problem. Everybody thinks their code is performance
critical, even when it isn't.

So, let's do a test. First, I set up a fake object that requires lots of
name look-ups:
.... pass
....
Now I run a test to time those name lookups:
.... loop = range(n)
.... t = time.time()
.... for i in loop:
.... f = obj.method.attribute.record
.... t = time.time() - t
.... return t
....0.11942911148071289

Now set up a test bypassing the name lookups:
.... loop = range(n)
.... y = obj.method.attribute.record
.... t = time.time()
.... for i in loop:
.... f = y
.... t = time.time() - t
.... return t
....0.055256843566894531

So four global lookups performed 100,000 times takes 0.1 second, while a
single local lookup performed 100,000 times takes 0.05 second. Not a big
difference. Extrapolating from four lookups down to one would suggest
that global lookups are faster than local, which can't be right.

Redoing test_func_overhead to only make one lookup (change the
reference to obj.method.attribute.record to just obj) gives very
surprising results:
0.020340204238891602

According to my testing, looking up a global variable is *faster* than
looking up a local. Hmmmm. Caching effects perhaps?

Try the local version again:
0.016184806823730469

Ah, that's better! At least now the local lookup is slightly faster
than the global. But also widely different from the first run. Function
lookup is so fast to start with, and affected by so many external factors,
that the cost-benefit equation of trying to optimize it is not very good.
There almost certainly will be more useful things for you to spend your
time on.

More comments below:-
log ("about to slip into python feature request mode.")

Maybe this is what the PEP 336 guy was thinking of (Make None Callable).
Obviously we don't want None to be callable, but
what about a "Null" [1] that's callable, with any args ?

But I guess what I am really asking for is something on the bytecode
level that tells the VM to "do nothing".

Here's an idea: make "pass" into an expression (a value) that is callable,
with any args, and returns None.

log ("finished with python feature request mode.")

And how do you use this without the overhead of function calls?

Do you sprinkle your code with:

if DEBUG:
log("spam")
else:
pass("spam")

?

Why not just do this?

if DEBUG: log("spam")

Which is much simpler. Or even simpler still, put the "if DEBUG" test
inside log, since the extra time taken in calling the function is probably
lost in the noise of the rest of your code.




Not related to the logging issue:
I recently discovered "pyc" [2], but i don't quite see how i can
use it while maintaining python source compatability. [snip]
[2]: http://students.ceid.upatras.gr/~sxanth/pyc/

How fascinating. From the pyc page:

"In fact you can use pyc to re-compile your standard library and make it
about 100kB smaller."

Hmmm. Let's see now, Python 2.3.3 on Fedora Core 2:

$ du -hs /usr/lib/python2.3/
88M /usr/lib/python2.3/

Oh joy! So by using pyc, I can save 0.11% of the Python library storage
requirements!

For all I know, pyc is a very useful package, and kudos to the author for
taking the time and effort to develop it. But if this space saving is
"one good reason to use pyc" according to the author, I'm not impressed.
 
B

Bengt Richter

Hi,

I'm after a no-op command, so that i can redirect
logging commands in performance critical code.

Something like this:

def log(*args): print args
def null_log(*args): pass
if not DEBUG: log = null_log

is unacceptable because of the overhead of calling
functions in python.
I think you could make the existence of log calls dependent on
whether you compile with an optimize flag by abusing
an assert statement, e.g.,

assert log(some, args) or True

would always make the call in debug mode, but would never raise the exception
because of the "or True", even if log return None. If you compile with optimization,
the entire assert statement disappears from the byte code, UIAM.

if you use

if __debug__: log(some, args)

I think[1] you still the the if-test code, though that is pretty quick
compared to a function call, so maybe you don't have to worry about it,
unless it is in a super-hot loop.

[1] I thought is saw somewhere that
if __debug__: suite
might be completely optimized away like assert, but I couldn't locate it off hand.
It would seem pretty safe and useful though.

Regards,
Bengt Richter
 
@

@(none)

Bengt said:
I think you could make the existence of log calls dependent on
whether you compile with an optimize flag by abusing
an assert statement, e.g.,

assert log(some, args) or True

This is a session with the -O flag, so asserts disapear:
.... for i in xrange(N): i=0
....
0.821492910385
.... for i in xrange(N): foo()
........ for i in xrange(N): assert log()
....0.61060500144958496

Woohoo!!


Simon.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,222
Members
46,809
Latest member
moe77

Latest Threads

Top