GreenIT
Master 2 · Polytech UCALab 3D. Lopez Pacheco

Mesurer
l'énergie d'un programme.

Un correcteur orthographique maison, mesuré avec CodeCarbon. Même énergie en France et en Allemagne, mais 6,8 fois plus de CO2 en Allemagne. Puis un défi : faire tomber la consommation en optimisant l'algorithme, puis en changeant de technologie.

Ce que le lab démontre. Émissions = énergie x intensité. L'énergie se réduit en écrivant mieux le code, l'intensité dépend du pays et de l'heure. Les deux leviers sont indépendants.
56g/kWh
France, moyenne annuelle
381g/kWh
Allemagne, moyenne annuelle
22 à 81g/kWh
la France, selon le jour
0,25Wh
spellchecker rapidfuzz, 90 s
En 30 secondes

CodeCarbon estime l'énergie d'un programme (puissance du matériel, durée) et la multiplie par l'intensité carbone de l'électricité. En offline, l'intensité est la moyenne annuelle du pays ; en online, elle est instantanée (Electricity Maps) ; on peut aussi la forcer. La distance entre deux mots est la distance de Damerau-Levenshtein (une transposition compte 1). Un spellchecker naïf compare chaque mot inconnu à tout le dictionnaire (235 976 mots) : trop lent pour être mesuré. On l'améliore par des buckets de longueur et une DP plafonnée, puis par du C (rapidfuzz), seul à finir en 90 s.

01 · L'outil

CodeCarbon : de la puissance au CO2

CodeCarbon (version 3.3.1 requise : la 2.2.2 n'a ni detect ni --offline sur monitor) enveloppe un programme, estime la puissance du CPU, de la RAM et du GPU, la multiplie par la durée, puis par l'intensité carbone. codecarbon detect décrit la machine : ici un Apple M4 Pro, 12 threads, 24 Go.

codecarbon monitor --offline --country-iso-code FRA -- python3 wdistance.py
ÉNERGIE

energy_consumed (kWh)

Puissance estimée par composant, durée, et en mode process la part d'utilisation du processus. Dans le lab : environ 4,6e-8 kWh pour wdistance.py.

ÉMISSIONS

emissions (kgCO2eq)

Énergie x intensité carbone. C'est la colonne qu'on compare entre pays.

SORTIE

emissions.csv

Une ligne par exécution : durée, puissances, énergie, pays, version, matériel. Base des graphiques du lab.

02 · L'intensité carbone

Même énergie, pays différent

Dix exécutions en France, dix en Allemagne, avec le même programme. Bascule entre l'énergie et les émissions :

Instrument France contre Allemagne, mesures du lab (10 runs chacun)
Ce qu'on observe x 6,8
Intensité FRA56 g/kWh
Intensité DEU381 g/kWh
Mixnucléaire / charbon-gaz

Quatre façons de fixer l'intensité

OFFLINE

Moyenne annuelle du pays

--offline --country-iso-code FRA : pas de réseau, intensité moyenne de l'année. FRA 56, DEU 381 g/kWh.

ONLINE

Instantanée, via Electricity Maps

Sans --offline et avec une clé API : l'intensité au moment du run. Exige un experiment_id obtenu par codecarbon config. Mesuré : 22 g/kWh en France ce jour-là.

FORCÉE

force_carbon_intensity_g_co2e_kwh

Une valeur imposée dans le fichier de config, sans clé ni compte. Le 6 janvier 2026 en France : 81 g/kWh. Vérifié : émissions / énergie = 81,0 exactement.

POURQUOI 81 ?

Le froid

Hiver : forte demande de chauffage, recours au gaz et aux imports carbonés, peu de solaire. Le carbone d'un même kWh varie selon la saison et le jour.

Piège du fichier de config

Pour l'offline, le fichier ~/.codecarbon.config doit être [codecarbon] nu. Si le token Electricity Maps y traîne, CodeCarbon interroge l'intensité en direct de ta vraie position et ignore --country-iso-code : FRA et DEU renvoient alors la même valeur (22 g/kWh). Le token ne sert qu'aux exercices online.

Même énergie, quatre intensités : ce que ça change pour le spellchecker rapidfuzz du lab (0,251 Wh) ou pour un petit script.

Instrument Émissions d'un même programme selon l'intensité
Énergie du programme -
FormuleE x I

Pour 0,251 Wh (0,000251 kWh) en France offline : 0,000251 x 56 = 14 mg, ce que le lab mesure (14 mgCO2eq).

03 · Le programme

La distance entre deux mots

Le lab demande le nombre minimal de modifications pour passer d'un mot à l'autre. Test imposé : hievr contre hiver doit valoir 1. Or c'est une transposition de deux lettres adjacentes (e et v), que Levenshtein simple compte pour 2. Il faut la variante Damerau-Levenshtein (OSA) où une transposition adjacente est une opération.

d[i][j] = min( d[i-1][j] + 1,               // suppression
                d[i][j-1] + 1,               // insertion
                d[i-1][j-1] + (a[i]!=b[j]) ) // substitution ou égalité
si a[i]==b[j-1] et a[i-1]==b[j] :             // transposition adjacente
    d[i][j] = min( d[i][j], d[i-2][j-2] + 1 )

Regarde la matrice se remplir ligne par ligne. Les cases marquées * sont celles où la transposition fait gagner un point. Choisis un des quatre tests du sujet, ou tape tes propres mots (9 lettres max) :

valide avec Entrée ou en quittant le champ
0 / 0
Prêt

04 · Le spellchecker

Comparer chaque mot inconnu au dictionnaire

Le programme charge words (235 976 mots) dans un set (test d'appartenance en O(1)), lit Tom Sawyer, nettoie chaque mot et, s'il n'est pas dans le dictionnaire, cherche jusqu'à 10 remplacements à distance 1 et 10 à distance 2. Sur le livre : 3136 mots inconnus (dialecte, en-tête Gutenberg). Chacun déclenche un scan du dictionnaire.

NAÏF

Scan complet, DP complète

Chaque mot inconnu contre les 235 976 mots. Au plus 740 millions de calculs de distance. Non terminé après plus de 10 minutes.

CHALLENGE 1

Algorithme et structure

Buckets par longueur et DP plafonnée à 2. Exact à 100 %. Toujours plus de 5 minutes en Python pur.

CHALLENGE 2

Autre technologie

rapidfuzz (distance en C) avec score_cutoff=2. 90 s, 0,251 Wh, 14 mgCO2eq. Seul à finir vite.

Combien de mots le bucket évite-t-il de comparer ?

Une distance au plus 2 impose un écart de longueur au plus 2 : pour un mot de L lettres, seuls les mots de longueur L-2 à L+2 sont candidats. Voici l'effet, calculé sur le vrai fichier words :

Instrument Fenêtre de longueur ±2 sur le vrai dictionnaire
Gain du bucket seul -
Comparaisons (3136 mots)-

Pour les longueurs courantes (5 à 12 lettres), la fenêtre garde encore 25 à 57 % du dictionnaire : le bucket seul ne gagne qu'un facteur 2 à 4. C'est la DP plafonnée (abandon dès qu'une ligne dépasse 2) et surtout le C qui changent l'ordre de grandeur.

def suggest(word, by_len):
    d1, d2 = [], []
    for length in range(len(word) - 2, len(word) + 3):
        for cand in by_len.get(length, ()):
            dist = DamerauLevenshtein.distance(word, cand, score_cutoff=2)   # rapidfuzz, en C
            if dist == 1 and len(d1) < 10: d1.append(cand)
            elif dist == 2 and len(d2) < 10: d2.append(cand)
    return d1, d2
Lien avec le cours

C'est exactement la démarche du Lab 1 : éviter du travail (buckets, plafond), puis changer la technologie (C). L'ordre attendu d'énergie est naïf, puis Python optimisé, puis rapidfuzz.

05 · Le score

Précision et énergie, dans un seul nombre

Le lab note un spellchecker sur deux axes : l'énergie moyenne consommée (Wh) et la précision.

score = 0,1 / ( avenergy x ( 1 + somme |distance_optimale - distance_trouvée| ) )

Un spellchecker exact (OSA) a une somme d'écarts nulle : seul compte l'énergie. Avec rapidfuzz (0,251 Wh) : 0,1 / 0,251 = 0,40. Joue avec les deux termes :

Instrument Calculatrice de score
Score -
1 + somme des écarts-

Diviser l'énergie par deux double le score. Une seule erreur de distance (somme = 1) le divise par deux : un algorithme approximatif doit être deux fois plus sobre pour compenser une seule erreur. C'est pourquoi le challenge 1 exige un résultat 100 % exact.

06 · Questions du lab corrigées

Les dix étapes

Exo 1

La distance entre mots

Damerau-Levenshtein, tests imposés.

▸
def wdistance(a, b):
    m, n = len(a), len(b)
    d = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(m + 1): d[i][0] = i
    for j in range(n + 1): d[0][j] = j
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            cost = 0 if a[i-1] == b[j-1] else 1
            d[i][j] = min(d[i-1][j] + 1, d[i][j-1] + 1, d[i-1][j-1] + cost)
            if i > 1 and j > 1 and a[i-1] == b[j-2] and a[i-2] == b[j-1]:
                d[i][j] = min(d[i][j], d[i-2][j-2] + 1)      # transposition
    return d[m][n]
PaireDistancePourquoi
hievr / hiver1une transposition (e et v)
hiv / hiver2insérer e et r
ver / hiver2insérer h et i
vre / hiver3insérer h, i puis une transposition (r et e)

On teste aussi les mots échangés : la distance est symétrique (les insertions d'un sens sont les suppressions de l'autre).

Piège

Levenshtein simple donne 2 pour hievr / hiver et fait échouer le test. C'est la variante OSA (une transposition adjacente = 1) qui est demandée.

Exo 2

Installer CodeCarbon

pip, fichier de configuration, detect.

▸
pip install --user --break-system-packages "codecarbon[carbonboard]" rapidfuzz
echo "[codecarbon]" > ~/.codecarbon.config
codecarbon detect

Sur un Python système géré (PEP 668), --break-system-packages est nécessaire (ou un environnement virtuel). Il faut codecarbon 3.3.1 : la 2.2.2 livrée avec Anaconda n'a ni detect ni --offline sur monitor. detect affiche le matériel : Apple M4 Pro, 12 threads, 24 Go.

Exo 3

Offline : France contre Allemagne

Dix runs chacun, comparer énergie et émissions.

▸
for i in $(seq 10); do codecarbon monitor --offline --country-iso-code FRA -- python3 wdistance.py; done
for i in $(seq 10); do codecarbon monitor --offline --country-iso-code DEU -- python3 wdistance.py; done
Paysenergy_consumedemissionsintensité implicite
France4,6e-8 kWh2,6e-9 kgCO2eq56 g/kWh
Allemagne4,5e-8 kWh1,7e-8 kgCO2eq381 g/kWh

c Énergie

Identique dans les deux pays : elle dépend du matériel et du code, pas de la localisation.

d Émissions

Environ 6,7 fois plus en Allemagne (le rapport des intensités : 381 / 56 = 6,8). Le mix allemand est plus carboné (charbon, gaz) que le français (nucléaire). Même énergie, plus de CO2.

Note : wdistance.py ne dure que des microsecondes, l'énergie est minuscule. La charge utile à mesurer est le spellchecker.

Exo 4-5

Le spellchecker, puis 5 runs en France

Naïf, format de sortie, mesure.

▸

a Structure de données

Un set pour le dictionnaire : le test « le mot existe-t-il ? » est fait pour chaque mot du livre, donc il doit être en O(1).

c Nettoyage

def clean(token):
    kept = [c for c in token.lower() if c.isalpha() or c == "'"]
    return "".join(kept).strip("'")      # "I'd" et "You'll" gardés, "'abc'" devient "abc"

d Sortie

"abc" does not exist. Did you mean ['...distance 1...'] ['...distance 2...']

5 Mesure

for i in $(seq 5); do codecarbon monitor --offline --country-iso-code FRA -- python3 spellchecker.py; done

La version naïve n'a pas terminé après plus de 10 minutes (3136 mots inconnus x 235 976 mots) : point de départ à optimiser, non mesurable telle quelle.

Exo 6

Mode online

Intensité instantanée, et pourquoi elle est plus basse.

▸
  1. Créer un compte sur dashboard.codecarbon.io et un compte Electricity Maps (gratuit avec un mail universitaire), puis une clé API Electricity Maps.
  2. codecarbon login ouvre le navigateur et crée credentials.json.
  3. Supprimer ~/.codecarbon.config, lancer codecarbon config (interactif : organisation, projet, expérience) : il écrit les identifiants dont l'experiment_id.
  4. Ajouter electricitymaps_api_token = <clé> au fichier, puis codecarbon monitor -- python3 programme.py (sans --offline).
Sécurité

credentials.json et la clé Electricity Maps sont des secrets : ne jamais les committer ni les coller dans un compte rendu.

Émission plus haute, égale ou plus basse que sans Electricity Maps ? Ici plus basse : l'API renvoie 22 g/kWh pour la France ce jour-là, contre 56 g/kWh de moyenne annuelle en offline. Beaucoup de nucléaire et de renouvelable à cet instant. Le résultat peut aussi être plus haut un autre jour (exercice 7).

Statut

Les étapes de connexion sont interactives (invites et navigateur) : elles se font à la main dans un terminal. Le jeton Electricity Maps a été validé auprès de l'API (GET /v3/carbon-intensity/latest?zone=FR, en-tête auth-token).

Exo 7

L'effet de la météo

Le 6 janvier 2026, intensité forcée.

▸

a Récupérer l'intensité

Developer Hub d'Electricity Maps, France, temporalité past, granularité daily, ou l'API carbon-intensity/past?zone=FR&datetime=2026-01-06T12:00:00Z : 81 g/kWh.

b La forcer dans CodeCarbon

[codecarbon]
force_carbon_intensity_g_co2e_kwh = 81

c Vérifier

Après les runs, émissions / énergie = 81,0 g/kWh exactement : l'intensité imposée est bien appliquée.

d Pourquoi plus haute ce jour-là (81 contre 22) ?

Hiver et froid : forte demande de chauffage électrique, recours au gaz et aux importations carbonées pour couvrir la pointe, et peu de solaire. L'intensité d'un kWh n'est pas une constante du pays : elle varie avec la saison, le jour et l'heure.

Exo 8

Le score

Précision et énergie.

▸

Score = 0,1 / (avenergy x (1 + somme |distance optimale - distance trouvée|)), avec avenergy l'énergie moyenne en Wh de tous les runs (colonne energy_consumed x 1000).

Le spellchecker renvoie la distance exacte donc la somme des écarts vaut 0. Avec la version rapidfuzz mesurée (0,251 Wh) : score = 0,1 / 0,251 = 0,40. À faire valider par l'encadrant.

Ch. 1-2

Les deux challenges

Algorithme puis technologie, sans perdre en précision.

▸

1 Challenge 1 : algorithme et structure

Précision 100 % conservée, deux optimisations : buckets par longueur (distance <= 2 implique un écart de longueur <= 2, donc on ne compare qu'aux mots de longueur L-2 à L+2) et DP plafonnée (dès qu'une ligne de la matrice dépasse le seuil 2, on abandonne car la distance ne pourra plus redescendre). Vérifié identique au naïf sur des mots de test.

2 Challenge 2 : autre technologie

Même structure en buckets, mais la distance vient de rapidfuzz (DamerauLevenshtein.distance(..., score_cutoff=2), en C). Résultats identiques au Python pur, bien moins d'énergie CPU par appel.

VersionDuréeÉnergieCO2 (FRA)
Naïfplus de 10 min, non terminénon mesurénon mesuré
Challenge 1 (Python)plus de 5 minnon mesurénon mesuré
Challenge 2 (rapidfuzz)90 s0,251 Wh14 mgCO2eq
Honnêteté de la mesure

Seule la version rapidfuzz a été mesurée jusqu'au bout. Les deux autres sont trop lentes en Python pur pour une campagne complète : on ne les compare donc qu'en ordre attendu (naïf plus coûteux que fast, plus coûteux que rapidfuzz), pas en chiffres.

07 · Réviser

QCM et cartes mémo

Le QCM tire un échantillon au hasard dans le pool ; chaque réponse est corrigée et expliquée.

Entraîneur Pour un QCM qui s'adapte à ton niveau, revient sur tes erreurs et explique chaque réponse avec une fiche illustrée : s'entraîner sur ce chapitre.

Prêt

Pool de 22 questions. Tire un échantillon au hasard, correction immédiate et explication à chaque réponse.

Cartes mémo

CodeCarbon
Comment il calcule ?
clique pour la réponse ↻
CodeCarbon
Énergie (puissance estimée x durée) x intensité carbone. Sortie : emissions.csv.
CodeCarbon
Version requise ?
clique pour la réponse ↻
CodeCarbon
3.3.1 : la 2.2.2 n'a ni detect ni --offline.
Offline
Intensité en offline ?
clique pour la réponse ↻
Offline
Moyenne annuelle du pays : --offline --country-iso-code FRA (56 g/kWh), DEU (381).
Offline
FRA contre DEU ?
clique pour la réponse ↻
Offline
Énergie identique, émissions x6,8 en Allemagne (381 / 56) : charbon et gaz contre nucléaire.
Offline
Piège du fichier de config ?
clique pour la réponse ↻
Offline
Doit être [codecarbon] nu en offline. Un token Electricity Maps fait ignorer le code pays.
Online
Prérequis online ?
clique pour la réponse ↻
Online
Un experiment_id (codecarbon login puis codecarbon config, interactifs) et une clé Electricity Maps.
Online
Intensité forcée ?
clique pour la réponse ↻
Online
force_carbon_intensity_g_co2e_kwh = 81. Vérif : émissions / énergie = 81,0.
Météo
Pourquoi 81 g/kWh le 6 janv. 2026 ?
clique pour la réponse ↻
Météo
Froid : chauffage, gaz et imports carbonés, peu de solaire. En direct : 22 g/kWh.
Distance
OSA ?
clique pour la réponse ↻
Distance
Damerau-Levenshtein : une transposition adjacente = 1. hievr / hiver = 1 (Levenshtein simple : 2).
Distance
Les 4 tests du sujet ?
clique pour la réponse ↻
Distance
hievr/hiver 1, hiv/hiver 2, ver/hiver 2, vre/hiver 3, et symétrique.
Spellchecker
Structure du dictionnaire ?
clique pour la réponse ↻
Spellchecker
Un set : appartenance en O(1). 235 976 mots, 3136 mots inconnus dans Tom Sawyer.
Spellchecker
Challenge 1 ?
clique pour la réponse ↻
Spellchecker
Buckets par longueur (L±2) et DP plafonnée à 2. Exact à 100 %.
Spellchecker
Challenge 2 ?
clique pour la réponse ↻
Spellchecker
rapidfuzz (C) avec score_cutoff=2 : 90 s, 0,251 Wh, 14 mgCO2eq.
Score
Formule du score ?
clique pour la réponse ↻
Score
0,1 / (avenergy x (1 + somme des écarts)). Exact et 0,251 Wh : 0,40.