Difference between revisions of "Whole Program Optimization/fr"

From Lazarus wiki
(Exemple concret)
(Exemple concret)
Line 35: Line 35:
  
 
Le compilateur enregistrera les classes créées dans le programme (<tt>tchild1</tt> et <tt>tchild2</tt>), et après l'étape de liaison également que <syntaxhighlight lang="pascal" enclose="none"> notcalled </syntaxhighlight> n'est en fait pas appelée.
 
Le compilateur enregistrera les classes créées dans le programme (<tt>tchild1</tt> et <tt>tchild2</tt>), et après l'étape de liaison également que <syntaxhighlight lang="pascal" enclose="none"> notcalled </syntaxhighlight> n'est en fait pas appelée.
 
The compiler will record which classes are created in the program (<tt>tchild1</tt> and <tt>tchild2</tt>), and after the linking step also that the <syntaxhighlight lang="pascal" enclose="none">notcalled</syntaxhighlight> procedure is in fact not called.
 
  
 
Ensuite, enregistrez le code assembleur généré dans <syntaxhighlight lang="bash" enclose="none">twpo4.s1</syntaxhighlight> pour une comparaison ultérieure.
 
Ensuite, enregistrez le code assembleur généré dans <syntaxhighlight lang="bash" enclose="none">twpo4.s1</syntaxhighlight> pour une comparaison ultérieure.
Line 52: Line 50:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Now compile the program a second time, using the information in <syntaxhighlight lang="bash" enclose="none">twpo4-1.wpo</syntaxhighlight> and collecting new information in <syntaxhighlight lang="bash" enclose="none">twpo4-2.wpo</syntaxhighlight>.
+
Maintenant compilez le programme une seconde fois, en utilisant l'information dans <syntaxhighlight lang="bash" enclose="none">twpo4-1.wpo</syntaxhighlight> et en collectant de nouvelles informations dans <syntaxhighlight lang="bash" enclose="none">twpo4-2.wpo</syntaxhighlight>.
At this point, the compiler knows that no instance of the <syntaxhighlight lang="pascal" enclose="none">tbase</syntaxhighlight> class is created in the program, and therefore it replaces all regular entries in its virtual method table with references to <syntaxhighlight lang="pascal" enclose="none">FPC_ABSTRACTERROR</syntaxhighlight>.
+
 
Virtual class methods cannot be eliminated this way, since they can also be called without constructing an instance of this type.
+
Arrivé à ce point, le compilateur sait qu'aucune instance de la classe <syntaxhighlight lang="pascal" enclose="none">tbase</syntaxhighlight> n'est créée dans le programme et par conséquent il remplace toutes les entrées normales dans sa table de méthodes virtuelles par des références à <syntaxhighlight lang="pascal" enclose="none">FPC_ABSTRACTERROR</syntaxhighlight>.
 +
Les méthodes de classes virtuelles ne peuvent pas être éliminées de cette façon, puisqu'elles peuvent être appelées sans construire d'instance de ce type.
  
Because the compiler knows from the previously generated feedback file that the <syntaxhighlight lang="pascal" enclose="none">notcalled</syntaxhighlight> procedure is never called, it will also record that only an instance of <syntaxhighlight lang="pascal" enclose="none">tchild1</syntaxhighlight> is created, as the <syntaxhighlight lang="pascal" enclose="none">tchild2</syntaxhighlight> instance was only created inside this <syntaxhighlight lang="pascal" enclose="none">notcalled</syntaxhighlight> procedure.
+
Parce que le compilateur connaît depuis le précédent fichier de rétroaction généré que la procédure <syntaxhighlight lang="pascal" enclose="none">notcalled</syntaxhighlight> n'est jamais appelée, il enregistrera aussi que seule une instance de <syntaxhighlight lang="pascal" enclose="none">tchild1</syntaxhighlight> est créée, comme l'instance <syntaxhighlight lang="pascal" enclose="none">tchild2</syntaxhighlight> était seulement créée dans cette procédure <syntaxhighlight lang="pascal" enclose="none">notcalled</syntaxhighlight>.
For optimization purposes, it will however still consider that an instance of <syntaxhighlight lang="pascal" enclose="none">tchild2</syntaxhighlight> may be created, as for optimization purposes it still uses the feedback file generated during the previous compilation.
+
À des fins d'optimisation, il considérera cependant toujours qu'une instance de <syntaxhighlight lang="pascal" enclose="none"> tchild2 </syntaxhighlight> peut être créée, car à des fins d'optimisation, il utilise toujours le fichier de rétroaction généré lors de la compilation précédente .
  
Afterwards, we again save the generated assembler code for later comparisons.
+
Ensuite, nous enregistrons à nouveau le code assembleur généré pour des comparaisons ultérieures.
  
 
<syntaxhighlight lang="bash">
 
<syntaxhighlight lang="bash">

Revision as of 11:49, 15 December 2020

English (en) français (fr)

Généralités

Traditionnellement, les compilateurs optimisent un programme procédure par procédure, ou au mieux unité de compilation par unité de compilation.

L'optimisation globale de programme, en abrégé OGP, signifie que le compilateur considère toutes les unités de compilation qui compose un programme ou une bibliothèque et les optimise en utilisant une connaissance approfondie de comment ils sont utilisés ensemble dans un cas particulier.

La manière dont fonctionne l'OGP en général est comme suit :

  • vous compilez le programme normalement, en disant au compilateur d'enregistrer divers bits d'information dans un fichier de rétroaction.
  • vous recompilez the programme (et facultativement toutes les unités qu'il utilise) avec les OGP, en fournissant le fichier de rétroaction comme une entrée supplémentaire au compilateur.

Dans cetaines implémentations, le compilateur génère que sorte de code d'intermédiaire (p.ex., le byte code) et l'éditeur de lien réalise toutes les OGP tout au long de la traduction vers la cible ISA (NdT acronyme non compris). Dans le cas de FPC pourtant, le schéma suivi est celui décrit plus bas.

Principes généraux

Quelques principes généraux ont été suivis lors de la conception de l'implémentation par FPC de l'OGP:

  • Toute l'information nécessaire pour générer le fichier de rétroaction OGP pour un programme est toujours enregistré dans les fichiers PPU. Ce qui signifie que vous pouvez utiliser une RTL pour l'OGP (même si la RTL elle-même ne sera alors pas optimisée, votre programme et ses unités peuvent être correctement optimisés car le compilateur sait tout ce qu'il doit savoir sur toutes les unités RTL);
  • Le fichier de rétroaction OGP généré est du texte brut. L'idée est qu'il devrait facile d'inspecter ce fichier à la main et d'y ajouter de l'information produite par des outils externes si désiré (p.exe. des informations de profilage);
  • L'implémentation du sous-système OGP dans le compilateur est très modulaire, donc il devrait être facile d'y adjoindre des fournisseurs d'informations OGP additionnels ou de choisir à l'exécution entre différents forunisseurs d'informations pour la même sorte d'information. En même temps, l'interaction avec le reste du compilateur est réduite au strict minimum pour améliorer la maintenabilité;
  • Il est possible de générer un fichier de rétroaction WPO tout en en utilisant un autre comme entrée. Dans certains cas, l'utilisation de ce deuxième fichier de rétroaction comme entrée lors d'une troisième compilation peut encore améliorer les résultats.

Comment utiliser

Générer le fichier de rétroaction WPO (WPO feedback file)

En tout premier, compilez votre programme (ou bibliothèque) et toutes ses unités comme vous le feriez normalement, mais en compilant, vous ajoutez

-FW/path/to/feedbackfile.wpo -OW<selected_wpo_options>

.

Le compilateur va alors, juste après que votre programme a été lié, collecter toutes les informations nécessaires pour effectuer les OGP demandés lors d'une compilation successive, et stocker ces informations dans /path/to/feedbackfile.wpo.

Utiliser le fichier de rétroaction WPO

Pour appliquer réellement les WPO, recompilez le programme/bibliothèque et tout ou partie des unités qu'il utilise en employant

-Fw/path/to/feedbackfile.wpo -Ow<selected_wpo_options>

, pointant ainsi le compilateur vers le fichier de rétroaction généré à l'étape précédente.

Le compilateur lira alors l'information collectée sur le programme durant la précédente compilation, et l'utilise durant la compilation en cours des unités et/ou du programme/bibliothèque. Les unités non recompilées durant cette seconde passe ne seront bien évidemment pas optimisées, mais elle fonctionneront encore normalement si elle sont utilisées avec les unités et programme/bibliothèque optimisées.

Exemple concret

L'exemple ci-dessous fait référence à twpo4.pp.

Compilez le programme une première fois, en collectant les rétroactions dans twpo4-1.wpo dans le répertoire courant.

Le compilateur enregistrera les classes créées dans le programme (tchild1 et tchild2), et après l'étape de liaison également que notcalled n'est en fait pas appelée.

Ensuite, enregistrez le code assembleur généré dans twpo4.s1 pour une comparaison ultérieure.

$ ppn37 -FWtwpo4-1.wpo -OWall -CX -XX -Xs- -al twpo4
Free Pascal Compiler version 2.3.1 [2008/12/11] for i386
Copyright (c) 1993-2008 by Florian Klaempfl
Target OS: Darwin for i386
Compiling twpo4.pp
Assembling program
Linking twpo4
66 lines compiled, 0.6 sec
$ mv twpo4.s twpo4.s1

Maintenant compilez le programme une seconde fois, en utilisant l'information dans twpo4-1.wpo et en collectant de nouvelles informations dans twpo4-2.wpo.

Arrivé à ce point, le compilateur sait qu'aucune instance de la classe tbase n'est créée dans le programme et par conséquent il remplace toutes les entrées normales dans sa table de méthodes virtuelles par des références à FPC_ABSTRACTERROR. Les méthodes de classes virtuelles ne peuvent pas être éliminées de cette façon, puisqu'elles peuvent être appelées sans construire d'instance de ce type.

Parce que le compilateur connaît depuis le précédent fichier de rétroaction généré que la procédure notcalled n'est jamais appelée, il enregistrera aussi que seule une instance de tchild1 est créée, comme l'instance tchild2 était seulement créée dans cette procédure notcalled. À des fins d'optimisation, il considérera cependant toujours qu'une instance de tchild2 peut être créée, car à des fins d'optimisation, il utilise toujours le fichier de rétroaction généré lors de la compilation précédente .

Ensuite, nous enregistrons à nouveau le code assembleur généré pour des comparaisons ultérieures.

$ ppn37 -FWtwpo4-2.wpo -OWall -Fwtwpo4-1.wpo -Owall -CX -XX -Xs- -al twpo4
Free Pascal Compiler version 2.3.1 [2008/12/11] for i386
Copyright (c) 1993-2008 by Florian Klaempfl
Target OS: Darwin for i386
Compiling twpo4.pp
Assembling program
Linking twpo4
66 lines compiled, 0.1 sec
$ mv twpo4.s twpo4.s2

Compile the program a final time, using the information collected in twpo4-2.wpo. This time, the compiler knows that only a tchild1 instance is created, and will therefore turn the bb.test call from a virtual method call into a static method call (because it knows that even though the type of bb is tbase, in practice it can only be a tchild1 since no tbaseh or other tbase-descendent class instance is created).

$ ppn37 -Fwtwpo4-2.wpo -Owall -CX -XX -al twpo4
Free Pascal Compiler version 2.3.1 [2008/12/11] for i386
Copyright (c) 1993-2008 by Florian Klaempfl
Target OS: Darwin for i386
Compiling twpo4.pp
Assembling program
Linking twpo4
66 lines compiled, 0.1 sec

Now, let's have a look at the differences between the generated assembler files. After the first recompilation:

$ diff -u twpo4.s1 twpo4.s2
--- twpo4.s1	2008-12-11 18:59:55.000000000 +0100
+++ twpo4.s2	2008-12-11 19:00:15.000000000 +0100
@@ -214,15 +214,15 @@
 	.long	0,0,0
 	.long	FPC_EMPTYINTF
 	.long	0
-	.long	_SYSTEM_TOBJECT_$__DESTROY
+	.long	FPC_ABSTRACTERROR
 	.long	_SYSTEM_TOBJECT_$__NEWINSTANCE$$TOBJECT
-	.long	_SYSTEM_TOBJECT_$__FREEINSTANCE
-	.long	_SYSTEM_TOBJECT_$__SAFECALLEXCEPTION$TOBJECT$POINTER$$LONGINT
-	.long	_SYSTEM_TOBJECT_$__DEFAULTHANDLER$formal
-	.long	_SYSTEM_TOBJECT_$__AFTERCONSTRUCTION
-	.long	_SYSTEM_TOBJECT_$__BEFOREDESTRUCTION
-	.long	_SYSTEM_TOBJECT_$__DEFAULTHANDLERSTR$formal
-	.long	_P$PROGRAM_TBASE_$__TEST
+	.long	FPC_ABSTRACTERROR
+	.long	FPC_ABSTRACTERROR
+	.long	FPC_ABSTRACTERROR
+	.long	FPC_ABSTRACTERROR
+	.long	FPC_ABSTRACTERROR
+	.long	FPC_ABSTRACTERROR
+	.long	FPC_ABSTRACTERROR
 	.long	0

 .const_data

As you can see, all references to the virtual methods in the virtual method table of tbase have been removed, since the compiler knows they can never be referenced. This allows the linker to throw away these methods if they are not referenced anywhere else either. In our example, that is only true for tbase.test.

Now let's look at the effect of the second recompilation:

$ diff -u twpo4.s2 twpo4.s
--- twpo4.s2	2008-12-11 19:00:15.000000000 +0100
+++ twpo4.s	2008-12-11 19:00:29.000000000 +0100
@@ -103,9 +103,7 @@
 	movl	%eax,-4(%ebp)
 # [54] bb.test;
 	movl	-4(%ebp),%eax
-	movl	-4(%ebp),%edx
-	movl	(%edx),%edx
-	call	*80(%edx)
+	call	L_P$PROGRAM_TCHILD1_$__TEST$stub
 # [55] bb.free;
 	movl	-4(%ebp),%eax
 	call	L_SYSTEM_TOBJECT_$__FREE$stub
@@ -139,10 +137,7 @@
 # [63] bb.test;
 	movl	L_U_P$PROGRAM_BB$non_lazy_ptr-Lj2(%ebx),%eax
 	movl	(%eax),%eax
-	movl	L_U_P$PROGRAM_BB$non_lazy_ptr-Lj2(%ebx),%edx
-	movl	(%edx),%edx
-	movl	(%edx),%edx
-	call	*80(%edx)
+	call	L_P$PROGRAM_TCHILD1_$__TEST$stub
 # [64] a:=2;
 	movl	L_U_P$PROGRAM_A$non_lazy_ptr-Lj2(%ebx),%eax
 	movl	$2,(%eax)

As you can see, the two virtual method calls to bb.test (one inside the never called notcalled procedure, and one in the main program) have been replaced with calls to tchild1.test.

Note that you could also recompile the entire run time library for this particular program, if you want.

Quand utiliser

Since whole program optimization requires multiple compilations, it is advisable to only use this functionality when compiling a final release version.

Also keep in mind that once a unit has been compiled using wpo for a particular program, it has to be recompiled if you want to use it in another program.

Optimisation disponible de programme entier

Toutes les optimisations

parameter
-OWall/-Owall
effect
Enables all whole program optimizations described below.
limitations
The combined limitations of all optimizations described below.

Dévirtualisation de programme entier

parameter
-OWdevirtcalls/-Owdevirtcalls
effect
Changes virtual method calls into normal (static) method calls when the compiler can determine that a virtual method call will always go to the same static method. This makes such code both smaller and faster. In general, it's mainly an enabling optimization for other optimizations, because it makes the program easier to analyse due to the fact that it reduces indirect control flow.
limitations
  • The current implementation is context-insensitive. This means that the compiler only looks at the program as a whole and determines for each class type which methods can be devirtualised, rather than that it looks at each call statement and the surrounding code to determine whether or not this call can be devirtualised;
  • The current implementation does not yet devirtualise interface method calls (not when calling them via an interface instance, nor when calling them via a class instance).


Optimiser la table des méthodes virtuelles

parameter
-OWoptvmts/-Owoptvmts
effect
This optimization looks at which class types can be instantiated and which virtual methods can be called in a program, and based on this information it replaces virtual method table (VMT) entries that can never be called with references to FPC_ABSTRACTERROR. This means that such methods, unless they are called directly via an inherited call from a child class/object, can be removed by the linker. It has little or no effect on speed, but can help reducing code size.
limitations
  • Methods that are published, or getters/setters of published properties, can never be optimized in this way, because they can always be referred to and called via the RTTI (which the compiler cannot detect).
  • Such optimizations are not yet done for virtual class methods (but it should not be that difficult to add)

Vivacité de symbole

parameter
-OWsymbolliveness/-Owsymbolliveness
effect
This parameter does not perform any optimization by itself. It simply tells the compiler to record which functions/procedures were kept by the linker in the final program. During a subsequent wpo pass, the compiler can then ignore the removed functions/procedures as far as wpo is concerned (e.g., if a particular class type is only constructed in one unused procedure, then ignoring this procedure can improve the effectiveness of the previous two optimizations).
limitations
  • This optimization requires that the nm(1) utility is installed on the system. For Linux binaries, objdump(1) will also work. In the future, this information could also be extracted from the internal linker for the platforms that it supports.
  • Collecting information for this optimization (using -OWsymbolliveness) requires that smart linking is enabled (-XX) and that symbol stripping is disabled (-Xs-). When only using such previously collected information (using -OwSymbolliveness or -Owall), these limitations do not apply.

Format de fichier de rétroaction WPO

This information is mainly interesting if you want to add external data to the WPO feedback file, e.g. from a profiling tool. If you are just a user of the WPO functionality, you can ignore what follows.

The file consists of comments and a number of sections. Comments are lines that start with a #. Each section starts with % (percent sign and space) followed by the name of the section (e.g.,% contextinsensitive_devirtualization). After that, until either the end of the file or until the next line starting with with "% ", first a human readable description follows of the format of this section (in comments), and then the contents of the section itself.

There are no rules for how the contents of a section should look, except that lines starting with # are reserved for comments and lines starting with % are reserved for section markers.