7.2Getting the most out of the optimizer

Typed Racket’s optimizer can improve the performance of various common
Racket idioms. However, it does a better job on some idioms than on
others. By writing your programs using the right idioms, you can help
the optimizer help you.

To best take advantage of the Typed Racket optimizer, keep the following in
mind. The Optimization Coach package provides optimization coaching
support to help you in this task.

7.2.1Numeric types

Being type-driven, the optimizer makes most of its decisions based on
the types you assigned to your data. As such, you can improve the
optimizer’s usefulness by writing informative types.

However, the second one uses more informative types: the
Float type includes only 64-bit floating-point numbers
whereas the
Real type includes both exact and
inexactreal numbers
and the Inexact-Real type includes both 32- and 64-bit
floating-point numbers.
Typed Racket’s optimizer can optimize the latter program to use
float
-specific operations whereas it cannot do anything with the
former program.

Thus, to get the most of Typed Racket’s optimizer, you should use the
Float type when possible. For similar reasons, you should use
floating-point literals instead of exact literals when doing
floating-point computations.

When mixing floating-point numbers and exact reals in arithmetic
operations, the result is not necessarily a Float. For
instance, the result of (*2.00) is 0 which is not
a Float. This can result in missed optimizations. To prevent
this, when mixing floating-point numbers and exact reals, coerce exact
reals to floating-point numbers using exact->inexact. This is
not necessary when using + or -. When mixing
floating-point numbers of different precisions, results use the
highest precision possible.

On a similar note, the Float-Complex type is preferable to
the Complex type for the same reason. Typed Racket can keep
float
complex numbers
unboxed; as such, programs using
complex numbers
can have better performance than equivalent programs that
represent
complex numbers
as two
real numbers.
As with floating-point literals, float
complex
literals (such as 1.0+1.0i) should be preferred over exact
complex
literals (such as 1+1i). Note that both parts of a literal must be
present and
inexact
for the literal to be of type
Float-Complex; 0.0+1.0i is of type
Float-Complex but 0+1.0i is not.
To get the most of
Typed Racket’s optimizer, you should also favor rectangular
coordinates over polar coordinates.

7.2.2Lists

Typed Racket handles potentially empty lists and lists that are known
to be non-empty differently: when taking the car or the
cdr of a list Typed Racket knows is non-empty, it can skip
the check for the empty list that is usually done when calling
car and cdr.

In this example, Typed Racket knows that if we reach the else branch,
l is not empty. The checks associated with car and
cdr would be redundant and are eliminated.

In addition to explicitly checking for the empty list using
null?, you can inform Typed Racket that a list is non-empty
by using the known-length list type constructor; if your data is
stored in lists of fixed length, you can use the List type
constructors.

For instance, the type of a list of two Integers can be
written either as:

7.2.3Vectors

In addition to known-length lists, Typed Racket supports known-length
vectors through the Vector type constructor. Known-length
vector access using constant indices can be optimized in a similar
fashion as car and cdr.

In many such cases, however, structs are
preferable to vectors. Typed Racket can optimize struct access in all
cases.

7.2.4Contract boundaries

When interoperating with untyped code (see Typed-Untyped Interaction),
contracts are installed between typed and untyped modules. Contracts can have
significant overhead, thus typed-untyped boundary crossings should be avoided
in performance-sensitive code.

Typed Racket provides types for most of the bindings provided by #lang
racket; using require/typed is unnecessary in these cases.

If you suspect that contracts at a typed-untyped boundary may be have a
significant cost in your program, you can investigate further using the
contract profiler.

If the contract profiler is not already installed, the following command
will install it: