Which happens first?
Carlos Alberto Reis Ribeiro
cribeiro at mail.inet.com.br
Sat Apr 7 21:42:24 EDT 2001
At 19:41 06/04/01 +0500, you wrote:
>I know that this may be a dumb questio, yet I am new to programming and I
>am courious in a mathmatical expression such as ( 16 * ( 15j + ( 4.2 -
>3.9 ) + 20 ) / -2 ) % 5 which happens first is the () or is it the
>conversion to the complex number?? I would think it would be the ()
<WARNING> I you are *really* new to programming, the following advice may
look like overkill. But it does answer your question, while at the same
time raising others...</WARNING>
Having learned recently about the dis module (thanks to Robin :-) I set out
to find how this expression was compiled. I am using Python 2.0, and I
believe that a lot of optimizations are being included by the Python gurus.
Anyway, what I discovered worried me little bit. This is the code for the
expression above:
>>> p = compile('( 16 * ( 15j + ( 4.2 - 3.9 ) + 20 ) / -2 ) % 5',
'<string>', 'single')
>>> dis.disassemble(p)
0 SET_LINENO 0
3 SET_LINENO 1
6 LOAD_CONST 0 (16)
9 LOAD_CONST 1 (15j)
12 LOAD_CONST 2 (4.2000000000000002)
15 LOAD_CONST 3 (3.8999999999999999)
18 BINARY_SUBTRACT
19 BINARY_ADD
20 LOAD_CONST 4 (20)
23 BINARY_ADD
24 BINARY_MULTIPLY
25 LOAD_CONST 5 (2)
28 UNARY_NEGATIVE
29 BINARY_DIVIDE
30 LOAD_CONST 6 (5)
33 BINARY_MODULO
34 PRINT_EXPR
35 LOAD_CONST 7 (None)
38 RETURN_VALUE
1) At [15], we have the widely known floating point representation problem.
3.9 is not exactly repreentable in binary floating point notation, so the
3.899... (we all know, it's not a Python problem. Just to shown that it
happens).
2) The complex constant is recognized by the compiler. There is no
intermediate step - no call to make a number "complex". This is good.
3) The complex number is only used at the BINARY_ADD call at line 19. There
is only one operation being evaluated before [23] (namely, [18], that is
the subtraction between parenthesis). So the expression is calculated as a
complex number from this point on.
4) Now the ugly part. At [25], we load a constant (with the value 2), and
then do a unary minus operation. The question is, does it really need to be
so? There is any good reason not to optimize this, making the conversion to
the negative value in the compiler?
5) Other strange thing: at least in this example, the compiler did not made
any optimization on constant expressions. This is evident by the fact that
the entire expression is shown step by step. I sincerely don't know if the
compiler's behavior would be different if I was loading, let us say, a
source file or a module; however, I think that this is not going to make
difference. Only a careful study of the parser/compiler implementation can
show the truth.
My personal conclusions are as follows. Please feel free to disagree and/or
clarify:
1) Avoid any unnecessary calculation, specially inside tight loops. I tend
to use lots of explicit calculations, in order to make code easier to
understand. Things like adding/subtracting one, or calculating an offset
(for a struct, for instance) are common applications of this idiom, for
example:
>>> p = a[(3*4+2)-1] # get the second byte of the third DWORD
This kind of construct should be avoided.
2) If your expression involves value of mixed types, check *carefully* the
sequence of the calculation. It is possible to optimize the evaluation a
little bit by moving the more complex (and slower) types to the end of the
evaluation.
3) The opposite may also be true; in some cases, the cost of the coercion
may be higher than forcing the right types in the expression:
>>> q = compile('1*2.0', '<string>', 'single')
>>> dis.disassemble(q)
0 SET_LINENO 0
3 SET_LINENO 1
6 LOAD_CONST 0 (1)
9 LOAD_CONST 1 (2.0)
12 BINARY_MULTIPLY
13 PRINT_EXPR
14 LOAD_CONST 2 (None)
17 RETURN_VALUE
>>>
There will be a intermediate step while executing step 12
(BINARY_MULTIPLY), because the first parameter is an integer that needs to
be converted to a float. It is going to be more efficient to write the
expression as 1.0*2.0, so this conversion is not needed.
I think that this may be turned into a recipe...
Carlos Ribeiro
More information about the Python-list
mailing list