Eiffel compilation and tuning execution speed

by Martin Seiler (modified: 2007 Apr 05)

Introduction

For a beginner the different compilation modes of EiffelStudio can be confusing and I've heard people mention that they don't want to use Eiffel because it's runtime execution is considered too slow. The fact of the matter is, it is not slow! It is only slow when using all or the most aggressive contracts in "workbench" mode, which is by no means the result of an optimized final compilation. Such aggressive checking is sometimes what is needed when developing a new project, and sometime you can throttle back on such aggression. This article explains the various compilation modes and their pros and cons. The results will demonstrate that Eiffel is just as fast as C or C++.

Compilation Overview

EiffelStudio supports two major back-ends for code generation:

  • compilation to native code by using C as a portability layer and is called Classic Compilation.
  • compilation to MSIL byte-code is better known as .NET Compilation

Classic Compilation

Classic compilation itself consists of different sub modes which are:

  • Workbench code, which consists either of
    • Melted code or
    • Frozen code
  • Finalized code

Melted and frozen compilation is used in symbiosis with the EiffelStudio workbench. The difference between melted and frozen code is mainly their implementation which gives them different properties:

  • melted is implemented as a form of byte code and
  • frozen is C code augmented with support for tools like the debugger

This means that frozen code needs at one point the C compiler to transform it into an executable format while the byte code relies on the runtime interpreter and therefore needs no additional compilation step.

The finalized mode compiles to C code like the frozen code but it is an optimized version of C code. As it is C code one needs again a C compilation step to actually get an executable program. It is this mode which gives a fantastic boost to the execution speed of Eiffel programs. The speed is comparable to C/C++.

Now one might have the following question:

Why is a frozen executable slower than a finalized one?

It is because of various reasons:

  • Frozen executables support the transition to byte code, which is then executed by the interpreter of the run-time.
  • Frozen executables contain support code for EiffelStudio tools like the debugger.
  • Frozen code in general, as is melted code, is designed to support flexibility, not speed in the first place.

How To Use The Various Compilation Possibilities

First we summarize the properties of the two workbench codes and then we have a look at finalized code:

Melted code

  • as it is byte code which is interpreted and therefore slower than C based code
  • does not need a C compilation step, therefore is the fastest to compile
  • it can switch to frozen C code
  • it has support for tools like the debugger and the profiler

Frozen code

  • is C code and therefore faster than interpreted melted code
  • is slower than finalized code because it is augmented with support code for
    • switching back and forth to melted code (i.e. it has the ability to call the interpreter) and
    • tools like the debugger.

Finalized code

  • is C code and therefore faster than interpreted melted code
  • is optimized
    • dynamic binding of feature calls is faster than in every other mode
    • static binding is applied if possible, thus saving the cost for dynamic dispatch
    • dead code removal and thus reducing the code size
    • it has no support for the EiffelStudio tools like the debugger (but it can be debugged by using a C debugger like gdb or VisualStudio)

As you can see, all modes have their trade-offs: Finalized is fast, Melted and Frozen can be debugged and melted code is quickly compiled.

So what you do is the following: You use melted/frozen compilation as the duo to develop your program. Typically you first select a precompile you want (like Eiffel Base) and freeze (meaning you generate frozen code) your program. Then you start adding, changing features and continue to melt them into your system (for example by hitting F7). By doing so you spend some initial time compiling the frozen C code. But after that you spend very little time compiling your program, as melted code is generated and your changes can immediately be tested. From time to time you want to refreeze your system to ensure that execution speed stays on a reasonable level. Do this maybe at the end of a working day or when you go for lunch.This works especially well for very large projects.

Once you have finished developing and you want to distribute your program you hit the finalize button and wait until EiffelStudio has generated an optimized native binary for you.

.NET Compilation

For .NET there is only one code generation. There are no different compilation modes as .NET has itself a different architecture: In .NET the MSIL code gets (pre) jitted into native code on demand and is thereafter cached. There is not much you can do about it except the points which are mentioned in the next section.

Tuning Execution Speed by Disabling Contracts

While debugging usually you leave as many assertions enabled as possible, as your goal is to find bugs. If you find it being too slow consider first deactivating the invariants as they are the most expensive to compute from all assertions. If you have algorithms which use a lot of computational power and are currently not under active debugging you will want to consider enabling/disabling assertions per cluster level. A very nice feature if you work with ECF libraries is that you can switch on supplier preconditions which only checks calls into the library but not calls which are made internally in the library.

If your write contracts over complex data structures it can very well be that the evaluation of the contract might become more expensive than the computation which is actually performed by the called feature.

Conclusion

So by knowing how to harness the benefits of the different compilation back-ends of EiffelStudio and by being aware what performance impact the contracts can have, you should now be able to identify the bottleneck in your program and decide, whether or not you want to make the trade towards more speed and how you want to achieve it.

If you want to know more details make sure to checkout the two references I attached.