Next Previous Up Top Contents Index

8 Dispatch Optimization Coloring in the Editor

8.3 Optimizing the Reversi application

In this section we look at the dispatch optimization color information for part of the Reversi application and see what we can do to optimize it.

Before doing that, we should build the Reversi application in Production mode so we know that the application has been optimized as much as possible.

1. Open the Reversi project.
2. Choose Project > Settings and, on the Compile page, set the compilation mode to "Production mode".
3. Choose Project > Clean Build.
4. When the build is complete, go to the Sources page and open the file game.dylan.
An editor window showing game.dylan appears.
5. In the editor window, turn on the View > Color Dispatch Optimizations check item.

We can now see color information showing how dispatch optimizations were or were not carried out during the last build.

6. Go to the definition of the method <reversi-game>.
You can use Edit > Find or the "binoculars" toolbar button to do this.

This is the definition of <reversi-game>:

define class <reversi-game> (<object>)
  slot reversi-game-board :: <reversi-board> 
    = make(<reversi-board>);
  slot %player :: <player> = #"black",
    init-keyword: player:;
  slot %players :: <integer> = 1,
    init-keyword: players:;
  slot black-algorithm :: <algorithm> 
    = default-algorithm-for-player(#"black"),
    init-keyword: black-algorithm:;
  slot white-algorithm :: <algorithm> 
    = default-algorithm-for-player(#"white"),
    init-keyword: white-algorithm:;
  slot reversi-game-update-callback :: <function> = always(#f),
    init-keyword: update-callback:;
  slot reversi-game-message-function :: false-or(<function>) 
    = #f, init-keyword: message-function:;
end class <reversi-game>;

There are three different colorings in this definition. The call to the function always, a Dylan language built-in function, is in light gray. That means the call has been eliminated completely from the compiled application. A call to the function always is defined to return a function object that always returns the value passed in the call to always. So here, the function object would always return #f. Unsurprisingly, the compiler evaluated this call completely, avoiding the need for run-time method dispatch.

The two calls to default-algorithm-for-player, a Reversi application method from algorithms.dylan, are colored in blue, signifying that the compiler managed to determine precisely which method to call, and inserted a direct call to that method in the compiled application. Again, the need for run-time method dispatch was averted.

Investigation shows that there is only one method on default-algorithm-for-player, which makes blue optimization simple here. The generic function for default-algorithm-for-player is defined implicitly, in the single define method default-algorithm-for-player call. Recall from the DRM (chapter 6) that implicitly defined generic functions are sealed by default. That fact allows the compiler to conclude that this method is the only method there will ever be on default-algorithm-for-player, making the optimization possible.

The third coloring is magenta, in the call to make on <reversi-board>, in the reversi-game-board slot definition. Here, then, is a generic function call that was not optimized. Magenta coloring means that for this call to make, the compiler could not determine the complete set of methods from which it could attempt to select the appropriate method to call. We will now make changes to the Reversi sources to optimize this call.

The problem here is that the compiler cannot be sure that additional methods on make might not be added at run time. By defining a sealed domain on make for <reversi-board>, we can clear this up.

7. Add the following to game.dylan:
define sealed domain make(subclass(<reversi-board>));

With this information, the compiler knows it has access to the complete set of methods on make for this class, and therefore can attempt to do the method selection itself.

We can recompile the application to see what effect our change has had.

8. Save game.dylan with File > Save.
9. Rebuild the application, and refresh the color information for game.dylan with View > Refresh.

The refreshed coloring shows the call to make on <reversi-board> in the reversi-game-board slot definition in light gray. This coloring means that the compiler determined which make method to call, computed the result of the call--a <reversi-board> object--and inlined the object.

Looking further down game.dylan, notice that the definition of reversi-game-size-setter also calls make on <reversi-board>, a call that is also colored light gray.

We can now look at other possible optimizations in game.dylan.

10. Go to the definition of the method initialize-board.

The definition of initialize-board is:

define method initialize-board (board :: <reversi-board>) => ()
  let squares = reversi-board-squares(board);
  for (square from 0 below size(squares))
    squares[square] := #f
  end;
  for (piece in initial-pieces(board))
    let square = piece[0];
      squares[square] := piece[1]
  end;
end method initialize-board;

In this method there is a green-colored call to reversi-board-squares on the parameter board, an instance of <reversi-board>. Green coloring denotes an access to a slot whose position in a class is fixed. This optimization was possible because the reversi-board-squares method is just the implicitly defined accessor for the slot reversi-board-squares:

define class <reversi-board> (<object>)
  slot reversi-board-size :: <integer> = $default-board-size,
    init-keyword: size:;
  slot reversi-board-squares :: <sequence> = #[];
end class <reversi-board>;

The compiler achieved this optimization because it knew three things. First, it knew that the generic function implicitly defined by the accessor method was sealed. (As normal Dylan methods, accessor methods implicitly define a generic function if one does not already exist; such a generic function is sealed because implicitly defined generic functions are sealed by default.) Second, the compiler knew the type of board in the call to the accessor method. Third, the compiler knew that the class <reversi-board> was sealed, because classes are sealed by default.

We can now move on to some other optimization. The call size(squares) in initialize-board is colored in magenta. There are several similar magenta colorings in game.dylan, where the compiler could not optimize a method call on the value returned from reversi-board-squares: calls to element, element-setter, empty?, and size. In all cases this is because the type of reversi-board-squares is <sequence>, which is an open class.

We could seal domains on <sequence> to get optimizations here. But the DRM defines <sequence> as an open class, and it is not good practice to seal protocols that do not belong to your library or libraries. However, we can change the type of reversi-board-squares to be in a domain which is already sealed. Changing the slot type to <simple-object-vector> gives us a sealed type as well as preserving the protocol in use, so that we do not have to change any of the calls being made.

11. Go to the definition of <reversi-board>.
12. Change the type of reversi-board-squares to be <simple-object-vector>.
13. Save game.dylan with File > Save.
14. Rebuild the application, and refresh the color information for game.dylan with View > Refresh.
15. Go back to the definition of initialize-board.

The size(squares) call is now colored green. Green coloring means the compiler determined that the call was equivalent to a slot access--particularly, an access to slot having a fixed offset from the memory address at which its class is located. The compiler removed the need for run-time method dispatch by replacing the call with code to access the location that would contain the slot value.

This particular optimization was possible because size is a slot accessor for instances of <simple-object-vector>, and, of course, because <simple-object-vector> is sealed.

You could examine the effects of this change on other calls that use the return value of reversi-board-squares. Some calls turn blue. Some calls to element-setter remain magenta because the compiler does not know the type of the index. Constraining the type of the index would improve such a call, turning it blue or even dark gray (inlined).


Getting Started with Functional Developer - 31 MAR 2000

Next Previous Up Top Contents Index