---
from: qwen
to: dom
date: 2026-06-08T11:55:00+02:00
topic: validation-independante-c40441d
status: open
priority: high
references:
  - commit: c40441d
  - mission: docs/coordination/inbox/for-qwen/2026-06-08_11-12_dom-via-codex_validation-c40441d-perf-fp.md
---

# Validation independante c40441d

## 1. Resume du commit

`c40441d fix(perf): apply MVP threading hotfix` — 120 lignes modifiees dans
`anonymizer_core_refactored_onnx.py`, 1 ligne dans `config/dictionnaires.default.yml`,
3 lignes dans `config/profiles.yml`, 20 fichiers archives (coordination).

### Modifications code

| Zone | Changement | Lignes |
|---|---|---|
| Env vars multi-cœur (H1) | `os.environ.setdefault` pour OMP/MKL/OPENBLAS/NUMEXPR/VECLIB AVANT imports lourds | 10 |
| `_configure_torch_threads()` | Idempotent, `set_num_threads(n_cpus)` + `set_num_interop_threads(min(n,8))` avec guard | 27 |
| `_get_doctr_model()` | Cache + appel `_configure_torch_threads()` avant chargement | 6 |
| `_run_ner_on_original_text()` | Appel `_configure_torch_threads()` (PDF natif sans OCR) | 5 |
| `redact_pdf_raster()` | ThreadPoolExecutor en frozen + fallback + log PERF + env `ANON_DISABLE_RASTER_THREADS` | ~40 |
| `dictionnaires.default.yml` | CONCERTATION retire des force_terms | 1 |
| `profiles.yml` | Force_terms redondants retires | 3 |

## 2. Validation des risques techniques

### 2.1 Env vars posees avant imports lourds

**✅ VERIFIE** — `os.environ.setdefault` aux lignes 32-34, premier import lourd
(pdfplumber) ligne 45. PIL ligne 48, fitz ligne 51, numpy ligne 1299.

L'ordre est correct : les variables sont lues par numpy/torch/onnxruntime a
leur initialisation, donc elles doivent etre posees avant tout import transif.
C'est le cas.

### 2.2 Idempotence `torch.set_num_interop_threads`

**✅ VERIFIE** — Le flag global `_TORCH_THREADS_CONFIGURED` empeche un 2e appel.
`set_num_interop_threads` est dans un `try/except` interne avec `pass` si
l'API refuse (deja figuree par un travail torch anterieur).

Test empirique : 3 appels successifs sans exception, log unique
`torch threads config: intra=32 inter=8 (CPUs=32)`.

### 2.3 Appel H1 pour OCR et PDF natif/NER

**✅ VERIFIE** — Deux points d'appel :
- `_get_doctr_model()` → chemin OCR (PDF scanne, texte extrait via docTR)
- `_run_ner_on_original_text()` → chemin PDF natif (texte riche, OCR saute)

Les deux chemins couvrent les deux modes de traitement. L'appel dans
`_run_ner_on_original_text()` est un complement de Claude par rapport a ma
version initiale (qui ne couvrait que l'OCR). C'est necessaire car le NER
torch (EDS-Pseudo, GLiNER) tourne sur le texte original et serait mono-thread
sans cet appel.

### 2.4 Rollback `ANON_DISABLE_RASTER_THREADS=1`

**✅ VERIFIE** — Le code lit :
```python
disable_threads = os.getenv("ANON_DISABLE_RASTER_THREADS", "").lower() in {"1", "true", "yes", "on"}
```
5 valeurs reconnues. En mode frozen avec cette variable, le raster revient
en sequentiel. Le log indique `reason=env_disabled`.

### 2.5 Risque thread-safety PyMuPDF/Pillow en frozen

**⚠️ ATTENTION — risque identifie**

`ThreadPoolExecutor` partage le meme processus. PyMuPDF (`fitz`) et Pillow
sont-ils thread-safe ?

- **PyMuPDF** : la doc officielle dit que chaque objet `fitz.Document` et
  `fitz.Page` doit etre utilise dans un seul thread. Le code raster utilise
  un `fitz.open()` **par thread** (dans `_rasterize_page`), donc pas de
  partage d'objet entre threads. ✅ OK.
- **Pillow** : `Image.frombytes` et `Image.save` sont thread-safe pour des
  operations independantes sur des objets separes. ✅ OK.
- **GIL Python** : les operations lourdes (rasterisation PyMuPDF, encodage
  PNG Pillow) liberent le GIL car ce sont des extensions C. Le
  multi-threading apporte donc un vrai gain parallele. ✅ OK.

**Conclusion** : le risque est mitigé par l'isolation des objets `fitz` par
thread. Aucun conflit identifie.

### 2.6 Absence de changement de detection PII

**✅ VERIFIE** — Le diff ne modifie aucune logique de detection. Seuls les
points suivants changent :
- CONCERTATION retire de `dictionnaires.default.yml` (force_terms)
- Force-terms redondants retires de `profiles.yml`
- Commentaire mis a jour dans `_kv_value_only_mask` (`CHUXX, sigle local...`)

Aucune modification des regex, NER, gazetteers, ou logique de propagation.

## 3. Tests unitaires

**98 passed, 0 failed** avec `.venv/bin/python -m pytest tests/unit -q`.

Le test 009 (Biarritz, pyahocorasick) passe dans le venv car la dependance
est installee. Mon test precedent avec `python3` systeme (97 passed) etait
un artefact d'environnement, pas une regression.

## 4. Mini-corpus pdf_natif (6 PDF natifs)

| Fichier | PII hits | Force terms | CONCERTATION |
|---|---|---|---|
| FC14.pdf | 45 | 0 | ✅ absent |
| FC16.pdf | 45 | 0 | ✅ absent |
| FC17.pdf | 45 | 0 | ✅ absent |
| FC19.pdf | 45 | 0 | ✅ absent |
| FC21.pdf | 45 | 0 | ✅ absent |
| FC8.pdf | 44 | 0 | ✅ absent |

**Evaluateur qualite** :

```
SCORE GLOBAL : 100.0/100  [A+]
  Leak score : 100.0/100
  FP score   : 100/100

Fuites noms audit      : 0
Fuites regex (PII)     : 0
Noms INSEE (contexte fort) : 0
Termes médicaux masqués : 0
Alertes sur-masquage    : 0
```

**CONCERTATION** : ✅ Aucun force-term genere sur les 6 PDF. Le retrait du
dictionnaire est valide.

## 5. Matrice de validation Windows

### Scenarios de test

| # | Scenario | Fichier attendu | Mesures |
|---|---|---|---|
| 1 | PDF natif court (<5 pages) | FC8.pdf ou equivalent | Temps <5s, CPU >30%, RAM <4 Go |
| 2 | PDF natif moyen (10-30 pages) | FC14.pdf (4 pages) ou plus long | Temps proportionnel, CPU >30% |
| 3 | PDF scanne court (<5 pages) | PDF scanne disponible | Temps <30s, CPU >30%, mode raster=threads |
| 4 | PDF reel lent Dom | Fourni par Dom | Temps avant/apres, CPU, RAM, mode raster |

### Mesures attendues dans `anonymisation.log`

| Ligne log | Valeur attendue (avant) | Valeur attendue (apres) |
|---|---|---|
| `torch threads config: intra=N inter=M (CPUs=X)` | Absente | `intra=8 inter=8 (CPUs=8)` (machine Dom) |
| `Raster PDF: mode=threads pages=N workers=W dpi=D frozen=1` | Absente (sequential) | Present si PDF >2 pages |
| `PERF ... stage=...` | Present | Present (unchanged) |
| `Raster PDF: mode=sequential ... reason=env_disabled` | N/A | Si `ANON_DISABLE_RASTER_THREADS=1` |
| CPU processus | ~12% | >40% |
| RAM pic | 16 Go | Similaire ou legerement superieur |

### Criteres GO/NO-GO

| Critere | GO | NO-GO |
|---|---|---|
| Leak score | 100/100 | <100 |
| FP score | 100/100 | <95 |
| Temps total PDF lent | <50% du temps precedent | >= temps precedent |
| CPU moyen | >30% | <20% |
| Crash/erreur | Aucun | 1+ |
| RAM pic | <20 Go | >24 Go |

## 6. Avis

**GO conditionnel** pour rebuild Windows, sous reserve que :

1. Le push `c40441d` vers Gitea soit fait (bloquant pour Windows)
2. Le test PDF reel de Dom confirme le gain CPU/temps
3. Aucun crash thread-safety ne remonte

**NO-GO** si le PDF reel montre :
- Une regression leak (force_term retire trop tot pour un cas non teste)
- Un crash PyMuPDF en mode threads (rare mais possible avec certains PDF)
- Un gain CPU negligible (<10%) malgre la config threads
