True, False, None (was re. Pruss's manifesto)
Ron Adam
radam2 at tampabay.rr.com
Mon Nov 10 19:10:13 EST 2003
On Mon, 10 Nov 2003 22:11:53 GMT, Alex Martelli <aleax at aleax.it>
wrote:
>Ron Adam wrote:
> ...
>> Is it possible to make the argument optional for while? That may
>
>Yes, it is technically possible -- you'd need to play around with the
>Dreaded Grammar File, but as Python grammar tasks go this one seems
>simple.
>
>> allow for an even faster time?
>
>No, there is no reason why "while 1:", "while True:" once True is
>a keyword, and "while:" were the syntax extended to accept it, could
>or should compile down to code that is at all different.
>
>> while:
>> <instructions>
>>
>> It would be the equivalent as making the default argument for while
>> equal to 1 or True. Could it optimize to single cpu instruction when
>> that format is used? No checks or look ups at all?
>
>"while 1:" is _already_ compiled to a single (bytecode, of course)
>instruction with "no checks or look ups at all". Easy to check:
>
>>>> x=compile('while 1: foop()', '<str>', 'exec')
>>>> dis.dis(x)
> 1 0 SETUP_LOOP 19 (to 22)
> 3 JUMP_FORWARD 4 (to 10)
> 6 JUMP_IF_FALSE 11 (to 20)
> 9 POP_TOP
> >> 10 LOAD_NAME 0 (foop)
> 13 CALL_FUNCTION 0
> 16 POP_TOP
> 17 JUMP_ABSOLUTE 10
> >> 20 POP_TOP
> 21 POP_BLOCK
> >> 22 LOAD_CONST 1 (None)
> 25 RETURN_VALUE
>
>the four bytecodes from 10 to 17 are the loop: name 'foop'
>is loaded, it's called with no argument, its result is
>discarded, and an unconditional jump back to the first of
>these steps is taken (this loop, of course, will only get
>out when function foop [or the lookup for its name] cause
>an exception). (The 2 opcodes at 6 and 9 never execute,
>and the opcode at 3 could be eliminated if those two were,
>but that's the typical kind of job for a peephole optimizer,
>an easy but low-returns project).
>
>Note the subtle difference when we use True rather than 1:
>
>>>> x=compile('while True: foop()', '<str>', 'exec')
>>>> dis.dis(x)
> 1 0 SETUP_LOOP 19 (to 22)
> >> 3 LOAD_NAME 0 (True)
> 6 JUMP_IF_FALSE 11 (to 20)
> 9 POP_TOP
> 10 LOAD_NAME 1 (foop)
> 13 CALL_FUNCTION 0
> 16 POP_TOP
> 17 JUMP_ABSOLUTE 3
> >> 20 POP_TOP
> 21 POP_BLOCK
> >> 22 LOAD_CONST 0 (None)
> 25 RETURN_VALUE
>
>_Now_, the loop runs all the way through the bytecodes
>from 3 to 20 included (the opcodes at 0 and 21 surround
>it just like they surrounded the unconditional loop we
>just examined). Before we can get to the "real job" of
>bytecodes 10 to 17, each time around the loop, we need
>to load the value of name True (implying a lookup), do
>a conditional jump on it, otherwise discard its value.
>
>If True was a keyword, the compiler could recognize it and
>generate just the same code as it does for "while 1:" --
>or as it could do for "while:", were that extension of
>the syntax accepted into the language.
>
>
>As to the chances that a patch, implementing "while:" as
>equivalent to "while 1:", might be accepted, I wouldn't
>be particularly optimistic. Still, one never knows!
>
>
>Alex
Thanks for the detailed explanation. I would only suggest 'while:' if
there was a performance advantage. Since there is none, the only
plus is it save two keystrokes. That isn't reason enough to change
something that isn't broken.
Thanks for showing me how to use compile() and dis().
>>> x=compile('while 1: pass','<str>','exec')
>>> dis.dis(x)
1 0 SETUP_LOOP 12 (to 15)
3 JUMP_FORWARD 4 (to 10)
6 JUMP_IF_FALSE 4 (to 13)
9 POP_TOP
>> 10 JUMP_ABSOLUTE 10
>> 13 POP_TOP
14 POP_BLOCK
>> 15 LOAD_CONST 1 (None)
18 RETURN_VALUE
Yes, this is as short as it gets. :-)
In the case of using 'while 1:' it seems to me you end up loosing
what you gain because you have to test inside the loop which is more
expensive than testing at the 'while' statement.
>>> x=compile('a=1\nwhile a:\n a=0','<str>','exec')
>>> dis.dis(x)
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (a)
2 6 SETUP_LOOP 18 (to 27)
>> 9 LOAD_NAME 0 (a)
12 JUMP_IF_FALSE 10 (to 25)
15 POP_TOP
3 16 LOAD_CONST 1 (0)
19 STORE_NAME 0 (a)
22 JUMP_ABSOLUTE 9
>> 25 POP_TOP
26 POP_BLOCK
>> 27 LOAD_CONST 2 (None)
30 RETURN_VALUE
>>> x=compile('a=1\nwhile 1:\n a=0\n if a==0:\n break','<str>','exec')
>>> dis.dis(x)
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (a)
2 6 SETUP_LOOP 36 (to 45)
>> 9 JUMP_FORWARD 4 (to 16)
12 JUMP_IF_FALSE 28 (to 43)
15 POP_TOP
3 >> 16 LOAD_CONST 1 (0)
19 STORE_NAME 0 (a)
4 22 LOAD_NAME 0 (a)
25 LOAD_CONST 1 (0)
28 COMPARE_OP 2 (==)
31 JUMP_IF_FALSE 5 (to 39)
34 POP_TOP
5 35 BREAK_LOOP
36 JUMP_ABSOLUTE 9
>> 39 POP_TOP
40 JUMP_ABSOLUTE 16
>> 43 POP_TOP
44 POP_BLOCK
>> 45 LOAD_CONST 2 (None)
48 RETURN_VALUE
>>>
This requires twice as many instructions, 12 vs 6. So it's probably
better to avoid 'while 1:' or 'while True': if possible. But it is
nice to know 'while 1:' is 20% faster than 'while True:' in those
situations where you need to use it.
_Ron Adam
More information about the Python-list
mailing list