Optimisations
Vue d'ensemble des optimisations disponibles dans Catnip, avec l'idée simple : accélérer sans complexifier.
Niveaux d'Optimisation
Catnip supporte 4 niveaux d'optimisation (0-3) contrôlables via pragma, CLI ou API.
Défaut : optimize=0 (aucune optimisation)
| Niveau | Alias | Description | Passes actives |
|---|---|---|---|
| 0 | none, off (défaut) | Aucune optimisation | Aucune |
| 1 | low | Optimisations légères | Constant folding (IR) |
| 2 | medium | Optimisations standard | Constant folding, Strength reduction, Block flattening (IR) |
| 3 | high | Optimisations agressives | Toutes passes IR + CFG (dead code, merge, empty, branches), 5 itérations |
Quand Activer les Optimisations
Défaut (optimize=0) - Recommandé pour :
- Scripts et REPL (latence optimale)
- Code avec JIT activé (JIT domine les optimisations)
- Développement itératif (compilation 2x plus rapide)
optimize=3 - Activer pour :
- Code exécuté 100+ fois sans JIT (gains 4-11%)
- Templates/code généré avec beaucoup de dead code (réduction AST 46%)
- Scripts de production sans JIT
Trade-offs mesurés :
- Compile-time :
optimize=3est 2x plus lent (0.3ms → 0.7ms) - Runtime sans JIT :
optimize=3est 4-11% plus rapide - Runtime avec JIT : aucune différence (JIT masque les optimisations)
Les optimisations sont un boost au démarrage. Le JIT est un turbo qui s'allume en plein vol. Si ton code chauffe assez pour déclencher le JIT, les optimisations de compile-time deviennent secondaires.
Contrôle du Niveau
Via CLI :
catnip script.cat # Défaut (optimize=0)
catnip -o level:3 script.cat # Active toutes les optimisations
# Alias textuels
catnip -o level:none script.cat # Niveau 0 (défaut)
catnip -o level:low script.cat # Niveau 1
catnip -o level:medium script.cat # Niveau 2
catnip -o level:high script.cat # Niveau 3
Via pragma :
# Défaut : optimize=0
pragma("optimize", 3) # Activer toutes les optimisations
pragma("optimize", 1) # Optimisations légères
Via API Python :
from catnip import Catnip
cat = Catnip() # Défaut (optimize=0)
cat = Catnip(optimize=3) # Toutes les optimisations
Introspection :
catnip.optimize # Retourne le niveau actuel (0-3)
# Branchement conditionnel
if catnip.optimize > 0 {
"code optimisé"
} else {
"sans optimisation"
}
Passes Disponibles
Catnip applique deux types de passes complémentaires.
Les passes IR existent en deux implémentations : PyO3 (catnip_rs/src/semantic/) opérant sur Op (pipeline Standard)
et pure Rust (catnip_core/src/semantic/passes/) opérant directement sur IR (pipeline Standalone). Le trait
PurePass et le PureOptimizer appliquent les passes itérativement jusqu'au point fixe (max 10 itérations, détection
par PartialEq).
Passes IR (Niveau Expression)
Optimisations locales sur expressions et statements :
-
Constant Folding - Évalue les expressions constantes au compile-time
-
2 + 3→5 -
True and False→False -
Strength Reduction - Remplace opérations coûteuses par équivalents rapides
-
x * 2→x + x(si x simple) -
x ** 2→x * x -
Block Flattening - Simplifie les blocs imbriqués
-
{ { { x } } }→x -
Dead Code Elimination - Supprime code inaccessible
-
Code après
return -
Branches
if False -
Blunt Code Simplification - Simplifie patterns maladroits
-
not not x→x x == True→xnot (a < b)→a >= b(inversion de comparaison)x and (not x)→False(complement)-
Les simplifications
and/oravec constantes (x and False→False,x or True→True) ne s'appliquent que quand les deux opérandes sont desBool. En Catnip,and/orretournent toujours un booléen — simplifier avec un opérande non-bool changerait le type de retour -
Constant/Copy Propagation - Propage valeurs connues
-
x = 5; y = x * 2→x = 5; y = 10
Passes CFG (Niveau Contrôle de Flux)
Optimisations globales sur le graphe de flot de contrôle :
- Dead Block Elimination - Supprime blocs inaccessibles
- Block Merging - Fusionne blocs consécutifs
- Empty Block Removal - Supprime blocs vides
- Constant Branch Folding - Résout branches constantes
Passes SSA (Niveau Inter-blocs)
Optimisations globales en forme SSA (Braun et al. 2013), activées au niveau 3 :
- CSE inter-blocs (
ssa_cse.rs) - Élimine expressions redondantes entre blocs dominants (même opcode + mêmes operandes SSA) - LICM (
ssa_licm.rs) - Hoist les opérations pures invariantes hors des boucles vers un preheader - GVN (
ssa_gvn.rs) - Global Value Numbering, assigne un value number à chaque expression pour détecter les équivalences - DSE globale (
ssa_dse.rs) - Élimine les SetLocals avec RHS pur dont le résultat n'est jamais utilisé, itération au fixpoint
Architecture du Pipeline
Ordre d'exécution :
- Passes IR (8 passes) sur l'IR brut
- CFG construction depuis IR optimisé
- Analyse de dominance + construction SSA (Braun)
- Passes SSA (4 passes) sur le graphe en forme SSA
- Destruction SSA (phi → SetLocals)
- Passes CFG (4 passes) sur le graphe
- Reconstruction IR depuis CFG optimisé
- Semantic analysis → Op final
Itérations : à niveau 3, le pipeline IR+CFG est exécuté 5 fois (certaines passes débloquent d'autres optimisations)
Tail Call Optimization (TCO)
La TCO est une optimisation toujours active (indépendante du niveau) :
Principe : transforme récursion terminale en boucle (stack O(1))
fact = (n, acc) => {
if n <= 1 { acc }
else { fact(n-1, n*acc) } # ← Tail call
}
Détection : automatique par l'analyseur sémantique (appels en dernière position)
Implémentation : trampoline pattern (pas de frame empilée)
Boucles : visit_while et visit_for forcent in_tail_position = false avant de visiter le body. Les appels dans
le corps d'une boucle ne sont jamais marqués comme tail calls, même quand la boucle est le dernier statement d'une
fonction.
Voir ARCHITECTURE section TCO pour détails.