hehehehehehe...

On 17/12/2010 2.01, Steven D'Aprano wrote:
On Thu, 16 Dec 2010 23:34:21 +0000, BartC wrote:

In terms of a more realistic function (admittedly still a little
contrived, as the loop would be written differently), I tried this:

def p2(n):
   p=1
   while True:
     if n<=p: return p
     p<<=1
   return 0

for i in xrange(1000000):
   x=p2(i)

p2() calculates the smallest power of 2>= it's operand.

Using while True as shown, it took 3.4 seconds. Using While 1, it took
2.6 seconds (Python 2.5).


Right. And a saving of 0.8 microseconds per iteration is a micro-
optimization which is likely to be invisible in any real situation.

I mean, yes, you saved almost an entire second. Wow. Save another 179 of
them and you'll almost have enough time to make yourself a coffee.

Bart, we get it. Nobody denies that the optimization is real, only that
it is generally meaningful. Who cares whether it takes 2 seconds or 4
seconds to generate one million results if the rest of the application
takes 3 minutes to run?

*If* your application is such that saving 0.8 microseconds per iteration
actually is useful, AND your loop has to be written as a while True loop,
then this *may* be a useful micro-optimization to save 0.8 microseconds
per iteration. That's a vanishingly tiny proportion of all code written.
If your code happens to meet those conditions, then by all means use
"while 1". Or move to Python 3, where "while True" has the same
optimization performed.

But in general, such micro-optimizations are not terribly useful. If you
shave off 1 second off a program that runs in 3 seconds, chances are
nobody is even going to notice. Two seconds or three, who cares? Either
way, it's too short to do anything else, and not long enough to matter.
If you shave off an hour off a program that takes 20 hours, who is going
to care?

But so long as it doesn't introduce bugs, or make maintenance harder, or
add complexity, such micro-optimizations don't harm either.


HEY! That was MY argument! ;-))

Newsgroups: comp.lang.python
Subject: Re: If/then style question
Date: Tue, 21 Dec 2010 20:54:02 +0100

I'd bet you would stress your point Steven! But you don't need to persuade me, 
I do already agree.
I just meant to say that, when the advantage is little, there's no need to 
rewrite a working function.
And that with modern CPUs, if tests take so little time, that even some 
redundant one is not so much of a nuisance.
in your working example, the "payload" is just a couple of integer calculations, that take very little time too. So the overhead due to redundant if tests does show clearly. And also in that not-really-real situation, 60% overhead just meant less than 3 seconds. Just for the sake of discussion, I tried to give both functions some plough to pull, and a worst-case situation too:

>>> t1 = Timer('for x in range(100): print func1(0),',
...  'from __main__ import func1')
>>>
>>> t2 = Timer('for x in range(100): print func2(0),',
...  'from __main__ import func2')
>>>
>>> min(t1.repeat(number=1, repeat=1))
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1
53.011015366479114
>>> min(t2.repeat(number=1, repeat=1))
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1
47.55442856564332

that accounts for a scant 11% overhead, on more than one million tests per 
cycle.

That said, let's make really clear that I would heartily prefer func2 to func1, based both on readability and speed. Thank you for having spent some time playing with me!
Francesco

On 19/12/2010 1.05, Steven D'Aprano wrote:
> Well, let's try it with a working (albeit contrived) example. This is
> just an example -- obviously I wouldn't write the function like this in
> real life, I'd use a while loop, but to illustrate the issue it will do.
>
> def func1(n):
>      result = -1
>      done = False
>      n = (n+1)//2
>      if n%2 == 1:
>          result = n
>          done = True
>      if not done:
>          n = (n+1)//2
>          if n%2 == 1:
>              result = n
>              done = True
>      if not done:
>          n = (n+1)//2
>          if n%2 == 1:
>              result = n
>              done = True
>      if not done:
>          for i in range(1000000):
>              if not done:
>                  n = (n+1)//2
>                  if n%2 == 1:
>                      result = n
>                      done = True
>      return result
>
>
> def func2(n):
>      n = (n+1)//2
>      if n%2 == 1:
>          return n
>      n = (n+1)//2
>      if n%2 == 1:
>          return n
>      n = (n+1)//2
>      if n%2 == 1:
>          return n
>      for i in range(1000000):
>          n = (n+1)//2
>          if n%2 == 1:
>              return n
>      return -1
>
>
> Not only is the second far more readable that the first, but it's also
> significantly faster:
>
>>>> from timeit import Timer
>>>> t1 = Timer('for i in range(20): x = func1(i)',
> ... 'from __main__ import func1')
>>>> t2 = Timer('for i in range(20): x = func2(i)',
> ... 'from __main__ import func2')
>>>> min(t1.repeat(number=10, repeat=5))
> 7.3219029903411865
>>>> min(t2.repeat(number=10, repeat=5))
> 4.530779838562012
>
> The first function does approximately 60% more work than the first, all
> of it unnecessary overhead.


--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to