Aller au contenu
14 août 2026·8 min de lecture

Faut-il réécrire Fortran en Rust ou l'encapsuler ?

Découvrez quand réécrire Fortran en Rust, quand encapsuler un noyau éprouvé et comment comparer parité, maintenance et coûts matériels.

Faut-il réécrire Fortran en Rust ou l'encapsuler ?

Un noyau Fortran qui produit des résultats fiables depuis vingt ans ne devient pas du mauvais code parce que l'application qui l'entoure est devenue pénible. Si le noyau a une interface étroite, des builds reproductibles et un responsable encore capable de le diagnostiquer, l'encapsuler derrière une interface moderne reste généralement la décision la moins chère et la plus sûre. Le langage source ne justifie pas à lui seul de réécrire des mathématiques qui fonctionnent.

La décision change lorsque l'ancien noyau dicte le déploiement, interdit certains matériels, cache un état mutable ou exige des compétences que l'organisation ne possède plus. Sa conservation a alors un coût récurrent. Un portage vers Rust peut revenir moins cher qu'un nouveau cycle de compilateurs spéciaux, de machines obsolètes, de livraisons manuelles et d'incidents que seul un ingénieur retraité comprend. La difficulté consiste à comparer ces coûts sans confondre ancienneté et défaut, ni prendre la justesse passée pour une preuve d'exploitabilité future.

Séparez la valeur de l'algorithme du coût de son contenant

Un algorithme éprouvé et son implémentation en Fortran sont deux actifs liés, mais distincts. Les équations, coefficients, règles de convergence et comportements limites acceptés peuvent mériter d'être conservés, même si le système de build et les hypothèses d'exécution doivent disparaître.

Les équipes qualifient souvent un noyau d'« éprouvé » pour dire que la production en dépend depuis des années. Cet historique compte, mais il ne répond qu'à une question : le système complet a-t-il produit des résultats acceptables pour les entrées qu'il a réellement reçues ? Il ne prouve pas que le code est portable, exempt de comportement indéfini, sûr en concurrence ou compréhensible après le départ de son responsable actuel. Une longue durée de service peut même masquer des dépendances, car personne n'a récemment reconstruit le noyau sur une machine propre.

Notez ce qui donne sa valeur au noyau avant de choisir son traitement. L'inventaire utile reste concret :

  • Le modèle mathématique et la version que le métier accepte
  • Les plages d'entrée observées en production, y compris les cas invalides et dégénérés
  • La précision requise, le comportement d'arrondi et les tolérances de convergence
  • Les limites de temps et de mémoire qui affectent un vrai batch ou une vraie requête
  • Les options du compilateur, bibliothèques liées, fichiers de données et ordre d'initialisation

Cet inventaire fait apparaître une distinction importante. La parité numérique signifie que la nouvelle exécution reste dans une tolérance convenue. La parité comportementale couvre aussi les erreurs, avertissements, nombres d'itérations, ordre des sorties, traitement des NaN, délais et effets de bord. Un portage peut respecter la première définition et casser l'application selon la seconde. Une enveloppe peut préserver les deux, à condition que son interface ne modifie pas discrètement la représentation ou le cycle de vie.

L'âge du source doit figurer vers le bas du dossier de décision. Les obligations observables vont en haut. Si personne ne sait les énoncer, ni l'encapsulation ni la réécriture ne sont prêtes. Il faut d'abord retrouver le contrat à partir du code et des preuves de production.

Une interface étroite et stable favorise l'encapsulation

Encapsulez le noyau lorsque ses appelants peuvent le décrire comme un petit ensemble d'opérations déterministes, avec des entrées et sorties numériques ordinaires. Un bon candidat ressemble plus à une bibliothèque qu'à une application : initialiser des tables immuables, transmettre des tableaux et des options scalaires, calculer, puis renvoyer des résultats et un statut.

Comptez les points de passage plutôt que les lignes de Fortran. Un solveur de 300 000 lignes exposé par six opérations stables peut être plus facile à contenir qu'une routine de 6 000 lignes qui lit des fichiers globaux, modifie des blocs COMMON, rédige des rapports et rappelle une interface utilisateur. Le second noyau a une surface comportementale plus grande malgré sa taille réduite.

Une enveloppe est intéressante lorsque toutes ces conditions sont remplies :

  • Un compilateur maintenu peut reproduire le binaire sur les machines prévues
  • Le noyau possède des tests ou des cas enregistrés avec des sorties de confiance
  • Les appels ne dépendent pas d'un état de processus caché, ou cet état peut être isolé
  • Le coût de conversion des données reste faible devant le temps de calcul
  • Les correctifs de sécurité et les diagnostics atteignent l'interface sans toucher aux mathématiques

L'enveloppe doit gérer la validation, les limites de mémoire, l'identification de version, la télémétrie et la conversion entre les types de l'application et ceux de Fortran. Elle ne doit pas prétendre corriger le comportement numérique. Cette séparation permet aux ingénieurs applicatifs d'améliorer l'exploitation sans modifier par accident le contrat des résultats.

Un test d'organisation est également utile : un nouvel ingénieur peut-il reconstruire la bibliothèque, exécuter ses cas et localiser un appel défaillant sans interroger l'auteur d'origine ? Si oui, le noyau conservé est une dépendance maîtrisée. Sinon, l'enveloppe risque seulement de cacher un programme orphelin. La documentation ne suffit pas. Le build et le diagnostic doivent fonctionner sur une machine vierge.

Utilisez l'ABI C comme une jointure petite et ennuyeuse

Fortran et Rust peuvent partager une frontière fiable au moyen de l'interface binaire C, si le côté Fortran utilise ISO_C_BINDING au lieu de conventions de symboles propres au compilateur. La jointure doit exposer des types numériques de largeur fixe, des longueurs de tableaux explicites, des tampons plats et des codes d'état entiers.

Ce point d'entrée Fortran rend l'organisation mémoire visible :

module kernel_api
  use, intrinsic :: iso_c_binding
  implicit none
contains
  subroutine evaluate(n, x, scale, y, status) bind(C, name="kernel_evaluate")
    integer(c_int), value :: n
    real(c_double), intent(in) :: x(n)
    real(c_double), value :: scale
    real(c_double), intent(out) :: y(n)
    integer(c_int), intent(out) :: status

    if (n < 1 .or. scale <= 0.0_c_double) then
      status = 1_c_int
      return
    end if

    call legacy_evaluate(n, x, scale, y)
    status = 0_c_int
  end subroutine evaluate
end module kernel_api

Le côté Rust garde l'opération unsafe dans un petit module et présente une API de slices vérifiée au reste de l'application :

unsafe extern "C" {
    fn kernel_evaluate(
        n: i32,
        x: *const f64,
        scale: f64,
        y: *mut f64,
        status: *mut i32,
    );
}

pub fn evaluate(x: &[f64], scale: f64) -> Result<Vec<f64>, KernelError> {
    let n = i32::try_from(x.len()).map_err(|_| KernelError::InputTooLarge)?;
    if x.is_empty() || !scale.is_finite() || scale <= 0.0 {
        return Err(KernelError::InvalidInput);
    }

    let mut y = vec![0.0_f64; x.len()];
    let mut status = 0_i32;
    unsafe {
        kernel_evaluate(n, x.as_ptr(), scale, y.as_mut_ptr(), &mut status);
    }

    match status {
        0 => Ok(y),
        code => Err(KernelError::Fortran(code)),
    }
}

Ce code est volontairement peu impressionnant. C'est une qualité à la frontière entre deux langages. Le Rust Nomicon décrit les appels de fonctions étrangères comme unsafe, car le compilateur ne peut pas vérifier le contrat de l'autre langage. Gardez le bloc unsafe assez petit pour être audité et rendez chaque précondition visible avant l'appel.

Le manuel d'interopérabilité de GNU Fortran explique comment les procédures et types interopérables passent par BIND(C) et ISO_C_BINDING. Suivez ce mécanisme au lieu de dépendre du décorage de noms habituel d'un compilateur. Un symbole exporté trouvé avec un outil d'inspection binaire prouve seulement le contenu du build actuel, pas la stabilité de l'interface avec le compilateur de demain.

N'envoyez ni structs Rust, ni types dérivés Fortran, ni tableaux allouables, ni chaînes propres à un langage à travers la première version de cette jointure. Aplatissez-les. Pour les matrices, documentez les dimensions, la dimension principale et l'ordre de stockage. Pour le texte, passez un tampon d'octets avec une longueur explicite et une règle d'encodage. Les représentations simples évitent les ambiguïtés coûteuses.

L'état caché fait échouer les enveloppes

Une enveloppe échoue quand elle donne à un programme avec état l'apparence d'une fonction pure sans contrôler cet état. Les variables SAVE, blocs COMMON, tableaux de travail en cache, variables d'environnement, numéros d'unité, fichiers du répertoire courant et modes de virgule flottante peuvent tous modifier le résultat d'un appel apparemment identique.

La concurrence révèle généralement le mensonge en premier. Deux requêtes web entrent dans l'enveloppe en même temps, toutes deux modifient le même espace de travail sauvegardé, et une réponse contient des valeurs tirées des entrées de l'autre. Un mutex peut rétablir la justesse, mais il sérialise aussi le débit. L'isolation par processus peut préserver le parallélisme en échange de mémoire et d'un coût de démarrage. Aucune option n'est automatiquement mauvaise, mais les deux doivent apparaître dans le modèle de capacité.

L'initialisation casse aussi souvent l'extraction. L'exécutable d'origine peut lire des coefficients, fixer une graine aléatoire ou appeler une routine de préparation avant le chemin numérique. L'extraction d'une bibliothèque qui n'exporte que la sous-routine finale peut renvoyer des nombres plausibles depuis un état non initialisé ou par défaut. Ces nombres sont plus dangereux qu'un plantage, car la supervision peut les accepter.

Cartographiez tout le cycle de vie de l'appel :

  1. Lancez un processus neuf et consignez chaque fichier, valeur d'environnement et bibliothèque chargés.
  2. Suivez l'initialisation jusqu'à ce que la première opération numérique puisse s'exécuter.
  3. Appelez deux fois le même cas et comparez toutes les sorties et valeurs d'état.
  4. Entrelacez deux cas différents, puis répétez-les dans des processus séparés.
  5. Forcez une entrée invalide, si possible un échec d'allocation et une absence de convergence.

Cette séquence indique si le noyau peut vivre dans un service multithread, s'il exige une file avec un seul worker ou s'il doit résider dans des processus isolés. Elle repère aussi les chemins d'erreur qui arrêtent le processus par STOP, écrivent sur la sortie standard ou laissent des résultats partiels. Une enveloppe ne peut plus traduire l'erreur une fois que le runtime Fortran a terminé son hôte.

L'organisation des tableaux mérite son propre contrôle. Fortran stocke les tableaux par colonnes; les bibliothèques Rust supposent souvent un ordre par lignes. Une matrice copiée peut avoir les bonnes dimensions tout en représentant sa transposée ou un pas mémoire incohérent. Testez une matrice non carrée avec des valeurs uniques, car les cas carrés ou symétriques cachent l'erreur. Si les copies de conversion dominent le temps d'appel, modifiez l'interface pour accepter l'ordre natif du noyau au lieu de payer deux parcours complets de la mémoire.

Prouvez la parité avec des données proches de la production

Exécutez la réécriture dans votre périmètre
En environnement réglementé, nos modèles peuvent fonctionner hors réseau sur du matériel situé chez le client.

La parité exige une comparaison exécutable, pas une revue de code ni quelques sorties de référence. Exécutez les anciens et nouveaux chemins sur les mêmes entrées enregistrées, capturez leurs résultats observables complets et classez chaque différence.

Commencez avec du trafic de production ou des enregistrements batch après avoir retiré les données que l'environnement de test ne doit pas contenir. Ces traces portent des combinaisons que les tests synthétiques manquent : groupes vides à côté de grands groupes, ordre inhabituel, indicateurs périmés, valeurs proches d'un seuil et nouvelles tentatives après un travail partiel. Ajoutez des cas construits pour les limites et le stress numérique, sans les substituer aux formes réellement exploitées.

Un banc de comparaison utile émet un enregistrement comme celui-ci pour chaque appel :

{
  "case_id": "settlement-004812",
  "operation": "evaluate",
  "input_digest": "sha256:...",
  "old": {"status": 0, "iterations": 7, "output": [1.25, 3.5]},
  "candidate": {"status": 0, "iterations": 7, "output": [1.25, 3.5]},
  "comparison": {"max_abs": 0.0, "max_rel": 0.0, "accepted": true}
}

Ne choisissez pas un epsilon global par commodité. Une tolérance absolue fonctionne près de zéro; une tolérance relative suit la croissance des grandeurs; certaines sorties exigent des limites fondées sur leurs unités; les entiers et états exacts demandent l'égalité exacte. Les NaN nécessitent une règle explicite, car l'égalité ordinaire les traite autrement que les valeurs finies. Le zéro signé peut compter si des opérations ultérieures inspectent son signe.

Comparez les échecs avec le même soin que les calculs réussis. Si l'ancien chemin signale une absence de convergence après 40 itérations et que le nouveau retourne la dernière estimation comme un succès, les tableaux peuvent être proches alors que le contrat a changé. Si l'ordre des sorties n'est pas spécifié dans le source mais que le code aval en dépend depuis des années, le comportement de production en fait une obligation de migration jusqu'à la modification des appelants.

Conservez le banc après la mise en production. Il protège les mises à niveau du compilateur, les changements d'options d'optimisation, le remplacement de bibliothèques et les futurs travaux Rust. CodeHero emploie ce type de banc de parité avec du trafic de production enregistré pendant la modernisation de l'architecture, car une suite de tests unitaires réussie ne prouve pas à elle seule qu'un système ancien conserve son comportement aux limites.

Réécrivez lorsque les coûts de possession se répètent

Portez le noyau lorsque sa conservation impose une taxe opérationnelle récurrente que l'enveloppe ne peut pas supprimer. Les raisons les plus fortes concernent la possession et le déploiement, pas une préférence de langage.

Un portage mérite une étude sérieuse lorsque le compilateur Fortran approuvé ne prend pas en charge l'environnement cible, que le build dépend de bibliothèques abandonnées ou que chaque livraison exige une machine que l'organisation cherche déjà à retirer. Il en va de même si le diagnostic en production s'arrête à un binaire opaque et que les pannes ne peuvent pas être reliées aux entrées, aux phases ou à l'usage des ressources.

Les effectifs comptent, mais « nous ne recrutons pas de développeurs Fortran » reste un argument trop faible à lui seul. Un noyau stable encapsulé peut demander très peu de travail dans ce langage. Mesurez le besoin réel : à quelle fréquence l'algorithme change-t-il, combien d'incidents exigent un diagnostic du source, qui contrôle les mises à niveau du compilateur et que se passe-t-il en l'absence du responsable actuel ? Porter uniquement pour rejoindre le langage majoritaire peut consommer un budget important sans réduire ces charges.

Une fréquence élevée de changements renforce le dossier. Si le produit ajoute souvent des termes au modèle, modifie les règles de convergence ou exige que le noyau gère l'annulation des requêtes et des erreurs structurées, chaque changement traverse la jointure. L'enveloppe accumule alors une politique qui devrait vivre dans le calcul. Rust offre la possession directe de la mémoire, des types de résultat explicites, un parallélisme contrôlé et des outils qu'une plus grande partie de l'équipe applicative peut exploiter.

La sécurité et l'isolation forment un autre argument. Si le noyau consomme des fichiers non fiables, effectue des indexations non vérifiées ou partage le processus d'un service exposé, son confinement peut devenir obligatoire avant même le portage. Exécutez-le dans un worker restreint avec des limites d'entrée pendant la réécriture. Rust réduit de nombreuses erreurs mémoire dans le code réécrit, mais ne prouve pas les mathématiques, n'empêche pas l'épuisement des ressources et ne sécurise pas une bibliothèque étrangère dangereuse.

Un dossier de décision utile convertit les problèmes récurrents en temps d'ingénierie annuel et en coûts d'infrastructure. Incluez les licences du compilateur, les machines de build spéciales, le travail de livraison, la dépendance lors des incidents, la capacité inutilisée à cause de la sérialisation et les changements de plateforme bloqués. N'inventez pas une valeur financière à la « modernité ». Chiffrez le travail que l'organisation accomplit réellement.

Les réglages du compilateur font partie du contrat

Transformez l'enveloppe en jointure de migration
CodeHero remplace l'implémentation derrière une interface stable pendant que le banc protège le comportement.

Une commande de compilation appartient au source effectif du noyau. Les options d'optimisation, réglages de virgule flottante, architecture cible, bibliothèques mathématiques liées et même l'ordre des liens peuvent modifier les résultats numériques ou révéler des hypothèses qu'un ancien build tolérait par hasard.

Récupérez le build exact avant d'évaluer une enveloppe ou un portage. Conservez l'identité et la version du compilateur, toutes les options de compilation et de liaison, les définitions du préprocesseur, les versions des bibliothèques et les variables d'environnement du build. Capturez les commandes depuis l'outil de build plutôt que de recopier un commentaire issu d'une vieille procédure d'exploitation. Reconstruisez ensuite dans un environnement propre et comparez les cas de référence. Si cette reconstruction diffère déjà, l'équipe a un problème de reproductibilité avant même la migration.

L'optimisation agressive de la virgule flottante demande une attention particulière. Avec des options mathématiques permissives, un compilateur peut réassocier des opérations, fusionner une multiplication et une addition, considérer les NaN comme impossibles ou supposer que le signe de zéro n'a pas d'importance. Ces transformations peuvent accélérer le code tout en modifiant la convergence près d'un seuil. Il ne s'agit pas de savoir si une option respecte une norme stricte en théorie. Demandez si le contrat de production permet le changement et démontrez la réponse avec le banc de comparaison.

Les bibliothèques numériques externes appartiennent également au dossier. Un appel nommé DGEMM peut garder la même interface tandis qu'une autre implémentation de BLAS change l'ordre de réduction, les threads, la sélection du processeur et les performances. Cela reste généralement acceptable dans une bonne tolérance, mais peut influer sur un solveur proche de sa limite ou surcharger un service dont la couche externe crée aussi des threads. Enregistrez l'implémentation et les réglages de threads avec chaque benchmark.

Créez un manifeste de build à côté de chaque artefact candidat. Il peut rester simple :

kernel_version=2026.08
compiler=gfortran
compiler_version=<captured from build>
compile_flags=<captured from build>
blas_implementation=<name and version>
target_cpu=<declared target>
source_digest=<repository revision>
test_corpus=<immutable corpus revision>

Les chevrons désignent des champs à remplir, pas une autorisation de laisser l'information inconnue. Faites produire ce manifeste automatiquement par le build et exposez-le par une opération de version ou le journal de démarrage. Lorsqu'une sortie change après un déploiement, l'exploitation peut déterminer si le code, le compilateur, la bibliothèque ou le corpus a bougé.

Pour l'encapsulation, cette discipline transforme le binaire Fortran en composant gouverné plutôt qu'en fichier inexpliqué copié entre serveurs. Pour le portage, elle donne aux ingénieurs Rust le comportement réel à reproduire. Les builds Rust exigent le même traitement : verrouiller les versions des dépendances, enregistrer la chaîne du compilateur, déclarer les capacités CPU cibles et séparer les options de performance des options de parité jusqu'à la mesure de leurs effets.

N'imposez pas l'égalité bit à bit comme objectif universel. Quelques archives réglementées ou points de reprise binaires peuvent l'exiger, mais elle peut figer indéfiniment une combinaison de compilateur et de matériel. La plupart des systèmes numériques ont besoin de tolérances pertinentes pour la science ou la finance, avec un accord exact sur les décisions discrètes. Documentez la catégorie de chaque sortie. Un résultat booléen d'éligibilité ne peut pas dériver d'un epsilon, même s'il provient de calculs flottants.

Un dernier piège consiste à comparer un ancien build conservateur avec un candidat doté de toutes les optimisations disponibles, puis à attribuer le résultat au langage. Exécutez une matrice qui sépare implémentation et réglages : Fortran de référence, Fortran optimisé, Rust conservateur et Rust optimisé. Cette comparaison révèle si les économies proposées viennent du portage, d'une mise à niveau du compilateur ou de l'acceptation d'un contrat numérique différent.

Le coût matériel peut condamner une enveloppe correcte

Un noyau encapsulé peut préserver parfaitement les résultats tout en devenant l'option coûteuse si son modèle d'exécution empêche une bonne utilisation du matériel disponible. Mesurez la charge complète sur le matériel que vous comptez exploiter, avec la conversion des données, les frontières de processus, les mouvements mémoire et les files d'attente.

Ne supposez pas que Rust sera plus rapide. Les compilateurs Fortran mûrs produisent un excellent code numérique, et les noyaux établis appellent peut-être déjà des routines BLAS ou LAPACK optimisées. Un portage littéral peut perdre la vectorisation, allouer plus souvent ou remplacer un appel de bibliothèque optimisé par une boucle ordinaire. Le choix du langage ne supprime ni la bande passante mémoire ni la complexité algorithmique.

La pression économique se trouve généralement ailleurs. Le noyau peut n'être compilé que pour une ancienne architecture, dépendre d'un runtime fournisseur qui limite le déploiement ou sérialiser le travail par un état global. Il peut copier de grands tableaux plusieurs fois pour s'adapter à la frontière d'un nouveau service. Des instances cloud restent alors sous-utilisées tandis que les requêtes attendent derrière une seule voie de calcul. Ces coûts système sont mesurables et peuvent justifier un portage même si un appel Fortran isolé reste rapide.

Mesurez des distributions représentatives, pas un cas favorable. Relevez au minimum le débit, la latence de queue, le pic de mémoire résidente, les octets copiés à l'interface et le temps passé à attendre la zone sérialisée. Testez les cas chauds et froids si l'initialisation compte. Fixez les versions et options du compilateur dans le résultat pour permettre sa reproduction.

Testez ensuite les alternatives les moins chères à la réécriture. Supprimer une transposition inutile, mettre les workers en pool, mettre à jour le compilateur ou remplacer une interface fichier par un tampon mémoire peut rendre assez de capacité. Dans ce cas, l'enveloppe mérite un nouveau mandat. Si le noyau bloque toujours l'architecture ou l'accélérateur retenu, le portage reçoit une obligation de performance précise au lieu d'une promesse vague d'accélération.

Un portage Rust doit préserver le comportement, pas la syntaxe Fortran

Portez le noyau avec des preuves
CodeHero réécrit les noyaux Fortran en Rust et vérifie leur comportement sur du trafic de production enregistré.

Un bon portage traduit le modèle de calcul, puis lui donne une conception dont les ingénieurs Rust peuvent devenir responsables. La translittération conserve l'ancien flux de contrôle, les tableaux globaux, les valeurs sentinelles et les habitudes d'indexation tout en y ajoutant des combats contre le borrow checker. Le résultat devient plus difficile à relire que chacune des versions d'origine.

Figez le build de référence avant toute modification. Donnez-lui des options reproductibles, des données de test immuables et une invocation lisible par une machine. La référence n'a pas besoin d'être élégante. Elle doit rester disponible jusqu'à ce que le remplacement ait réussi la comparaison en production.

Découpez le portage selon des frontières mathématiques observables par le banc de parité. Le prétraitement, le choix des coefficients, une itération du solveur, l'évaluation de la convergence et la mise en forme du résultat constituent de bonnes unités. Portez-en une, appelez-la si possible depuis le chemin existant, puis comparez les valeurs intermédiaires. Une différence reste ainsi localisée à une étape, au lieu d'obliger les ingénieurs à examiner tout un solveur.

Rendez les choix numériques explicites dans Rust. Indiquez si les valeurs utilisent f32 ou f64, comment les conversions entières traitent les débordements, quel ordre de réduction est acceptable et si une multiplication-addition fusionnée peut modifier l'arrondi. Préservez d'abord l'algorithme accepté. Améliorez-le ensuite dans une modification séparée, avec ses propres preuves, car mélanger migration de langage et révision du modèle détruit la référence nette.

Représentez les échecs par des résultats typés plutôt que par des valeurs magiques, mais gardez le comportement extérieur initial jusqu'à l'adoption volontaire du nouveau contrat par les appelants. Si le chemin Fortran émet un avertissement avec une estimation exploitable, la première version Rust ne doit pas transformer silencieusement ce cas en erreur fatale. Une meilleure API ne sert que si le système autour change avec elle.

Réservez le Rust unsafe aux frontières étrangères et aux appels de bibliothèques audités. Du Rust pur peut encore paniquer sur un index, allouer sans limite pratique ou produire des NaN à partir d'opérations flottantes valides. Fixez des limites et renvoyez les échecs à la frontière du service. La sécurité mémoire résout une catégorie de pannes, pas le contrat d'exploitation.

Gardez la décision réversible jusqu'à ce que les preuves tranchent

Le programme le plus sûr retarde le choix irréversible tout en améliorant les deux options. Créez d'abord un build Fortran reproductible et un banc de parité. Placez ensuite le noyau existant derrière l'interface que vous voudriez même si son implémentation changeait. Ce travail est nécessaire à une bonne enveloppe et reste utile au portage.

Exécutez la version encapsulée sous une charge représentative. Vous disposez alors de preuves sur le coût de conversion, la concurrence, l'isolation des pannes et la responsabilité opérationnelle. Si elle respecte l'objectif de service et que l'équipe sait maintenir son build, arrêtez-vous. Garder du bon Fortran est une décision d'ingénierie, pas un manque d'ambition.

Si elle échoue, la même interface devient la frontière du remplacement Rust et les mêmes cas enregistrés évaluent chaque unité migrée. Déployez une comparaison en mode fantôme lorsque l'environnement le permet : exécutez le candidat sans lui confier la réponse, notez les différences bornées et examinez les cas hors tolérance. Protégez les entrées sensibles et plafonnez le calcul supplémentaire. L'exécution fantôme est une technique de test, pas une autorisation de dupliquer les données sans précaution.

Définissez les critères de sortie avant que le portage prenne son propre élan. Ils doivent couvrir la parité acceptée, les performances sur les machines visées, le comportement d'échec, les diagnostics d'exploitation et le retrait de l'ancien runtime. Définissez aussi la fenêtre de retour et les preuves nécessaires pour retirer la référence. Sans ces conditions, les équipes gardent deux implémentations sans fin et paient les deux coûts de possession.

La décision peut alors être formulée sans idéologie. Encapsulez lorsque le calcul est stable, l'interface étroite et la chaîne d'outils exploitable. Réécrivez lorsque les contraintes récurrentes de possession ou de matériel coûtent plus cher qu'un portage mesuré, et lorsque l'organisation peut comparer la nouvelle implémentation à l'ancienne. Si vous ne pouvez encore démontrer aucune de ces affirmations, consacrez la prochaine journée d'ingénierie au banc de parité plutôt qu'à traduire une boucle.

FAQ

Un vieux code Fortran est-il automatiquement dangereux ?

Non. L'âge et le langage ne disent pas si un noyau est sûr ou correct. Examinez le traitement des entrées, la mémoire, l'état mutable, les hypothèses du compilateur et la frontière de déploiement avant de juger.

Rust peut-il appeler directement une bibliothèque Fortran ?

Oui, par une ABI C compatible exposée avec ISO_C_BINDING et BIND(C) côté Fortran. Gardez l'appel étranger dans un petit module Rust unsafe et transmettez des tampons numériques simples, leurs longueurs et des codes d'état.

Réécrire Fortran en Rust accélérera-t-il le calcul ?

Pas forcément. Un code Fortran mûr peut déjà bien se vectoriser ou appeler des bibliothèques numériques optimisées. Mesurez la charge complète, car les copies, la sérialisation, la mémoire et les contraintes de déploiement comptent souvent davantage que la syntaxe des boucles.

Comment comparer les résultats flottants après le portage ?

Utilisez des tolérances absolues, relatives ou fondées sur les unités pour chaque sortie, avec des règles pour NaN, les infinis et le zéro signé. Comparez aussi les états et la convergence; des tableaux proches ne prouvent pas un comportement identique.

Faut-il encapsuler Fortran dans le processus du service web ?

Seulement après avoir vérifié son état, ses erreurs et son comportement concurrent. Un noyau qui appelle STOP, modifie des tableaux SAVE ou lit des fichiers globaux au processus doit souvent vivre dans un worker isolé.

Quel est le plus grand risque d'une enveloppe Fortran ?

L'état caché est souvent la surprise la plus coûteuse. Une signature propre peut masquer l'ordre d'initialisation, des blocs COMMON, des fichiers, des graines aléatoires et des espaces de travail non sûrs entre threads.

Quelle part du noyau faut-il porter à la fois ?

Portez la plus petite unité mathématique observable par le banc de parité. Comparer les étapes intermédiaires localise bien mieux la dérive numérique que remplacer tout le solveur dans une seule livraison.

Faut-il garder un spécialiste Fortran après l'encapsulation ?

Il faut quelqu'un capable de reconstruire, diagnostiquer et relire les changements, sans que ce soit forcément un poste à temps plein. Si une seule personne indisponible peut le faire, la dépendance n'est pas maîtrisée.

Quand le coût du compilateur justifie-t-il un portage Rust ?

Lorsque les licences, machines de build limitées, livraisons ou plateformes bloquées coûtent régulièrement plus que le portage mesuré et sa validation. Placez de vraies factures et des heures d'ingénierie dans la comparaison.

Peut-on améliorer l'algorithme pendant la migration Rust ?

Faites-le dans une modification séparée après la parité comportementale. Mélanger portage du langage et révision du modèle supprime la référence fiable et rend chaque différence plus difficile à expliquer.