📜 ⬆️ ⬇️

What are optimizing compilers for?

image


Today, almost all compilers that can be found on the market are optimizing. Those. they do not just translate the code written by the programmer into machine language, but can also improve it in terms of performance or volume.

But what exactly can a compiler do to improve the code?
')
First of all, the following three things:
  1. compiler can effectively implement programming language tools
  2. can maximize the capabilities of the equipment on which the program will be executed
  3. and also eliminate some drawbacks of the algorithm implemented by the programmer.

Consider each of these features in more detail.

Modern programming languages ​​provide the programmer with powerful tools for expressing an algorithm. However, if these tools are poorly supported at the compilation level, then the convenience of high-level languages ​​can result in serious performance losses.

Consider as an example a call to a virtual function in the C ++ language:
class A { public: virtual int func () { return 5; } }; class B : public A { public: virtual int func () { < >; } }; A* ptr = new A(); int value = ptr->func(); 


Here we have two classes: A and B. Class A contains the simplest virtual function that always returns a constant (the number “five”). Class B inherits class A and in doing so overrides a virtual function, so now it performs some complex calculations.

If the virtual functions mechanism is weakly supported in the compiler (Update: in this place it is not meant that some compilers support C ++ language tools more completely than others. This means that some compilers are able to support these tools with much less overhead than Others. See a detailed analysis of the example in the comments to the record), the compiler will not be able to understand which version of the virtual function should be called in the expression int value = ptr-> func (); However, a good compiler will be able to determine that it is the simplest function that needs to be called (that is, only an instance of class A is addressed via the “ptr” pointer). With that said, the last expression can be optimized as follows: int value = 5;

The given example also shows that there is no sense in talking about the absolute effectiveness of a certain programming language. A specific implementation of this language in the compiler must always be implied. For example, the question “Which is faster, C ++ or Java?” Is not entirely correct. A poorly implemented C ++ can lose to a good Java implementation, and vice versa a good C ++ compiler can give a huge head start to a bad Java compiler.

Thus, we see that the power of modern programming languages ​​in the expression of algorithms is justified only when there are effective implementations of these languages ​​at the compilation level.

Let us now consider the second opportunity that optimizing compilers provide the programmer, namely the ability to maximize the use of hardware resources.

Let's address once again to modern programming languages. Languages ​​such as C ++, Java, Pascal, and many others allow the programmer to concentrate as much as possible on writing the algorithm. In many respects this is achieved due to the fact that the language "hides" from the programmer the features of the equipment on which the program will be executed. The fullest possible use of hardware resources is precisely the task of the optimizing compiler.

Note that the problem of efficient allocation of hardware resources is not only a consequence of the use of high-level languages. Even if a programmer writes in assembler language and at the same time is perfectly familiar with the features of hardware, for which he writes, there are tasks that are very difficult to solve without using automatic tools.

As an example, consider the pipelining of cycles that exists in microprocessors of the Intel Itanium family. Due to pipelining, on the Itanium platform, it is possible to execute several iterations of one cycle in parallel (see figure).

Intel Itanium
Software pipelining

Here at the time t1 the execution of the first iteration of a certain cycle begins. At time t2, still in the process of executing the first iteration, the execution of the second iteration of the same cycle starts. And since time t3, three iterations of the same loop are simultaneously executed.

Resource planning for such pipelining is a rather complicated “mechanical” task. Even if a programmer is a dock in programming in assembler, he has no need to spend an incredible amount of his time on a task that the optimizing compiler can do in a split second.

We turn, finally, to the third advantage of optimizing compilers. Such compilers can make "touches" in the algorithms of programmers. And sometimes quite significant "strokes".

Of course, the compiler will not be able to radically change the algorithm (at least, modern compilers are still very far from this), however, it is quite possible for him to remove some non-optimalities. Sometimes the elimination of such non-optimalities can lead to a program acceleration at times.

Consider an example.
 for (i = 0; i < n; i++) {  = e * f; a[i] = c * i; } 


In this C-code fragment, at each iteration of the loop, the variable “c” is calculated. In this case, neither the variable “e” nor the variable “f” in the cycle are changed. Therefore, there is no need to calculate the variable “c” in the loop. It is enough to calculate it once outside the loop:
  = e * f; for (i = 0; i < n; i++) { a[i] = c * i; } 


This cycle option will work much faster than the previous one.

The transformation shown above will be able to perform even the simplest optimizing compiler. However, there are examples of much more complex transformations available only to the best members of the compiler family.

The examples given in the text show that optimizing compilers are designed to perform for the programmer that part of the work, the implementation of which would be irrational to involve expensive human resources. At the same time, the more reliable and “smart” the compiler, the more benefit you can get from the work of the programmer.

Source: https://habr.com/ru/post/134171/


All Articles