4.6 Derechos Fundamentales en Ecuador
4.3.4. Sospecho, procesado o acusado
4.3.4.1 Sospechoso
The net effect of a program can be said to be the total change which is pro-
duced by executing the program, ignoring the detailed mechanism by which that change is brought about. Optimisation mechanisms concentrate on producing an object program which has the same net effect as that specified by the source program, but which may not produce it by exactly the same means. The net effect of the example program discussed in chapter 1, for example, is to print the digit ‘4’ and it might seem reasonable to translate it into any object program which will produce that effect, perhaps by using the mechanisms of constant folding and deferred storage discussed below.
Figure 10.2 illustrates the notion of net effect with two examples. Example (i) shows how difficult it can be to discover what the net effect of a program is: an optimising compiler might easily discover that j1, j2 and j3 had constant values but might not discover that the loop sets each successive element of the vector Z to zero, and thus that the computation of ln(cos(...).) is never required. Example (ii) shows how optimisation can occasionally produce absurd effects: the assignment statement ‘x:=0’ can be moved outside the loop, the empty loop deleted and the assignment statement itself deleted since the value of ‘x’ is never utilised. The entire program then measures one ten-thousandth part of the time between one call of the system procedure ‘runtime’ and another!
2 Some famous current examples are those programs which aid weather forecasting by simu-
lating atmospheric circulation and which, rumour has it, can only just run faster than the real thing.
10.1. NET EFFECT AND ORDER OF COMPUTATION 171
(i) A foolish way to zero a vector:
begin array Z[1:20]; integer i, j1, j2, j3; j3 := 20; j1 := (j2+2)/10; j2 := j1-1; Z[1] := 0.0;
for i := j1 step j2 until j3 do
Z[i] := Z[i-1] * ln(cos(i)+1.14); ....
end
(ii) An ineffective attempt to measure object program speed: ....
start := runtime()
for i := 1 to 10000 do x := 0 finish := runtime()
print("time for assignment statement",
(finish-start)/10000);
Figure 10.2: Net effect of computation
Source: a := x-3; b := x*2; c := x**3; x := a+b*c
Initial order: x-3, a:=, x*2, b:=, x**3, c:=, a+b*c, x:=
Essential order: x-3 (#1) x*2 (#2) x**3 (#3)
#1+#2*#3 (#4)
a:= (#5) x:=(#6) c:= (#7) b:= (#8) Figure 10.3: Initial and essential orders of events
172 CHAPTER 10. CODE OPTIMISATION Joking apart, all optimisations consider the net effect first of a small fragment of object program, then of progressively larger fragments, building up perhaps to the translation of a fragment as large as an entire procedure body and even- tually producing a translation which preserves the net effect specified by the source program without necessarily following the precise details of its algorithm. Geschke (1972) points out that such a translation depends on the recognition of the essential order of computation events out of the initial order specified by the user’s program. Figure 10.3 shows an example of the difference between initial and essential order of events.
The essential order shows theabsenceof any ordering between many computa- tions. In the example of figure 10.3 events #1, #2 and #3 can occur in any order provided that they all occur before event #4. Events #5, #6, #7 and #8 can likewise occur in any order provided that in each case the assignment follows the relevant event #1, #2, #3 or #4. It is the absence of an essential ordering between many events which makes optimisation possible: the optimiser can re- organise the computation freely provided that the actual order of events obeys the constraints of the essential order. Every optimisation mechanism discussed in this chapter depends on this insight and on the manipulation of redundancies in the object program which it reveals. The essential order of computation can be envisaged as a collection of threads running through the tree, and optimisa- tion as a process of linearisation which preserves continuity of every thread and produces efficient translation by merging portions of different threads.
10.1.1 Basic blocks, regions and structured statements
The smallest unit of a program which can be optimised is a basic block: a collection of statements which can only be entered at the beginning and which doesn’t contain any intermediate labels, procedure or function calls.3 Within
a basic block the initial order of events is obvious and linear. Provided that when control leaves the basic block the net effect which has been produced is that specified by the source program, the user has no way of telling whether the program has been optimised or not.4 Therefore the optimiser can generate
code which produces that net effect in any way it wishes – naturally the aim is to produce the net effect with the minimum of run-time effort!
Since the initial order of events within the basic block is linear and predictable, optimisation can include simple techniques which note the effect of assignment statements in the symbol table descriptors of variables – the ‘constant folding’ and ‘deferred storage’ mechanisms discussed below, for example – and which move code fragments implicitly within the basic block rather than explicitly.
3 The terminology ‘basic block’ comes from FORTRAN and has nothing whatsoever to do
with block-structuring.
4 That is, provided that the source program obeys all the restrictions imposed on programs
by the language definition. If it does not, the result of optimisation may not be so obviously beneficial.
10.1. NET EFFECT AND ORDER OF COMPUTATION 173 Source: a := 3; if a<b then { x := a; y := x-1 } else { x := a; y := x+1 } a := 0; x := 0 Initial order: a := 3 a<b x := a y := x-1 x := a y := x+1 a := 0 x := 0 Optimised order: 3<b y := 2 y := 4 a := 0 x := 0
Figure 10.4: Structured statements and code motion
Unstructured program: With explicit iteration:
loop: <S1> { <S1>; <S2>; b := next(b) } <S2> repeatwhile f(b)
b := next(b)
if f(b) then goto loop
174 CHAPTER 10. CODE OPTIMISATION The end of a basic block is signalled by a labelled statement, a procedure or a function call. In principle control can arrive at a label from any number of different control contexts and therefore simple optimisations must be aban- doned when a label is encountered – constant values must be forgotten, deferred STORE instructions generated, and so on. Likewise the effects of a procedure call can be far-reaching and simple optimisations must be abandoned before the procedure is called. Nevertheless the optimisation of a basic block can be very effective, for example when it eliminates the re-calculation of an array element address.
With extra effort it is possible to analyse the ways in which control is passed between the basic blocks of the program, to discover the regions of the source program. Analysis of a basic block will reveal fragments which can be moved between the blocks of a region and thus the range and effectiveness of optimi- sation can be increased. The structured statements of a modern programming language provide the optimiser with ready-made regions: figure 10.4 shows how code motion between basic blocks can be effective in the optimisation of a con- ditional statement. It would be simple to discover the structure of the region given a threaded tree, less simple if the representation of the program was a linear one using compiler-generated labels and JUMPs.
Code motion between basic blocks within a region is made possible by the recognition of the essential order of computation of code fragments. Fragments which have no essential predecessor in the basic block may be moved into the preceding basic blocks of the region, fragments which have no essential successor into the following ones. Code motion is particularly effective in the case of a loop, because code within the loop will probably be executed many times while code outside will be executed perhaps only once. Figure 10.5 illustrates the advantages of explicit iterative statements: it is obvious that optimisation of the body of arepeatwhilestatement may have quite an effect on the efficiency of the object program, but it can be difficult to discover the effective loops in an unstructured program. Structured statements are easier to translate than their unstructured equivalent, as chapter 7 demonstrates: figure 10.5 shows that they are easier to optimise as well!