******************************************** Dispatch Optimization Coloring in the Editor ******************************************** This chapter is about source-code coloring in the editor that shows where and how optimizations have taken place. .. note:: Optimization work is best done in Production mode. See :ref:`project-settings`. .. index:: single: applications; optimization single: optimization; coloring in the editor single: dispatch optimizations single: optimization; dispatch About dispatch optimizations ============================ When you call a generic function in Dylan, the method that will be executed has to be selected from the set of methods defined on that generic function. The method is selected by comparing the types of the arguments passed in the generic function call to the parameter lists of the methods available; the method whose parameter list is closest to the types of the arguments passed in the call is the one that will be selected. (Note that there may be situations where no one method is more applicable than another, or even where there is no applicable method at all. These situations, which may be detected either at compile time or at run time, signal an error.) The process of selecting the right method to call is known as *method dispatch*. The algorithm for selecting a method is described in Chapter 6 of the DRM. Method dispatch can, in principle, occur entirely at run time; but in some circumstances, the Open Dylan compiler can work out at compile time precisely which method needs to be called, and so optimize away the need for run-time dispatch, making delivered applications faster. Depending on the circumstances, these *dispatch optimizations* can consist of replacing the generic function call with a direct call to the correct method; replacing the generic function call with a class slot access; or inlining the call completely. .. index:: Color Dispatch Optimizations menu command Optimization coloring ===================== When you compile a project, the Open Dylan compiler records the kinds of optimizations it performed for each source code file in the project. It also records cases where compile-time optimization was for one reason or another not possible. The Open Dylan editor provides a way to see this information, by choosing **View > Color Dispatch Optimizations**. This command colors a source code file so that you can see where the compiler managed to optimize method dispatch, and also places where you may be able to make changes that will make dispatch optimizations possible next time you compile the project. For instance, you will see a generic function call colored in blue where the compiler worked out the exact method to call and emitted a direct call to that method in the compiled code. :ref:`dispatch-optimization-colors` contains a full list of colors and their meanings. Editing colored source code --------------------------- Dispatch optimization coloring in a file shows only what the compiler achieved the last time you compiled the project containing the file. If you edit colored source code, the changes you make could affect the optimizations the compiler can carry out upon it the next time you compile the project. For this reason, when **View > Color Dispatch Optimizations** is on and you edit a colored line, the editor resets the entire line to black. All the other lines in the file keep their color. In this situation, you can re-color the line by doing **View > Refresh**. Note that once you have edited the text, the coloring information may no longer fit it, but you may still find it useful. Alternatively you may prefer to turn off coloring altogether once the coloring information for part of the file has been invalidated. Effect of compilation mode on dispatch optimizations ---------------------------------------------------- The Open Dylan compiler tries to optimize method dispatch whether compiling a project in Interactive Development mode or in Production mode. Because the compiler does more optimization in Production mode, you will see more coloring information after a Production-mode build. .. _dispatch-optimization-colors: Dispatch optimization colors and their meanings ----------------------------------------------- The following table shows the colors used to indicate different kinds of dispatch optimization. +-------------+-------------------------------+--------------------------+ | Color | Meaning | Recommended Action | +=============+===============================+==========================+ | Magenta | Call not optimized because | Happens when the generic | | | the compiler could not | function is open. Where | | | determine all the applicable | possible, turn open | | | methods. | protocols into sealed | | | | protocols. | +-------------+-------------------------------+--------------------------+ | Red | Call not optimized despite | Where possible, add type | | | the compiler finding all the | specializers to the | | | applicable methods. | bindings of the | | | | arguments to the call. | | | Can happen when the type of | | | | an argument is not specific | | | | enough. | | +-------------+-------------------------------+--------------------------+ | Blue | Call optimized. The compiler | None required. | | | found the appropriate method, | | | | and emitted a direct call to | | | | it. | | +-------------+-------------------------------+--------------------------+ | Green | Call optimized. The compiler | None required. | | | found that this call was an | | | | access to a slot whose | | | | position in a class is fixed. | | | | It replaced the function call | | | | with (faster) code to access | | | | that slot value. | | +-------------+-------------------------------+--------------------------+ | Dark Gray | Call optimized. The compiler | None required. | | | inlined the code of the | | | | appropriate method. | | +-------------+-------------------------------+--------------------------+ | Light Gray | This code was eliminated | None required. (Unless | | | completely. The compiler | the code should have | | | either determined that this | been called.) | | | call would never be made or | | | | that it would not make any | | | | difference to the outcome of | | | | other code with which it was | | | | associated, or it managed to | | | | evaluate the call directly. | | +-------------+-------------------------------+--------------------------+ Where possible, add type specializers to the bindings of the arguments to the call. 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. Open the Reversi project. #. Choose **Project > Settings** and, on the Compile page, set the compilation mode to "Production mode". #. Choose **Project > Clean Build**. #. When the build is complete, go to the Sources page and open the file *game.dylan*. An editor window showing *game.dylan* appears. #. 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. #. Go to the definition of the method ````. You can use **Edit > Find** or the "binoculars" toolbar button to do this. This is the definition of ```` : .. code-block:: dylan define class () slot reversi-game-board :: = make(); slot %player :: = #"black", init-keyword: player:; slot %players :: = 1, init-keyword: players:; slot black-algorithm :: = default-algorithm-for-player(#"black"), init-keyword: black-algorithm:; slot white-algorithm :: = default-algorithm-for-player(#"white"), init-keyword: white-algorithm:; slot reversi-game-update-callback :: = always(#f), init-keyword: update-callback:; slot reversi-game-message-function :: false-or() = #f, init-keyword: message-function:; end class ; 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 ````, 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 ````, we can clear this up. #. Add the following to *game.dylan* : .. code-block:: dylan define sealed domain make(subclass()); 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. #. Save *game.dylan* with **File > Save**. #. Rebuild the application, and refresh the color information for *game.dylan* with **View > Refresh**. The refreshed coloring shows the call to ``make`` on ```` 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 ```` object — and inlined the object. Looking further down *game.dylan*, notice that the definition of *reversi-game-size-setter* also calls ``make`` on ````, a call that is also colored light gray. We can now look at other possible optimizations in *game.dylan*. #. Go to the definition of the method *initialize-board*. The definition of *initialize-board* is: .. code-block:: dylan define method initialize-board (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 ````. 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* : .. code-block:: dylan define class () slot reversi-board-size :: = $default-board-size, init-keyword: size:; slot reversi-board-squares :: = #[]; end class ; 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 ```` 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 :drm:``, which is an open class. We could seal domains on :drm:`` to get optimizations here. But the DRM defines :drm:`` 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 :drm:`` 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. #. Go to the definition of ````. #. Change the type of *reversi-board-squares* to be :drm:``. #. Save *game.dylan* with **File > Save**. #. Rebuild the application, and refresh the color information for *game.dylan* with **View > Refresh**. #. 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 a 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 :drm:``, and, of course, because :drm:`` 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).