535 lines
35 KiB
Markdown
535 lines
35 KiB
Markdown
# Parallélisme interne borné
|
||
|
||
## Statut
|
||
|
||
**INTERNAL_PARALLELISM_COMPUTE_RESOURCES_V1 — PASS / FROZEN.**
|
||
**COMPUTE_GOVERNOR_V2 — PASS / FROZEN.**
|
||
**ORB_VULKAN_ASYNC_EXECUTION — PASS / FROZEN.**
|
||
|
||
Ce document décrit le contrat validé pour `features.extract`,
|
||
`features.extract.sift`, `visual_index.update`, `candidate_pair.generate` et
|
||
`matcher.run`, ainsi que la maintenance outer-parallel du
|
||
`geometric_verifier.run`. Le gel v1 porte sur le parallélisme interne borné, les
|
||
réservations Governor et les sorties canoniques. Il ne préjuge pas du choix
|
||
opérationnel CPU/Vulkan ajouté par la tranche v2.
|
||
|
||
## Frontière runtime
|
||
|
||
La Task Queue conserve un seul callback actif. Une Task peut exploiter à
|
||
l'intérieur de ce callback les `cpu_threads` effectivement admis par le
|
||
Resource Governor. Ce parallélisme interne ne crée ni pool global, ni deuxième
|
||
scheduler, ni file de travail persistante. Le Governor dérive un masque privé
|
||
depuis l'affinité permise et la topologie package/core Linux. La cible par
|
||
défaut réserve quatre CPU logiques lorsque praticable et conserve au moins un
|
||
CPU de calcul sur les petits hôtes. Avec une topologie fiable, un sous-ensemble
|
||
déterministe de coeurs physiques complets, frères SMT inclus, satisfait cette
|
||
cible avec le dépassement minimal. Les CPU exclus par une affinité externe
|
||
comptent déjà dans la réserve. Si topologie ou affinité manque, seul le budget
|
||
de compte portable est conservé et aucun masque/ID n'est inventé.
|
||
|
||
Sur l'hôte unrestricted 16-CPU validé, cette politique donne `0-5,8-13` pour le
|
||
calcul lourd et `6,7,14,15` pour le desktop, l'audio et l'interaction. Ces IDs
|
||
restent une preuve de cet hôte et ne bornent aucune autre machine.
|
||
|
||
L'unique worker Queue applique et vérifie son propre masque (`pid=0`) avant les
|
||
callbacks ; le creator/main/TUI reste inchangé et les enfants créés depuis le
|
||
worker héritent normalement de son affinité. Aucun TID auxiliaire énuméré n'est
|
||
passé à `sched_getaffinity()` ou `sched_setaffinity()` : retenir un
|
||
`PIDFD_THREAD` ne réserve pas le numéro TID consommé par ces interfaces. Avant
|
||
toute création de pthread applicatif et toute initialisation Vulkan, le
|
||
démarrage établit donc `MESA_SHADER_CACHE_DISABLE=true`. L'absence de variable
|
||
prend ce défaut sûr et une valeur explicite exacte `true` ou `1` est respectée ;
|
||
une valeur explicite fausse ou malformée est préservée mais le démarrage est
|
||
refusé. Sur la 780M validée, cette politique supprime les helpers de cache
|
||
`*:disk$0` qui élargissaient leur masque, et les threads runtime restants
|
||
conservent le compute-pool hérité. Elle est inoffensive hors Mesa, n'est ni
|
||
persistée ni scientifique, et son état/sa raison sont diagnostiqués sans faux
|
||
indicateur de recontrainte auxiliaire.
|
||
Le backend Vulkan possède en plus une barrière tardive non mutante : avant son
|
||
premier appel Mesa, une requête non vide exige la valeur exacte `true` ou `1`.
|
||
Absent, faux ou malformé produit un backend `UNAVAILABLE` mémorisé et aucune
|
||
sortie partielle. `backend_info` et les requêtes vides restent non initialisants.
|
||
Cette défense couvre les consumers publics ou de feasibility qui ne traversent
|
||
pas la Queue ; seuls leurs `main` autonomes peuvent prendre le défaut sûr avant
|
||
tout pthread.
|
||
Le compute-pool borne `cpu_threads` à l'admission ; il n'existe aucun plafond
|
||
global 12. Un échec d'application est
|
||
diagnostiqué sans altérer Task, Queue, durabilité ou science. Cette couture v2
|
||
est **PASS / FROZEN**, sans persistance ni ABI publique. Elle complète le gel
|
||
v1 sans le redéfinir.
|
||
|
||
## Audit GPU — 29 août 2026
|
||
|
||
Cet audit est une constatation de capacité et une recommandation de périmètre.
|
||
La tranche v2 en tire une politique GPU-first seulement pour un backend validé,
|
||
déterministe et mesurément supérieur. La machine contrôlée
|
||
expose `AMD Radeon 780M Graphics (RADV PHOENIX)`, iGPU Vulkan API 1.4.354 sous
|
||
Mesa 26.2.1. OpenCV 5.0.0 y indique le chargement dynamique de Vulkan et
|
||
d'OpenCL, avec TBB comme framework parallèle ; ses modules CUDA ne sont pas
|
||
disponibles. L'outil `clinfo` n'était pas installé : cette absence ne prouve ni
|
||
ne valide une exécution OpenCL. Elle ne constitue donc pas une couture GPU
|
||
utilisable par Lardon3D.
|
||
|
||
La 780M est UMA. Les fichiers amdgpu de l'hôte publient un petit aperture VRAM
|
||
de 512 Mio mais un GTT système de 7 986 020 352 octets ; Hardware Profile garde
|
||
le premier comme capacité de payload observable et le classe shared d'après ces
|
||
preuves bornées, sans liste de device IDs. Toute mémoire de travail GPU, toute
|
||
double résidence
|
||
CPU/GPU et tout staging host-visible consomment la RAM hôte et doivent être
|
||
comptés une seule fois par le Resource Governor, conformément à la règle UMA
|
||
du [Resource Boundary](resource_boundary.md#selected-gpu) ; ils ne créent pas
|
||
un budget VRAM indépendant. L'admission, la durée de réservation et la
|
||
libération restent du ressort du Governor et de la Task admise. Les copies ou
|
||
synchronisations hôte↔GPU, y compris sur mémoire partagée, restent un coût de
|
||
transfert/synchronisation à mesurer : l'UMA ne les rend pas gratuites.
|
||
|
||
| Étape | Backend courant / couture GPU existante | Bibliothèque ou API requise si étude ultérieure | Compatibilité, sortie et coût | Recommandation de l'audit |
|
||
| --- | --- | --- | --- |
|
||
| `features.extract` (ORB/SIFT) | Extraction OpenCV CPU ; aucune couture GPU de production. | Aucune API d'extraction GPU n'est validée. Les indicateurs OpenCV Vulkan/OpenCL dynamiques ne fournissent pas à eux seuls un backend d'extraction ; CUDA est indisponible dans ce build. | Il faudrait prouver l'algorithme, les keypoints, descripteurs, ordre et Feature File produits. Les descripteurs et leurs buffers devraient être résidents ou stagés en RAM UMA ; le transfert et la synchronisation risquent de dominer les images bornées. Bénéfice non mesuré, complexité élevée. | Rester CPU. Une étude ne peut commencer qu'avec une API/backend concret et une preuve d'équivalence. |
|
||
| `visual_index.update` | Construction et publication CPU ; aucune couture GPU. | Aucun backend/API GPU validé. | Les postings, leur compaction, tri total et publication déterministe sont aujourd'hui CPU. Un backend devrait préserver exactement `table_id,key24,feature_set_id,feature_index`, le segment, SHA-256 et les memberships ; il ajouterait double résidence UMA et synchronisation pour un bénéfice non mesuré. Complexité élevée. | Rester CPU ; aucun chantier GPU n'est justifié par cet audit. |
|
||
| `candidate_pair.generate` | Requête Visual Index, top-K et publication CPU ; aucune couture GPU. | Aucun backend/API GPU validé. | Les scores, top-K, tie-breaks, normalisation et ordre de publication sont canoniques. Une accélération devrait rendre ces résultats identiques malgré les accès DB et les petits résultats bornés ; copies/synchronisations UMA et l'accès persistant réduisent le bénéfice attendu. Bénéfice non mesuré, complexité élevée. | Rester CPU ; ne pas introduire un backend GPU ou une seconde politique de sélection. |
|
||
| `matcher.run` | BFMatcher CPU jusqu'à 12 participants ; backend Vulkan ORB exact en conversion begin/finish backend-owned. | Vulkan compute existant pour ORB/Hamming top-2 uniquement. Aucun chemin Vulkan/OpenCL/CUDA validé pour SIFT/RootSIFT. | ORB Vulkan prouve la parité top-2 et Match File complète avec le CPU. La 780M UMA impose de compter buffers et staging une fois en RAM hôte ; SIFT/RootSIFT restent CPU. | Workload GPU primaire validé et supérieur : AUTO GPU-first, CPU fallback. Ne pas étendre à SIFT/RootSIFT ni changer l'identité persistante. |
|
||
|
||
L'attente « sortie identique » est une exigence de preuve, non une présomption
|
||
attachée au GPU. Un futur backend ne peut partager une identité, un fingerprint
|
||
ou une version scientifique existants que si l'équivalence complète des sorties
|
||
canoniques est démontrée à la frontière concernée. Si ses sorties diffèrent, il
|
||
doit recevoir un backend/version et une identité scientifique versionnés, avec
|
||
une décision et une validation dédiées ; il ne doit jamais être choisi comme un
|
||
fallback transparent. Le mode ORB Vulkan sériel existant est l'exception déjà
|
||
prouvée à cette règle d'identité commune.
|
||
|
||
### Décision GPU finale — Radeon 780M
|
||
|
||
La validation de production de `matcher.run` distingue l'identité scientifique
|
||
de son mode opérationnel. La production normale ORB crée une enveloppe AUTO
|
||
CPU12/GPU0 et ORB Vulkan CPU1/GPU1; les API explicites conservent une seule
|
||
forme depth 1. Sa signature durable `MIXED` reste CPU12/GPU0 et décrit cette
|
||
politique, tandis que l'override CPU persiste la classe `CPU`. Chaque slot vaut
|
||
640 Kio: buffers A (256 Kio), B (256 Kio) et top-2 (128 Kio). AUTO normal
|
||
réserve 640 Kio à inflight 1. La capacité privée de sûreté/benchmark peut
|
||
réserver 1,25 Mio à depth 2. Le backend ne mappe aucun slot avant initialisation,
|
||
retient exactement la capacité admise pendant la séquence et libère le second
|
||
avant l'admission depth 1 suivante; `backend_info` rapporte cette rétention
|
||
réelle. Les 10 Mio de stage restent la mémoire CPU par paire.
|
||
Une panne de dispatch reprend entièrement le top-2 CPU avant
|
||
toute publication ; elle ne publie jamais une sortie GPU partielle et ne
|
||
change ni fingerprint, ni identité Match Result, ni SHA du Match File.
|
||
|
||
Sur l'hôte contrôlé (`AMD Radeon 780M Graphics (RADV PHOENIX)`, Vulkan 1.4.354,
|
||
Mesa 26.2.1), le backend a créé le device et exécuté les dispatchs réels. Les
|
||
mesures de kernel chaud, distinctes du coût Task/SQLite/checkpoint, sont :
|
||
|
||
| Paire ORB | CPU BFMatcher | Vulkan | Accélération Vulkan |
|
||
| --- | ---: | ---: | ---: |
|
||
| 768 × 768 | 0,67 ms | 0,34 ms | 1,97× |
|
||
| 4096 × 4096 | 17,00 ms | 1,62 ms | 10,49× |
|
||
| 8192 × 8192 | 67,54 ms | 3,91 ms | 17,27× |
|
||
|
||
La parité top-2, Match File et publication durable est couverte par une Task
|
||
Queue réelle à 769 × 769 descriptors, par une répétition déterministe et par
|
||
le fallback forcé. La forme explicite CPU12/GPU0, la signature AUTO `MIXED`
|
||
CPU12/GPU0 et la forme Vulkan CPU1/GPU1 utilisent des lots `1..12` et 10 Mio
|
||
par paire. La reprise accepte
|
||
en plus seulement les anciennes signatures entières CPU8/GPU0 et Vulkan
|
||
CPU1/GPU1 à lot maximal 8, puis les formes CPU12 antérieures au coût par paire.
|
||
Elles sont des signatures historiques de récupération, normalisées
|
||
éphémèrement avant admission ; une forme voisine est rejetée. La configuration
|
||
portable `-Dvulkan_orb=disabled` refuse Vulkan et conserve le fallback CPU.
|
||
|
||
Le gel v1 ne revendiquait pas de comparaison de débit corpus saine sous la
|
||
pression hôte alors observée. L'évidence directe apportée à la tranche v2
|
||
établit désormais ORB Vulkan comme backend déterministe, exact et mesurément
|
||
supérieur pour ce hot path. La politique canonique v2 est donc GPU-first pour
|
||
ORB Matcher lorsque son contrat GPU/UMA est admissible, avec fallback CPU.
|
||
Cette décision opérationnelle ne rouvre pas le gel scientifique v1 et ne
|
||
s'étend pas aux trois domaines GPU rejetés ci-dessous.
|
||
|
||
Les classifications finales sont :
|
||
|
||
| Domaine | Classification | Évidence déterminante |
|
||
| --- | --- | --- |
|
||
| `candidate_pair.generate` | `CANDIDATE_GPU=REJECTED_WITH_MEASURED_REASON` | Le coût est Visual Index, filtrage, branchement et publication SQLite ordonnée; aucune primitive GPU existante ne préserve ces identités et le CPU parallèle Candidate a déjà démontré 7,114× à t12. |
|
||
| `features.extract` | `FEATURE_GPU=REJECTED_WITH_MEASURED_OR_IMPLEMENTATION_EVIDENCE` | OpenCV 5.0.0 installé n'expose aucun ORB/SIFT Vulkan/OpenCL utilisable, CUDA est absent, et aucun seam existant ne peut prouver les Feature Files byte-identiques. |
|
||
| `visual_index.update` | `VISUAL_INDEX_GPU=REJECTED_WITH_MEASURED_REASON` | Postings, tri total, SHA et publication déterministe sont CPU; aucun kernel GPU borné existant ne couvre cette frontière. |
|
||
| `matcher.run` ORB | `MATCHER_GPU=EXISTING_BACKEND_VALIDATED_AND_PREFERRED` | Le backend est exact, déterministe et mesurément supérieur pour le hot path validé. AUTO le préfère quand GPU/UMA sont admis et conserve le CPU en fallback. |
|
||
|
||
Le comportement de production normal est `AUTO`, choisi par le Governor à
|
||
chaque admission de séquence. Les modes CPU/Vulkan explicites restent des
|
||
overrides de debug, benchmark et reproductibilité. L'enveloppe privée
|
||
CPU/Vulkan, le fallback CPU complet et les diagnostics bornés sont
|
||
**PASS / FROZEN** dans les limites validées.
|
||
|
||
## Extraction OpenCV
|
||
|
||
Le processus conserve une limite OpenCV globale. Sous l'unique callback Queue,
|
||
RAW, Photo Quality et la campagne appliquent/restaurent leur CPU admis (CPU1
|
||
dans les estimations courantes). ORB, SIFT et RootSIFT Extraction déclarent le
|
||
maximum `int` positif accepté par OpenCV, puis appliquent/restaurent exactement
|
||
le `cpu_threads` immutable borné au compute-pool. Matcher applique OpenCV1 à l'intérieur de sa
|
||
propre fenêtre et utilise les participants Task admis autour de cette
|
||
primitive. La réservation vit pendant l'exécution bornée et aucun second pool
|
||
runtime n'est créé.
|
||
|
||
Le nombre admis est une politique de ressources, pas une identité scientifique.
|
||
Les tests OpenCV 5.0.0 à 1/2/4/8/12 threads obtiennent les mêmes keypoints,
|
||
descripteurs et métriques ORB, SIFT et RootSIFT. Aucun fingerprint, format ou
|
||
schéma n'est modifié ; 12 est une preuve de l'hôte courant, pas un maximum
|
||
portable. Les utilisateurs imbriqués d'OpenCV ne peuvent pas faire
|
||
varier cette configuration process-wide pendant une extraction. Le callback
|
||
assure donc qu'une admission CPU est réellement consommée, sans confondre cette
|
||
dimension avec le lot admis.
|
||
|
||
Le Governor slow-start les dimensions CPU réductibles par doubles depuis 1,
|
||
puis le maximum exact de la capacité, toujours borné au compute-pool. Chaque
|
||
palier utilise deux observations et exige au moins 5 % de débit durable en plus.
|
||
Un seul essai CPU ou lot est actif à la fois ; après plafonnement ou refus CPU,
|
||
un kind dont le lot est adaptable peut seulement alors explorer le lot. Les
|
||
callbacks atomiques ORB/SIFT/RootSIFT publient un item seulement après extraction
|
||
et publication durable propre ; READY, `ALREADY_PRESENT` ou
|
||
`PUBLISHED_NOT_DURABLE` publie une observation zéro qui ne fait progresser aucun
|
||
essai. Visual Index suit la même règle par
|
||
segment, tandis que Candidate et Matcher observent chaque séquence. Les formes
|
||
fixes restent fixes mais conservent un diagnostic d'admission Governor-owned.
|
||
|
||
Pour Candidate Generation, l'estimation demande jusqu'à soixante-quatre threads
|
||
CPU et un slot I/O. Le callback de Queue compte comme un de ces threads ; il crée donc
|
||
au plus `cpu_threads - 1` threads enfants. Tous les enfants sont joints avant
|
||
la fin de la séquence, la libération de réservation ou
|
||
`lardon3d_task_sequence_break()`.
|
||
|
||
## Développement RAW d'une exécution sélectionnée
|
||
|
||
`raw.develop.batch/1` conserve la politique RAW v1, les octets PNG,
|
||
L3DRAWD1, le fingerprint, le content-addressing et toutes les identités du
|
||
développeur par Capture. Une séquence prend au plus huit items contigus depuis
|
||
le curseur durable de l'exécution sélectionnée et utilise au plus le nombre de
|
||
CPUs admis. Chaque participant possède son LibRaw et ses buffers privés ; les
|
||
896 Mio chargés par item admis couvrent le workspace LibRaw borné à 40 MP, les
|
||
copies RGB/BGR, les buffers PNG/validation, la pile enfant bornée de 1 Mio et la
|
||
marge allocateur/codec. Le petit contexte propriétaire est inclus dans cette
|
||
charge : un budget hôte post-réserve de 7 Gio admet donc exactement les huit
|
||
participants sûrs, sans débit fixe redondant. Cette estimation est une borne
|
||
opérationnelle de mémoire par participant, pas une limite scientifique de
|
||
dataset.
|
||
OpenCV est fixé à un thread interne afin de ne pas multiplier le fan-out.
|
||
|
||
Les enfants ne modifient ni curseur sélectionné ni progression Task. Après leur
|
||
jointure complète, le callback Queue propriétaire publie seul les mappings
|
||
`item_index -> image_id` dans l'ordre croissant. Chaque transaction de mapping
|
||
avance le curseur contigu, puis seulement le checkpoint Task peut avancer. Une
|
||
erreur, une annulation ou un échec de création d'enfant joint tout le travail
|
||
lancé ; des assets immuables content-addressés peuvent déjà exister, mais le
|
||
curseur ne les devine pas et la reprise converge par le développeur existant.
|
||
Une `sequence_break` sépare toutes les fenêtres, donc la sélection entière ne
|
||
retient jamais une réservation longue.
|
||
|
||
## Extraction Feature d'une exécution sélectionnée
|
||
|
||
`features.extract.batch/1` conserve exactement ORB v1, ses paramètres, son
|
||
fingerprint, ses keypoints/descripteurs U8×32 et le Feature File existant. Une
|
||
fenêtre contient au plus douze images indépendantes de l'ordre sélectionné. Le
|
||
propriétaire configure OpenCV à un thread pour éviter tout fan-out imbriqué ;
|
||
chaque participant possède son décodage et sa sortie bornés, sans accès SQLite,
|
||
puis tous sont joints.
|
||
|
||
Le propriétaire seul publie dans l'ordre et avance le curseur v25 après preuve
|
||
du Feature Set READY exact. CPU et lot partent du minimum. Comme un CPU
|
||
supplémentaire ne peut être exercé que par une image indépendante de la même
|
||
fenêtre, le Governor essaie et accepte ces deux limites ensemble (`1/1`, `2/2`,
|
||
`4/4`, puis le maximum sûr admis). Les observations de fenêtres durables
|
||
permettent de retenir le dernier palier apportant au moins 5 %. Le plafond douze
|
||
est une capacité opérationnelle de benchmark,
|
||
jamais une limite scientifique, et aucune voie GPU n'est introduite.
|
||
|
||
## Calcul et publication Visual Index
|
||
|
||
Une séquence sélectionne le même préfixe durable d'au plus seize Feature Sets
|
||
qu'en mode sériel. Jusqu'à seize participants effectivement admis lisent les
|
||
Feature Files immuables ; chaque reader, son descripteur de fichier et sa
|
||
tranche de 256 descripteurs appartiennent à un seul participant. Aucun enfant
|
||
n'utilise le handle Project DB partagé.
|
||
|
||
Chaque Feature Set écrit dans une tranche disjointe du buffer de postings borné
|
||
du segment. Après jointure, le callback compacte seul ces tranches dans l'ordre
|
||
de sélection, puis le tri total existant impose
|
||
`table_id,key24,feature_set_id,feature_index`. Le callback est l'unique
|
||
propriétaire de la sérialisation, du hash, de la publication asset et de la
|
||
transaction segment + memberships. Le nombre de participants ne change donc
|
||
ni octet, SHA-256, chemin, membership, génération, fingerprint, ni résultat de
|
||
requête.
|
||
|
||
Une erreur de lecture interdit toute publication. Si la création d'un enfant
|
||
échoue, le callback calcule après les jointures uniquement les tranches restées
|
||
sans producteur. Le curseur n'avance qu'après commit du segment ; le checkpoint
|
||
Task suit ce commit, et tous les enfants sont joints avant `sequence_break`.
|
||
Le buffer de postings n'est pas multiplié : ses tranches privées partitionnent
|
||
la capacité fixe existante. Les coûts supplémentaires sont au plus une pile et
|
||
un reader/FD Feature File par participant admis.
|
||
|
||
## Calcul et publication Candidate
|
||
|
||
**CANDIDATE_PARALLELISM_IMPLEMENTATION=PASS.** Le parallélisme interne
|
||
Candidate est validé lorsque le Governor admet à la fois plusieurs CPUs et un
|
||
lot contenant plusieurs memberships indépendants. Les benchmarks CPU 1/6/12
|
||
antérieurs restent valides pour leurs intervalles mesurés ; une preuve de débit
|
||
durable sur machine réelle exige en plus que chaque frontière observée admette
|
||
un lot supérieur à un.
|
||
|
||
L'admission CPU et l'admission de lot sont deux contrats opérationnels
|
||
différents. `desired_cpu_threads` borne le fan-out disponible, tandis que
|
||
`batch_size` borne strictement le nombre de memberships que la Task peut
|
||
prendre dans la séquence courante. Ainsi, `cpu_threads=12` avec
|
||
`batch_size=1` est architecturalement valide mais ne contient qu'une source
|
||
indépendante : Candidate calcule un seul participant utile. Il ne doit jamais
|
||
emprunter du travail à une séquence ultérieure pour remplir les CPUs admis.
|
||
|
||
Le test Candidate couvre désormais six Feature Sets et trois fenêtres
|
||
productives saines : l'admission initiale, puis deux réadmissions productives
|
||
après `sequence_break`, admettent chacune deux sources et obtiennent deux
|
||
participants utiles. Une troisième réadmission après `sequence_break` observe
|
||
le suffixe vide requis pour terminer. La sortie reste canonique, le curseur
|
||
durable est contigu et aucune paire n'est dupliquée. Le même test vérifie le
|
||
complément sûr : un lot admis d'un seul item, même avec douze CPUs disponibles,
|
||
produit exactement un participant par frontière productive. Cette réduction
|
||
est `EXPECTED_RESOURCE_SAFETY_BEHAVIOR`, pas une perte de parallélisme
|
||
Candidate.
|
||
|
||
**REAL_HOST_CURRENT_STATE=GOVERNOR_PRESSURE_THROTTLING_ACTIVE.** Le 29 août
|
||
2026, un run S21 réel a conservé son estimate Candidate CPU-12 et son format
|
||
de checkpoint courant (`256 KiB + 64 KiB/item`, lot `1..64`) après 237
|
||
séquences, mais le Governor a admis des lots d'un sous pression globale. Les
|
||
faits retenus sont : zram `6059936 / 6291452 KiB` (~96,3 %), swapfile ~1,92
|
||
GiB, tandis que Candidate restait ~55 MiB RSS, `VmSwap=0` et
|
||
`MemAvailable` ~7,6 GiB. La disparition ultérieure du swap `/dev/sdb1` vers
|
||
14:00 a modifié la configuration de swap de l'hôte, mais elle est postérieure
|
||
à l'effondrement initial du lot et n'en est pas la cause initiale prouvée.
|
||
Cette décision reflète
|
||
des signaux de swap/PSI globaux, non la réservation Candidate elle-même. Les
|
||
seuils Gate G ne sont pas modifiés pour améliorer un benchmark ; une preuve
|
||
réelle soutenue doit attendre un état hôte qui admet répétitivement
|
||
`batch_size > 1`.
|
||
|
||
Les sources sont les memberships durables du Visual Index, pagés en ordre
|
||
croissant de `feature_set_id`. Les IDs peuvent être clairsemés. Une fenêtre
|
||
contient au plus `2 * cpu_threads` sources et jamais plus de 64 ni du lot admis.
|
||
Chaque résultat
|
||
conserve au plus le top-K existant de 256 propositions ; l'estimation de Task
|
||
reste conservatrice à 256 Kio fixes et 8 Mio par item admis. Ce coût couvre la
|
||
pile enfant bornée de 4 Mio, le reader/Visual Index et le handle SQLite privé de
|
||
chaque participant.
|
||
|
||
Chaque participant possède son propre handle `project.db` pour les lectures et
|
||
le ferme avant de terminer. Le calcul parallèle charge le Feature Set source,
|
||
exécute exactement la requête Visual Index existante et forme les paires
|
||
canoniques en mémoire. Il ne publie rien.
|
||
|
||
La reprise normalise uniquement les estimations Candidate historiques
|
||
complètes reconnues — CPU1/128 Kio fixes et CPU12/256 Kio fixes, toutes deux à
|
||
64 Kio par item — vers la demande courante CPU64/256 Kio fixes/8 Mio par item.
|
||
Cette
|
||
estimation effective reste privée et n'est pas checkpointée avant admission ; le Governor peut
|
||
toujours la réduire à son budget disponible. Le Task ID, le curseur typé, le
|
||
lot 1–64 et les paramètres scientifiques restent inchangés. Les autres kinds
|
||
ne passent pas par cette normalisation Candidate. Matcher possède sa propre
|
||
normalisation bornée des deux formes CPU12 historiques, décrite ci-dessous.
|
||
|
||
Après la jointure complète, le thread propriétaire de la Task publie seul, dans
|
||
l'ordre croissant des sources puis dans l'ordre des résultats de requête. Il
|
||
emploie le comportement existant `find_candidate_pair` puis
|
||
`create_candidate_pair`. Les scores, top-K, tie-breaks, normalisation de paire,
|
||
fingerprints et identités scientifiques ne changent pas. Le nombre de threads
|
||
ne peut donc modifier ni les identités persistées, ni leur ordre de publication,
|
||
ni leur cardinalité.
|
||
|
||
## Préparation et publication Geometric Verifier
|
||
|
||
La policy scientifique GV v3, les paramètres, le fingerprint, la seed, le
|
||
masque, la matrice et `cv::UsacParams::isParallel=false` restent FROZEN. Le
|
||
parallélisme courant est externe à l'estimator : une séquence sélectionne un
|
||
préfixe d'au plus seize Match Results indépendants et jusqu'à huit participants
|
||
utiles les préparent en parallèle. La fenêtre/participant a été validée sûre
|
||
jusqu'à seize, mais la demande production s'arrête à huit selon la preuve de
|
||
débit durable ci-dessous.
|
||
|
||
Chaque préparation possède ses handles de fichiers Match/Feature et son objet
|
||
opaque borné ; le handle Project DB partagé reste sérialisé par son mutex
|
||
interne. Les enfants ne publient jamais. Après toutes les jointures, le callback
|
||
Queue propriétaire publie seul dans l'ordre des parents, avance uniquement le
|
||
préfixe contigu et checkpoint avant toute libération de réservation à
|
||
`sequence_break`. Un échec de création partielle stoppe les nouvelles prises de
|
||
travail, joint tous les enfants créés et détruit exactement une fois chaque
|
||
préparation déjà produite.
|
||
|
||
La preuve représentative réelle porte sur 4 113 parents, dont 4 102
|
||
applicables : 578 vérifiés et 3 524 rejetés. Les IDs canoniques, toutes les
|
||
colonnes GVR et le digest scientifique
|
||
`9401ef6168804b6f1d51f4cdf64cd6b33cbebd2934e5294c8feacc87f9c8ce86`
|
||
sont identiques à CPU1/2/4/8/12. Les débits complets, incluant préparation,
|
||
publication ordonnée, curseur et checkpoint, valent respectivement
|
||
60,7514/83,9556/104,7545/116,6329/119,7606 parents/s. CPU12 n'ajoute que
|
||
2,68 % sur CPU8, sous le deadband Governor de 5 % : CPU utile maximal 8,
|
||
fenêtre CPU sûre 16 et batch maximal 16.
|
||
|
||
La preuve S21 complète historique (172 741 parents traversés, 172 275
|
||
applicables, 24 065 vérifiés et 148 210 rejetés) reste valide avec son chemin
|
||
CPU1/batch8 original. Elle n'est pas présentée comme un rerun du nouveau chemin.
|
||
|
||
## Échec, reprise et progression
|
||
|
||
Une erreur de calcul à la source `S` interdit toute publication de `S` et de
|
||
toute source suivante déjà calculée dans la fenêtre. Les sources antérieures
|
||
entièrement publiées forment le seul préfixe susceptible d'avancer le curseur.
|
||
Une reprise revoit éventuellement un préfixe publié avant un crash mais non
|
||
checkpointé ; l'idempotence existante fait converger ce rejeu sans deviner
|
||
d'identité.
|
||
|
||
La progression est le rang du curseur parmi les memberships durables ordonnés,
|
||
divisé par leur nombre total. Elle n'utilise ni la valeur numérique du
|
||
`feature_set_id`, ni un compteur process-local, ni le nombre de séquences. Elle
|
||
est donc monotone pendant l'exécution et reconstruite à l'identique après
|
||
reprise pour un même état de memberships. Les valeurs incomplètes sont bornées
|
||
à 0–99 ; 100 n'est publié qu'après observation d'un suffixe vide et achèvement
|
||
de la Task.
|
||
|
||
## Calcul et publication Matcher
|
||
|
||
Les sources Matcher sont les Candidate Pairs durables, pagées en ordre
|
||
croissant de `candidate_pair_id`. Une fenêtre contient au plus
|
||
`2 * cpu_threads` paires et jamais plus de douze. Le callback Queue compte comme
|
||
un participant et crée donc au plus `cpu_threads - 1` enfants ; tous sont
|
||
joints avant publication, libération de réservation ou `sequence_break`.
|
||
|
||
Chaque participant CPU calcule un Match File temporaire privé. OpenCV reste à
|
||
un thread interne pendant cette séquence afin que `BFMatcher` ne crée pas un
|
||
second fan-out. Le Governor sélectionne avant admission soit la forme
|
||
CPU1..12/GPU0, soit ORB Vulkan CPU1/GPU1, 640 Kio UMA et inflight 1.
|
||
Les participants
|
||
CPU sont décrits par `cpu_threads`; `helpers` reste zéro. Le fallback CPU
|
||
complet est choisi avant la séquence si GPU/UMA n'est pas admis ; une panne de
|
||
backend ou une paire sous le seuil Vulkan pendant une séquence reprend aussi le
|
||
top-2 CPU exact dans la réservation immutable. Le diagnostic distingue backend
|
||
sélectionné et backend réel, y compris plusieurs paires complètes CPU/Vulkan.
|
||
Ce choix ne
|
||
modifie ni fingerprint, ni Lowe ratio, ni correspondances brutes, ni identité
|
||
Match Result.
|
||
|
||
La couture v2 emploie une enveloppe privée et le mode `AUTO` Governor-owned.
|
||
CPU/Vulkan explicites restent des overrides de
|
||
debug, benchmark et reproductibilité. Une fois une séquence admise, son
|
||
contrat est immutable jusqu'à libération ; seul le contrat de la séquence
|
||
suivante peut changer. Une Task ORB normale nouvelle persiste la classe
|
||
`MIXED` avec ses champs CPU12/GPU0 réels ; elle seule reconstruit AUTO. Les
|
||
signatures CPU ORB courantes ou historiques reconstruisent CPU fixe, et les
|
||
signatures Vulkan reconstruisent Vulkan fixe. Le build portable conserve
|
||
`MIXED` mais expose seulement CPU. Les signatures historiques exactes CPU8,
|
||
Vulkan à lot maximal 8 et CPU12 antérieures au coût par paire restent reconnues;
|
||
toute forme voisine est rejetée.
|
||
|
||
Le pipeline Vulkan rolling repose uniquement sur la couture privée interne.
|
||
Device/pipeline/layout/cache restent partagés; deux slots maximum dupliquent
|
||
command/fence/descriptors/buffers/query et portent chacun un handle
|
||
`slot+generation`. Toute fin, sortie invalide, annulation ou erreur nettoie le
|
||
handle exact ; un échec d'attente condamne la session. Une instrumentation
|
||
bornée prouve sans sommeil `SUBMIT(i) < SUBMIT(i+1) < FINISH(i) <
|
||
PUBLICATION_START(i) < PUBLICATION_FINISH(i)` à depth 2, ainsi que les chemins
|
||
begin/publish/cancel en échec et la réutilisation. Aucun helper GPU n'est créé.
|
||
Le payload mappé suit la capacité de séquence admise : un slot à depth 1, deux
|
||
seulement pendant depth 2, sans redimensionnement pending et avec libération du
|
||
second avant la prochaine admission. Une génération arrivée à `UINT64_MAX` ne
|
||
boucle pas; son slot est retiré définitivement avant toute nouvelle soumission.
|
||
|
||
Le runner `pre-sfm-real-execution` compile en plus un contrôle de benchmark
|
||
synchrone privé. Il force le contrat GPU depth 1 et exécute
|
||
`top2 synchrone → postprocess canonique → publication` avant la paire suivante.
|
||
Le défaut du runner reste rolling et le défaut Matcher normal reste AUTO ; CPU
|
||
ou Vulkan explicites demeurent des overrides. Le contrôle synchrone est absent
|
||
du binaire et du comportement de production, n'est pas persisté et ne peut pas
|
||
être repris depuis une Task pendante. Il sert uniquement à comparer l'overlap à
|
||
sortie identique. Le corpus court retenu démontre +10,27 % pour rolling depth 1
|
||
face à ce contrôle, avec digest identique. L'A/B forcé ABBA mesure ensuite
|
||
54,661652238 paires/s à depth 1 contre 55,797311953 à depth 2 (+2,077617 %),
|
||
sous le deadband 5 %, avec digest identique, quatre séquences de fallback local et zéro
|
||
panne/discard dans chaque exécution. Depth 2 est donc
|
||
**REJECTED_WITH_MEASURED_REASON** pour AUTO normal; il reste une capacité privée
|
||
de sûreté/benchmark; le lifecycle normal est **PASS / FROZEN**.
|
||
|
||
Le target runner/test compile aussi un contrôle A/B `--matcher-inflight 1|2`,
|
||
absent de la production. Pour AUTO rolling, il fixe min=max inflight et batch=2
|
||
par défaut. `--matcher-batch 2|4|8|12`, valable seulement avec inflight, fixe
|
||
aussi min=max batch dans la même enveloppe privée à capacité Vulkan unique;
|
||
l'unique
|
||
Governor conserve sélection, réservation UMA et contrat immutable. Un budget
|
||
GPU nul est refusé avant ouverture du Project, et une indisponibilité
|
||
GPU/backend/mémoire ne peut jamais sélectionner CPU à la place. Synchronous
|
||
accepte seulement 1.
|
||
Cette dimension opérationnelle n'est ni persistée ni scientifique, ne s'applique
|
||
pas à une Task pendante et est rapportée explicitement dans le JSON. Les
|
||
fixtures comparent rolling 1, rolling 2 et synchrone 1 sur des Projects neufs
|
||
avec sorties exactes. Une paire localement inéligible peut toujours produire sa
|
||
preuve CPU complète. Matcher l'attribue exactement une fois après publication
|
||
durable à un compteur d'items local-ineligible, backend-failure ou other; le
|
||
travail CPU sélectionné n'est pas un fallback. Les compteurs de séquences
|
||
et l'apprentissage de débit restent séparés : si la paire suivante échoue en
|
||
calcul/publication ou si la Task est annulée, le préfixe durable reste compté,
|
||
sans transformer la séquence avortée en observation valide. La cause locale
|
||
d'une paire n'est jamais écrasée par une panne begin/finish voisine.
|
||
Ils restent diagnostiques, mais seul le nombre d'items localement inéligibles doit
|
||
être identique entre cohortes de lots différents. Un item backend-failure/other,
|
||
un discard, un slot pending, un contrat différent ou toute admission CPU rend
|
||
`experiment_valid=false` et fait échouer la Task de benchmark après checkpoint
|
||
de l'éventuel fallback déjà durable. Les logs préliminaires batch 2/4 retenus
|
||
montrent quatre contre trois séquences locales pour les mêmes 4113 IDs/digest :
|
||
ils prouvent seulement que l'ancien comparateur dépendait du regroupement et ne
|
||
participent pas à la décision. La matrice item-valide
|
||
`forced-batch{2,4,8,12}-items{,-b}.stdout.jsonl` donne respectivement
|
||
54,180767704, 66,094373197, 74,784998723 et 76,755814095 paires/s, avec six
|
||
items locaux, zéro panne/autre et le même digest dans chaque run. Les gains de
|
||
palier sont +21,988624373 %, +13,148812987 % et +2,635308425 %. Le dernier est
|
||
sous le deadband 5 % : AUTO normal conserve `BATCH_MAX_USEFUL=8`, tandis que
|
||
batch 12 reste une capacité privée sûre
|
||
`REJECTED_WITH_MEASURED_REASON`.
|
||
Le contrôle de production sans override est conservé dans
|
||
`short-auto-batch8-governor-v2.stdout.jsonl` : la cadence durable complète fait
|
||
monter les contrats `1 → 2 → 4 → 8`, puis garde 8 jusqu'au résultat 4113/4113,
|
||
à 76,072 paires/s avec digest identique, inflight 1, helpers 0 et zéro panne ou
|
||
discard.
|
||
La preuve S21 finale sans override conserve exactement ces limites sur
|
||
172 741 paires : dernier contrat batch 8/inflight 1/helpers 0, 73,649 résultats
|
||
durables/s et zéro panne Vulkan. Un seul épisode YELLOW réduit batch 8 à 1;
|
||
après retour GREEN, la progression contrôlée 1 → 2 → 4 → 8 est rejouée.
|
||
Depth 2, helpers et batch 12 ne sont jamais admis en production. Le thread
|
||
Queue lourd reste sur `0-5,8-13`; `6,7,14,15` restent réservés au desktop.
|
||
|
||
La création et la reprise AUTO ne sondent plus le backend sur le caller/main.
|
||
Elles exposent la capacité depuis les seules métadonnées build/backend/GPU ; le
|
||
Governor possède le dimensionnement exact et l'admission UMA sur son snapshot. Le premier
|
||
`begin` initialise Vulkan seulement sur le worker Queue déjà contraint, après
|
||
établissement pré-pthread de la politique de cache Mesa. Aucun sweep auxiliaire
|
||
post-init, latch de Task ou retry de recontrainte par TID n'existe. Une paire
|
||
localement inéligible n'initialise pas le backend. Une panne d'initialisation
|
||
désactive les admissions GPU AUTO
|
||
suivantes, tandis qu'une paire sous le seuil reste une inéligibilité locale et
|
||
non une panne.
|
||
L'état request-bound distingue un handle soumis de ces deux causes : aucune
|
||
paire sans handle ne passe par `finish`. Une panne réelle invalide la
|
||
disponibilité Governor avant tout fallback ou sortie précoce ; une inéligibilité
|
||
locale conserve cette disponibilité. Les restaurations CPU/Vulkan/historiques
|
||
fixes ne peuvent pas écraser l'état établi par une restauration AUTO.
|
||
|
||
Après jointure, seul le callback propriétaire publie les stages, dans l'ordre
|
||
croissant de `candidate_pair_id`, par le chemin atomique Match Store existant.
|
||
Une erreur de calcul ou de publication interdit la paire fautive et tout son
|
||
suffixe déjà calculé. Le curseur avance exclusivement avec le préfixe contigu
|
||
durablement publié. Les temporaires non consommés sont supprimés sur succès,
|
||
échec et annulation ; les assets partagés ne sont jamais supprimés par ce
|
||
nettoyage. Le rejeu conserve l'idempotence Match Result existante.
|
||
|
||
## Hors périmètre
|
||
|
||
- parallélisme entre Tasks ou plusieurs callbacks Queue actifs ;
|
||
- pool de workers global ou persistant ;
|
||
- modification du schéma Project DB ;
|
||
- affinité, pinning ou IDs de CPU dans l'API ;
|
||
- modification de la sélection/scoring/fingerprint Candidate ;
|
||
- changement des décisions FROZEN du Resource Governor.
|