Whole Program Optimization/fr

From Lazarus wiki
Revision as of 12:47, 15 December 2020 by E-ric (talk | contribs) (Optimiser la table des méthodes virtuelles)

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

Compilez le programme une dernière fois, en utilisant les informations collectées dans twpo4-2.wpo. Cette fois, le compilateur sait que seul une instance de tchild1 est créée, et va par conséquent remplacer l'appel bb.test depuis une méthode virtuelle par une méthode statique (car il sait que même si le type de bb est tbase, en pratique il ne peut être qu'un tchild1 car aucune instance de tbase ou autre instance de classe descendant de tbase n'est créée.

$ 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

Maintenant, regardons les différences entre les fichiers assembleur générés. Après la première 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

Comme vous pouvez le voir, toutes les références aux méthodes virtuelles dans la VMT de tbase ont été retirées, puisque le compilateur sait qu'elles ne peuvent pas être réféencées. Cela permet à l'éditeur de liens de jeter ces méthodes si elles ne sont référencées nulle part ailleurs non plus.

Dans notre exemple, c'est seulement vrai pour tbase.test.

Maintenant regardons l'effet de la seconde 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)

Comme vous pouvez le voir, les deux appels de méthode virtuelle à bb.test (un dans la procédure notcalled, jamais appelée, et un dans le programme principal) ont été remplacé par des appels à tchild1.test.

Remarquez que vous pourriez aussi recompiler la RTL pour ce programme particulier, si vous voulez.

Quand utiliser

Puisque l'optimisation globale de programme demande de multiples compilation, il est recommandable de limiter son utilisation aux versions de livraison finale.

Gardez aussi à l'esprit qu'une fois que l'unité a été compilée en utilisant wpo pour un programme particulier, elle doit être à nouveau compilée pour un autre programme.

Optimisation disponible de programme entier

Toutes les optimisations

paramètre
-OWall/-Owall
effet
Active toutes les optimisations globlales de programme décrites ci-dessus.
limitations
Les limitations de toutes les optimisations décrites ci-dessus.

Dévirtualisation de programme entier

paramètre
-OWdevirtcalls/-Owdevirtcalls
effet
Transforme les appels de méthode virtuelle en appels de méthode statique quand le compilateur peut déterminer que l'appel de méthode virtuelle ira toujours à la même méthode statique. Cela rend le code plus petit et plus rapide. En général, il s'agit principalement d'une optimisation permettant d'autres optimisations, car elle facilite l'analyse du programme en réduisant le flux de contrôle indirect.
limitations
  • L'implémentation actuelle est insensible au contexte. Cela signifie que le compilateur ne regarde que le programme dans son ensemble et détermine pour chaque type de classe quelles méthodes peuvent être dévirtualisées, plutôt que de regarder chaque instruction d'appel et le code environnant pour déterminer si cet appel peut être dévirtualisé ou non;
  • L'implémentation actuelle ne dévirtualise pas encore les appels de méthode d'interface (ni lors de leur appel via une instance d'interface, ni lors de leur appel via une instance de classe).

Optimiser la table des méthodes virtuelles

paramètre
-OWoptvmts/-Owoptvmts
effet
Cette optimisation examine quels types de classe peuvent être instanciés et quelles méthodes virtuelles peuvent être appelées dans un programme, et sur la base de ces informations, il remplace les entrées de la VMT qui ne peuvent jamais être appelées avec des références vers FPC_ABSTRACTERROR. Ce qui signifie que de telles méthodes, qu'elles ne soient appelées directement via un appel inherited depuis une classe/objet enfant, peuvent être retirées par l'éditeur de lien. Cela a peu ou pas d'effet que la vitesse, mais peut aider à réduire la taille du code.
limitations
  • Les méthodes qui sont published, ou les getters/setters de propriétés publiées, ne peuvent jamais être optimisées de cette façon car elles peuvent toujours être référencées via la RTTI (ce que le compilateur ne peut pas détecter).
  • De telles optimisations ne sont pas encore faites pour les méthodes virtuelles de classe (mais cela ne devrait pas être difficile à ajouter).

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.