Freeze Compute Governor v2 and asynchronous Vulkan Matcher execution. Governor now owns production task admission and live resource adaptation, with GPU-first AUTO selection for validated backends, CPU12 host capacity, desktop CPU/RAM reserves, UMA accounting, pressure throttling, hysteresis, and recovery. Validate and freeze the rolling Vulkan ORB Matcher path, host topology policy, task capability envelopes, runtime telemetry, restart/durability contracts, portable CPU fallback, and scientific equivalence. COMPUTE_GOVERNOR_V2=PASS/FROZEN ORB_VULKAN_ASYNC_EXECUTION=PASS/FROZEN MATCHER_GPU=EXISTING_BACKEND_VALIDATED_AND_PREFERRED
37 KiB
Resource Governor Lardon3D
COMPUTE_GOVERNOR_V2 — PASS / FROZEN
COMPUTE_GOVERNOR_V2 — PASS / FROZEN.
ORB_VULKAN_ASYNC_EXECUTION — PASS / FROZEN. Gate G core reste
PASS / FROZEN. Cette évolution opérationnelle ne change ni le contrat
scientifique Matcher gelé, ni les octets Match File, ni l'ordre du curseur, ni
la durabilité. Sa cible est une boucle fermée à sequence_break() : le
Governor observe une télémétrie hôte/Task bornée, choisit un contrat pour la
séquence suivante, puis le maintient immutable pendant toute son exécution.
Cette couture privée est gelée ; seule une séquence ultérieure peut recevoir
un autre contrat. Les dimensions retenues, le corpus S21 complet, les suites
portable/Vulkan, les sanitizers et l'audit XHIGH ferment le statut v2.
Sur le profil validé Ryzen 7 8845HS, le Governor lit le masque d'affinité permis
et la topologie package/core Linux. Il réserve des groupes de coeurs physiques
complets, frères SMT inclus, en préférant déterministiquement les plus grands
IDs package/core. Avec le masque unrestricted 0–15 et une réserve logique de
quatre, il dérive le pool lourd 0-5,8-13 et réserve 6,7,14,15 au desktop
Arch/Sway, à l'audio et à l'interaction ordinaire. Si le caller est déjà limité
à au plus logical_total - reserve, son masque permis devient directement le
pool de calcul sans seconde soustraction. Sans affinité/topologie exploitable,
le budget portable logical_total - reserve subsiste sans exclusion arbitraire
de frères SMT et l'affinité est diagnostiquée inactive. Cette politique privée
est PASS / FROZEN sur le profil validé ; aucun ID CPU n'est persisté ni ne
devient une identité scientifique.
Seul le worker lourd de l'unique Queue applique et relit son propre masque
(pid=0) avant les callbacks. Le caller/main/TUI reste unrestricted et aucun
autre processus n'est touché. Un pidfd de thread ne stabilise pas l'identifiant
numérique consommé par sched_setaffinity(tid) après la sortie du thread : le
Governor n'énumère ni ne mute donc jamais un TID auxiliaire. À la place,
Lardon3D établit MESA_SHADER_CACHE_DISABLE=true avant toute création de pthread
applicatif et avant toute initialisation Vulkan. L'absence de variable prend ce
défaut sûr ; les valeurs explicites exactes true et 1 sont conservées. Une
valeur explicite fausse ou malformée n'est pas écrasée et fait échouer le
démarrage, car elle permettrait à Mesa de créer ses helpers de cache disque
observés capables d'élargir leur affinité. La variable est inoffensive pour les
drivers non-Mesa, opérationnelle seulement, non persistée et absente de toute
identité scientifique. Sur le profil 780M contrôlé, les helpers *:disk$0
disparaissent ; tous les threads runtime restants observés héritent et gardent
0-5,8-13. Les diagnostics privés exposent l'activité de cette politique, la
valeur sûre et sa raison, sans prétendre recontraindre des auxiliaires.
Cette garantie est aussi défensive dans le backend public : toute requête
non vide qui devrait initialiser Vulkan vérifie sans mutation la valeur exacte
true/1 avant le premier appel Mesa. Une valeur absente ou différente rend ce
contexte backend indisponible et retourne UNAVAILABLE sans sortie partielle.
La lecture metadata demeure non initialisante. Ainsi un consumer direct qui ne
passe ni par l'application ni par le runner ne contourne pas la réserve CPU.
Le nombre de CPUs du pool borne directement l'admission.
CPU Matcher reste validé dans 1..12, indépendamment du lot :
cpu_threads=12, batch_size=1 demeure un contrat valide.
La cible Compute Governor v2 conserve 3 GiB de MemAvailable et ne franchit
pas intentionnellement le plancher dur de 2 GiB sur cette classe d'hôte 16 GiB.
Les entrées de pression actives sont MemAvailable, les PSI CPU/mémoire/I/O et
les deltas swap-in/swap-out entre observations. L'occupation totale du swap est
un état historique, pas à elle seule une activité récente. Ces objectifs v2 ne
réécrivent pas rétroactivement les constantes Gate G core gelées ci-dessous.
Les observations saines autorisent une croissance lente. Pour une dimension
CPU réellement réductible, la rampe est exactement 1 → 2 → 4 → 8 → 12,
bornée par l'enveloppe et le compute-pool. Deux séquences établissent d'abord
une référence de débit durable puis ouvrent un essai borné au palier supérieur.
Les dimensions CPU/génériques conservent deux observations d'essai et un gain
d'au moins 5 % avant une nouvelle croissance. Le lot ORB Vulkan normal exige
désormais huit observations pures consécutives pour sa référence puis huit pour
chaque essai; sa décision compare les moyennes bornées au même deadband de 5 %.
Pour Matcher, cette observation est la cadence durable complète de la séquence :
à partir de la deuxième séquence elle commence juste avant la rupture/réadmission
et se termine seulement après calcul, publication owner-only et checkpoint
générique durable. Le temps interne du calcul reste une métrique séparée, mais
ne peut pas masquer le coût de réadmission que le choix du lot doit amortir.
CPU et lot partagent un unique essai : deux dimensions ne changent jamais dans
la même mesure. L'infrastructure privée sait aussi
borner un essai inflight, mais l'enveloppe ORB AUTO normale le fixe maintenant
à 1 après la mesure A/B décrite ci-dessous. Un échantillon rapide isolé ne
décide rien ; sans gain, le plafond revient au dernier palier accepté et s'y
arrête. PSI ou delta swap actif abandonne immédiatement l'essai ; toute
admission encore permise prend lot minimum, inflight minimum et CPU1
avant réservation. Un WAIT/REJECT Gate G reste inchangé. Après
l'hystérésis RED → YELLOW → GREEN, une nouvelle référence
permet de reprendre des essais contrôlés. Les diagnostics distinguent les
essais/gains/refus CPU, inflight et lot, backend-fallback-hold et
pressure-decrease. En production normale, ORB est inflight 1 et helpers 0.
La télémétrie hôte privée lit, avec capacités fixes et parse strict, les deltas
/proc/stat limités au masque du compute-pool, MemAvailable, PSI mémoire et
I/O some/full, les deltas actifs pswpin/pswpout, RSS/HWM du processus et
le gpu_busy_percent de l'unique index DRM retenu par Hardware Profile, sans
scan ni fallback vers une autre carte. Utilisation CPU et GPU
sont exprimées en basis points avec un bit known; régression, overflow,
troncature, token malformé ou signal absent donnent unknown. La charge globale
n'est pas rebaptisée utilisation du pool et un Task qui utilise son pool admis
n'est pas réduit pour ce seul fait. RSS/HWM reste une observation du processus,
jamais une réservation mémoire Task. Aucun échec de cette capture optionnelle
ne fait échouer l'exécution scientifique.
Pour ORB Vulkan normal, seul le lot 1..8 reste essayable ; inflight est fixé à
1 et helpers à 0. Huit séquences pures saines construisent la référence, puis
huit séquences au palier d'essai sont nécessaires avant acceptation ou refus.
Fallback, travail durable nul et pression sont exclus et réinitialisent la
fenêtre pertinente. Un backend affamé ou un GPU peu occupé sous hôte sain peut
ouvrir cet essai ; gpu_busy inconnu ne l'interdit pas lorsque la starvation
backend est observable. Seul le gain moyen de débit durable accepte le palier.
Une occupation GPU plus haute sans gain le fait revenir au dernier contrat
accepté et arrête cette croissance. Un ou deux échantillons, hauts ou bas, ne
décident donc jamais le lot GPU.
La mesure forcée ABBA du même corpus et du même binaire donne 54,661652238
paires/s à depth 1 et 55,797311953 paires/s à depth 2, soit +2,077617 %, sous
le deadband matériel de 5 %. Ces débits combinés valent
(2 * 4113 * 1e9) / (wall_ns_a + wall_ns_b), et non la moyenne des deux débits
par run. Les wall_ns bruts sont 75326831673/75162582080 à depth 1 et
73662096698/73764360098 à depth 2; les walls moyens sont donc
75,244706877/73,713228398 s. Les autres moyennes depth 1/depth 2 sont : fence
6,0684/3,6776 s, starvation 54,4534/50,1465 s,
publication 29,2582/30,0548 s, submit CPU 0,2655/0,3818 s, readback
0,0460/0,0873 s et GPU busy max 23/24 %. Les quatre exécutions ont le digest
7a9dbc38a23a600379167d55e24836b7acbb22eea25573e7440bdc9e4602b3b3, quatre
séquences de fallback local par exécution et zéro panne/discard. Conclusion opérationnelle :
DEPTH_MAX_VALIDATED_SAFETY=2, mais DEPTH_MAX_USEFUL=1; depth 2 est
REJECTED_WITH_MEASURED_REASON pour AUTO normal.
Les agrégats retenus sont
/home/fy59/Documents/Lardon/.real-pre-sfm-2026-08-30/governor-v2-evidence/
forced-depth1-a.stdout.jsonl, forced-depth1-b.stdout.jsonl,
forced-depth2-a.stdout.jsonl et forced-depth2-b.stdout.jsonl.
La Radeon 780M possède un slot GPU et utilise une mémoire UMA : buffers Vulkan,
staging, descripteurs/commandes et readback en vol sont débités exactement une
fois de la RAM hôte. Hardware Profile ne conclut plus « VRAM séparée » au seul
motif qu'amdgpu publie mem_info_vram_total. Il conserve la capacité de payload
rapportée, mais classe conservativement shared/UMA lorsqu'un petit aperture
VRAM volé/dédié est accompagné d'un GTT à l'échelle de la RAM système, ou
lorsque cette petite capacité reste incertaine. Sur l'hôte validé, les preuves
exactes sont 512 Mio de VRAM visible et 7 986 020 352 octets de GTT pour environ
16 Gio de RAM. Une classification UMA conservatrice d'un GPU à faible VRAM peut
refuser inutilement une admission ; classer à tort cet iGPU comme mémoire libre
séparée pourrait contourner les cibles 3 Gio/2 Gio et est interdit. La capacité
rapportée ne forme donc aucun second budget VRAM indépendant sur UMA.
La politique canonique v2 est GPU-first lorsqu'un backend est validé,
déterministe et mesurément plus rapide, sous réserve que son contrat GPU/UMA
soit admis. ORB Matcher est aujourd'hui le workload primaire qui satisfait ces
conditions : la frontière top-2 et la sortie complète sont exactes, et
l'évidence contrôlée le classe supérieure au CPU pour ce hot path. CPU demeure
le fallback portable et de panne. Les choix CPU/Vulkan explicites restent des
overrides de debug, benchmark et reproductibilité. La production normale cible
AUTO, choisi par le Governor et observable par séquence. Le code courant
implémente ce comportement pour le Matcher ORB normal : GPU validé d'abord,
puis capacité CPU complète si le build, le backend, le GPU ou l'admission UMA
ne sont pas sûrs. La création/reprise AUTO n'initialise ni ne sonde Vulkan sur
le caller : le premier begin initialise le driver sur le worker Queue déjà
contraint. Un échec produit une paire CPU complète sans preuve partielle et
désactive l'admission GPU ultérieure lorsqu'il s'agit réellement du backend ;
une paire sous le seuil Vulkan reste une simple inéligibilité, pas une panne.
Le Matcher représente séparément SUBMITTED, inéligibilité locale et panne :
il n'appelle finish qu'avec le handle soumis correspondant. Une panne réelle
ne reclasse que les requêtes effectivement soumises ou non encore tentées; la
cause locale déjà établie pour une paire voisine reste locale. Elle met la
disponibilité partagée à faux immédiatement, même si le fallback CPU,
l'annulation ou la publication échoue ensuite. Seule une reprise AUTO peut
réétablir l'éligibilité depuis les faits runtime ; les reprises fixes et
historiques ne l'écrasent pas.
Une faute locale avant soumission (lecture Feature, allocation, chemin) ou
après finish Vulkan réussi (filtrage, allocation, staging/fsync Match File)
appartient au bucket other, jamais à backend-failure. La paire est
recalculée entièrement sur CPU; le backend partagé et les successeurs déjà
soumis restent valides. Le booléen privé backend_fault n'est vrai que si la
transaction Vulkan begin ou finish elle-même échoue.
Les API CPU/Vulkan explicites restent fixes. La télémétrie bornée conserve un
dernier diagnostic sérialisé et un état de rampe par kind/backend ; elle
distingue backend sélectionné/réel, contrat complet, pression, mesures hôte,
débit durable et compteurs Matcher/Vulkan. Une couture privée permet un pull
since(serial) et un format texte borné ; rien n'est imprimé directement dans
ncurses, persisté ou accumulé en grande histoire. Les callbacks atomiques
Feature/SIFT/RootSIFT enregistrent un item seulement après extraction et
publication durable propre. READY/ALREADY_PRESENT réutilisé ou publication non
durable enregistre zéro et n'avance aucun essai. Visual Index applique la même règle par segment ;
Candidate enregistre chaque séquence durable. Un fallback CPU complet d'une
séquence sélectionnée Vulkan annule l'essai et reconstruit ultérieurement une
référence pure ; il n'entraîne jamais la baseline Vulkan.
Le runner opt-in pre-sfm-real-execution consomme maintenant cette couture
d'évidence. Ses échantillons JSON sont explicitement qualifiés
latest-change-coalescing : le polling peut fusionner des séquences rapides et
ne prétend donc pas les énumérer toutes. En parallèle, le Governor maintient par
kind/backend des compteurs cumulatifs saturants et des extrema de télémétrie de
taille fixe. Le résumé final rapporte ainsi les admissions et séquences
enregistrées, items durables, changements de contrat et sommes
CPU/Vulkan/publication sans histoire non bornée ni double comptage. Ces agrégats
vivent seulement avec l'instance Governor ; ils ne sont ni un budget RSS, ni un
nouveau payload, ni une persistance.
Ils conservent les classifications par séquence et ajoutent les comptes exacts
par item pour les fallbacks Matcher : inéligibilité locale, panne backend ou
raison autre/inconnue. La Task incrémente l'unique compteur de l'item seulement
après publication durable de son fallback CPU complet; une admission CPU
normale n'est pas un fallback. Cet incrément immédiat est séparé du feedback de
fin de séquence : un préfixe déjà durable reste compté si une paire suivante
échoue ou est annulée, sans créer une observation de débit pour la séquence
avortée. Un high-water mark Task borné évite le double comptage in-process; il
n'est ni persisté ni reconstruit au redémarrage. Ces compteurs fixes saturent
avec le drapeau
commun de l'agrégat et, contrairement au nombre de séquences, restent invariants
quand le lot change. Le contrôle matriciel forcé du runner n'expose qu'une
capacité Vulkan aux batch/depth demandés : zéro admission CPU, payload exact,
aucun changement de contrat, panne, discard ou slot pending sont nécessaires à
experiment_valid=true. Les items localement inéligibles ne rendent une
comparaison valide que si leurs comptes sont égaux entre cohortes; tout item de
panne ou autre cause échoue fermement.
La matrice forcée item-valide retenue est
forced-batch{2,4,8,12}-items{,-b}.stdout.jsonl sous le répertoire d'évidence
ci-dessus. Chaque run publie 4113 paires, six items localement inéligibles, zéro
item backend-failure/other et le digest
7a9dbc38a23a600379167d55e24836b7acbb22eea25573e7440bdc9e4602b3b3. Selon
(2 * 4113 * 1e9) / (wall_ns_a + wall_ns_b), les walls bruts
76150272845/75674818393, 61319835797/63138565155,
55148130595/54847191200 et 53446248173/53724786321 ns donnent respectivement
54,180767704, 66,094373197, 74,784998723 et 76,755814095 paires/s. Les gains de
palier sont +21,988624373 %, +13,148812987 % et +2,635308425 %. Le dernier est
sous le deadband 5 % : BATCH_MAX_VALIDATED_SAFETY=12 reste disponible au
benchmark privé, BATCH_MAX_USEFUL=8 borne AUTO normal et batch 12 est
REJECTED_WITH_MEASURED_REASON. Les anciens
forced-batch2-current.stdout.jsonl et forced-batch4.stdout.jsonl, comparés
par nombre de séquences au lieu d'items, restent une preuve historique du
comparateur obsolète et ne participent pas à cette décision.
Le run de production sans override
short-auto-batch8-governor-v2.stdout.jsonl confirme ensuite la boucle réelle :
contrats 1 → 2 → 4 → 8, dernier lot 8, inflight 1, helpers 0, 4113 résultats
durables en 54,066973393 s soit 76,072 paires/s, six fallbacks locaux, zéro
panne/discard et le même digest L3DMRD1. Le compute-pool contient 12 CPU,
les quatre CPU 6,7,14,15 restent réservés, l'UMA est comptée, le minimum
MemAvailable vaut 12 421 971 968 octets et aucun swap-in/out n'est observé.
La preuve S21 finale sans override,
final-s21-auto.stdout.jsonl, ferme la boucle sur 172 741 paires : AUTO
sélectionne Vulkan pour les 21 630 admissions, publie 172 741 résultats en
2 345,444485079 s (73,649 paires/s), termine avec batch 8, inflight 1 et
helpers 0, et ne compte aucune panne, aucun discard ni slot pending. La seule
admission classée YELLOW ramène batch 8 à 1; plusieurs séquences GREEN
reconstruisent ensuite la référence et remontent 1 → 2 → 4 → 8, sans
rebond. Le minimum MemAvailable est 10 927 390 720 octets, PSI mémoire
maximal 0, swap-in/out maximal 0, GPU busy moyen/médian/maximal 26/27/36 % et
HWM processus maximal 250 658 816 octets. L'UMA admise reste 655 360 octets;
les masques exacts sont compute 0-5,8-13 et reserve 6,7,14,15. Cette preuve
est opérationnelle : le digest scientifique et la reprise sont documentés par
le contrat Matcher, pas redéfinis ici.
L'ensemble v2 est PASS / FROZEN. Le gel porte sur cette architecture et
ses bornes validées, sans rouvrir Gate G ni le Matcher scientifique.
Les décisions GPU négatives existantes restent inchangées : Candidate, Feature et Visual Index restent CPU; SIFT/RootSIFT Matcher restent BFMatcher L2 CPU. Il n'est introduit ni second scheduler, ni Queue, ni daemon, ni sous-système de ressources.
Audit des 14 kinds de production
Tous les kinds passent par l'unique Queue et l'unique Governor, y compris ceux
dont toutes les dimensions sont fixes. Dans le tableau, CPU 1..N décrit la
réduction possible par l'admission; lot 1..N décrit la dimension de lot
adaptable. La mémoire réservée vaut fixe + par_item * batch_size. Tous les
coûts GPU par item valent zéro; la forme ORB Vulkan normale réserve exactement
un slot inflight de 640 Kio, débité une fois de la RAM sur UMA. La capacité
privée de sûreté/benchmark peut retenir deux slots, soit 1,25 Mio, seulement
sous un contrat forcé depth 2. Device, pipeline, layouts et cache restent partagés; leurs
allocations driver opaques ne reçoivent pas un coût inventé.
| Kind v1 | Estimation courante | Dimensions réellement consommées / constat Phase 1 |
|---|---|---|
raw.develop |
MIXED; CPU 1; lot 1; hôte 2 Gio + contexte, 0/item; I/O 1; GPU 0 |
Fixe et atomique. Un garde Queue applique CPU1 au pool OpenCV process-wide puis restaure la valeur précédente. |
photo_quality.triage |
IMPORT; CPU 1; lot 1; hôte contexte retenu + 20 Mio, 0/item; I/O 1; GPU 0 |
Un groupe par séquence. Un garde Queue applique/restaure CPU1 ; lot et contexte sont honnêtes. |
acquisition_campaign.run |
IMPORT; CPU 1; lot 1; hôte contexte retenu + 256 Kio + 64 Kio/item; I/O 1; GPU 0 |
Sources, confirmations, requête et plan sont chargés avant admission. La reprise remplace seulement l'enveloppe opérationnelle historique sous-estimée par ce coût exact ; le snapshot durable reste inchangé. |
import.images |
IMPORT; CPU 1; lot 1..32; hôte 128 Kio + NAME_MAX+64/item; I/O 1; GPU 0 |
Le callback consomme exactement le lot admis et réadmet entre lots. |
features.extract |
CPU; CPU 1..12; lot 1; hôte 64 Mio + 512 Mio/item; I/O 1; GPU 0 | Une image. Le callback applique/restaure exactement le CPU admis dans OpenCV, y compris si la vérification échoue après mutation. Sortie égale à 1/2/4/8/12. |
features.extract.sift |
CPU; CPU 1..12; lot 1; hôte 64 Mio + 1 Gio/item; I/O 1; GPU 0 | Même enforcement/rollback adaptatif. La forme durable courante reste CPU12 et la forme historique CPU1 exacte est normalisée en mémoire. |
features.extract.rootsift |
CPU; CPU 1..12; lot 1; hôte 64 Mio + 1 Gio/item; I/O 1; GPU 0 | Même exécution adaptative que SIFT ; aucune couture GPU validée. |
visual_index.update |
CPU; CPU 1..12; lot 1..16; hôte 8 Mio + 2 Mio/item; I/O 1; GPU 0 | CPU et lot sont lus du contrat; au plus cpu_threads-1 enfants joints avant publication. |
candidate_pair.generate |
CPU; CPU 1..12; lot 1..64; hôte 256 Kio + 64 Kio/item; I/O 1; GPU 0 | CPU et lot sont consommés; fenêtre min(2*CPU, 24). La forme sérielle historique exacte est normalisée en mémoire. |
matcher.run |
CPU: CPU 1..12, lot 1..12, hôte 0 + 10 Mio/item, I/O 1, GPU 0. ORB Vulkan normal: CPU 1, lot 1..8, même hôte/item, I/O 1, GPU 1 + 640 Kio, inflight 1. | ORB AUTO adapte seulement le lot sur huit observations pures par palier; CPU complet en fallback. Le benchmark privé conserve batch 12 et depth 2 (1,25 Mio), pas la politique normale. Vulkan explicite et contrôle synchrone restent depth 1; SIFT/RootSIFT et CPU explicite restent fixes. Helpers=0. |
geometric_verifier.run |
CPU; CPU 1; lot 1..8; hôte 4 Mio, 0/item; I/O 1; GPU 0 | Le lot est consommé séquentiellement; USAC conserve isParallel=false. |
track_builder.run |
CPU; CPU 1; lot 1; fixe (4 Mio + arêtes * (48 + 2*160)) * facteur, facteur 2 jusqu'à 400k arêtes puis 8; 0/item; I/O 1; GPU 0 |
Rebuild atomique. La reprise valide le scope mais ne recalcule pas l'estimation avant admission. |
sparse_sfm.run |
CPU; CPU 1; lot 1; fixe ceil_Mio(128 Mio + 64 Kio/image + 2 Kio/track + 512 octets/observation); 0/item; I/O 1; GPU 0 |
Exécution atomique; BA à un thread. La forme est redérivée à la reprise sans réconciliation de l'estimation persistée. |
incremental_reconstruction.run |
CPU; CPU 1; lot 1; fixe ceil_Mio(256 Mio + 128 Kio*(caméras base + images extension) + 4 Kio*(landmarks base + tracks extension) + 1 Kio*(observations base + extension)); 0/item; I/O 1; GPU 0 |
Exécution atomique; la forme est redérivée à la reprise sans réconciliation de l'estimation persistée. |
Ces écarts sont des constats d'implémentation Compute Governor v2. Ils ne créent ni nouvelle limite scientifique, ni nouvelle identité, ni modification du schéma Project DB. Une enveloppe privée de capacités peut corriger la sélection/réconciliation sans modifier l'ABI C public du Task Kind Registry.
SIFT v1A
Une extraction SIFT demande jusqu'à douze threads CPU, un slot IO, aucun GPU,
pour une image. L'estimation structurelle conservatrice est environ 1,06 Gio
(décodage, pyramides, candidats et F32×128), lot 1, pic de record batch zéro.
Le callback applique exactement le contrat admis dans 1..12 au pool OpenCV
process-wide sous l'unique propriétaire Queue, puis restaure la valeur
précédente sur toutes les sorties. Les tests déterministes ORB, SIFT et
RootSIFT couvrent 1/2/4/8/12 et obtiennent les mêmes keypoints, descripteurs et
métriques. Cette dimension opérationnelle ne change ni fingerprint ni Feature
Set.
Responsabilité
Le Resource Governor est l'unique propriétaire des budgets (RAM, GPU, CPU, IO). Il arbitre les ressources disponibles et calcule les lots adaptatifs pour chaque tâche.
Le profil interactif par défaut conserve un quart de la RAM détectée et un
quart des threads logiques pour le système hôte. Sur 16 Gio/16 threads, cela
donne environ 3,8 Gio et 4 threads de headroom. Une nouvelle admission attend
également lorsque PSI CPU some avg10 atteint 20 %, ou PSI mémoire 1 %. Ces
signaux n'interrompent jamais le petit job déjà réservé. Ces valeurs décrivent
le profil Gate G core gelé; la cible opérationnelle v2 active est le couple
3 GiB/2 GiB et les signaux différentiels définis en tête de document.
La soft floor vaut un quart et la hard floor un huitième de la RAM détectée. La soft floor place le Governor au minimum en YELLOW ; la hard floor le place immédiatement en RED. Le premier delta swap entre deux snapshots produit YELLOW. Un second delta consécutif produit RED. Le premier snapshot ne constitue qu'une baseline et n'est jamais interprété comme une activité récente.
La récupération interdit RED → GREEN : trois observations saines produisent
RED vers YELLOW, puis trois autres YELLOW vers GREEN. Le plafond reste 1 pendant
ces phases. Une fois GREEN, chaque groupe de trois observations saines double
le plafond : 1, 2, 4, 8, puis les paliers supérieurs utiles aux autres kinds.
Contrat Gate G gelé
PASS / FROZEN. Les constantes existantes ci-dessus
restent inchangées. La RAM disponible conserve le modèle conservateur
min(MemAvailable, RAM physique) - réserve hôte - réservations actives, borné à
zéro. Le double comptage conservateur possible d'une allocation déjà visible
dans MemAvailable est accepté : un faux WAIT est préféré à un overcommit.
Les snapshots de production emploient CLOCK_MONOTONIC et sont valides jusqu'à
un âge exact de 1000 ms inclus. Un snapshot plus ancien ou daté dans le futur
produit WAIT, sans réservation ni mutation de l'état de politique du
Governor. La capture synchrone complète impossible reste une erreur
opérationnelle qui fait échouer la tâche avant callback. Une télémétrie PSI ou
vmstat optionnelle absente reste inconnue et ne crée aucune pression fictive.
Gate G core cible un processus Linux natif non contraint et n'est pas cgroup,
systemd MemoryMax ou RLIMIT-aware. Il gouverne un seul GPU : le périphérique
DRM de plus petit numéro retenu par Hardware Profile. La capacité et l'usage
doivent provenir de ce même périphérique. La mémoire UMA est débitée exactement
une fois du budget RAM. Le multi-GPU est différé.
Le Governor ne garantit aucune allocation et ne transforme ni swap, ni zram, ni stockage externe en RAM. Il ne modifie aucun paramètre scientifique. Aucun scratch, historique RSS long terme, redimensionnement de réservation depuis le RSS ou monitoring live n'appartient à Gate G core. L'observation courante bornée RSS/HWM de Compute Governor v2 reste strictement diagnostique.
API principale
Création et destruction
lardon3d_resource_governor_create()- Créer un gouverneurlardon3d_resource_governor_destroy()- Détruire un gouverneur
Configuration
lardon3d_resource_governor_set_policy()- Définir la politique
Décision
lardon3d_resource_governor_decide()- Décider de l'admission d'une tâche
Réservation
lardon3d_resource_governor_reserve()- Réserver des ressourceslardon3d_resource_governor_reserve_available()- Réserver les ressources disponibleslardon3d_resource_governor_release()- Libérer une réservationlardon3d_resource_governor_reservation_is_valid()- Vérifier la validité
Métriques
lardon3d_resource_governor_availability()- Obtenir la disponibilitélardon3d_resource_governor_record_batch()- Enregistrer les métriques d'un lotlardon3d_resource_governor_generation()- Obtenir la génération actuellelardon3d_resource_governor_wait_for_change()- Attendre un changementlardon3d_resource_governor_pressure()- Lire GREEN, YELLOW ou RED
Invariants
- Le scheduler ne décide jamais des ressources
- Le Resource Governor est l'unique propriétaire des budgets
- Les réservations sont obligatoires avant toute exécution
- Les réservations sont libérées exactement une fois
- Les estimations de ressources sont immuables
- L'historique des métriques est strictement borné (8 entrées par classe)
- Un contrat de séquence est immutable jusqu'à sa libération; seule la séquence suivante peut être adaptée
Cycle de vie
1. Capture d'un snapshot de ressources
2. Décision d'admission
3. Réservation opaque
4. Exécution de la tâche
5. Enregistrement des métriques
6. Libération de la réservation
Adaptation dynamique
Calcul de lots adaptatifs
- Basé sur la consommation mémoire historique
- Mise à jour à chaque exécution
- Conservative (sous-estimation plutôt que sur-estimation)
Historique borné
- 8 entrées par classe de tâche
- Buffer circulaire
- Mise à jour FIFO
Réserves
- Sous-estimation temporaire possible avec des estimations statiques
- L'adaptation de débit v2 reste bornée au dernier état par kind/backend ; elle ne persiste ni historique volumineux ni décision matérielle.
- L'import
import.imagesest admis avec 128 Kio fixes, un coût borné par item, un thread CPU, un slot I/O et des lots de 1 à 32. Il enregistre le nombre d'images logiques nouvellement enregistrées dans le ScanSet et la durée réelle du lot. Cela inclut une copie orpheline identique adoptée, même si aucun octet n'est recopié.peak_memory_bytes == 0signifie explicitement « mesure inconnue » : l'échantillon peut conserver taille/durée mais n'alimente jamais l'adaptation mémoire. - Pas de communication inter-classes de tâches
features.extractréserve un lot de 1, demande jusqu'à douze threads CPU et un slot I/O, avec 64 Mio fixes et 512 Mio par image. Cette estimation conservatrice couvre le chemin actuel sans prétendre mesurer les allocations internes d'OpenCV. l'admission choisit 1..min(12, compute_pool)et le callback applique ce nombre immutable au pool OpenCV.record_batchcouvre la validation source, le décodage, ORB, la publication et la finalisation DB ;peak_memory_bytes == 0signifie « mesure inconnue ».visual_index.updatedemande jusqu'à douze threads CPU, un slot I/O, 8 Mio fixes et 2 Mio par Feature Set, par lots de 1 à 16. Le GPU vaut zéro. Le callback compte comme participant et crée au pluscpu_threads - 1enfants, tous joints avant publication et rupture de séquence. Chaque participant possède au plus un reader/FD Feature File ; les tranches de postings privées partitionnent le buffer borné du segment.record_batchcompte uniquement les memberships commités et conserve la mémoire inconnue à zéro.candidate_pair.generatedemande jusqu'à douze threads CPU, un slot I/O, 256 Kio fixes et 64 Kio par Feature Set, par lots de 1 à 64. Le GPU vaut zéro. La reconstruction reconnaît uniquement l'ancienne estimation exacte (128 Kio fixes, 64 Kio par item, un thread CPU, un slot I/O, aucun GPU, lots 1 à 64, classe CPU) et la normalise éphémèrement vers l'estimation courante complète ; aucun checkpoint d'estimation seule n'est publié et une forme voisine n'est jamais réinterprétée comme legacy.record_batchcompte le nombre de paires générées par séquence et la durée réelle du lot ;peak_memory_bytes == 0signifie « mesure inconnue ». Chaque séquence interroge le Visual Index pour jusqu'à 64 memberships. Le calcul emploie des fenêtres internes d'au plus deux sources par thread admis et 24 sources au total ; le propriétaire de Task persiste ensuite seul et en ordre canonique. Cette estimation opérationnelle ne limite pas la taille scientifique du dataset.matcher.rundemande jusqu'à douze threads CPU, un slot IO et 10 Mio par Candidate Pair admise, correspondant au working set contrôlé inférieur à environ 10 Mio par paire au maximum SIFT/RootSIFT (8 Mio de descripteurs contigus, KNNk=2, sorties et fichier bornés), hors scratch interne OpenCV. Ses lots CPU restent bornés à 1..12 Candidate Pairs; ORB Vulkan AUTO normal est borné à 1..8. PASS / FROZEN — P4 / Governor v2 : une fenêtre contient au plus deux paires par thread CPU effectivement admis et douze paires au total. Le callback Queue est un participant, crée au pluscpu_threads - 1enfants et les joint avant publication et libération de la réservation. Chaque paire conserve ses buffers dans un stage privé jusqu'à sa publication ordonnée ou son nettoyage ; OpenCV reste à un thread interne pour éviter une sursouscription imbriquée. Le Governor réserve donc au plus 120 Mio contrôlés pour un lot de douze ; cette borne opérationnelle ne limite pas la cardinalité scientifique du dataset. Une Task ORB normale nouvelle persiste la classe honnêteMIXEDavec les champs opérationnels CPU12/GPU0, lot 1..12 et 10 Mio par paire : cette classe signifie que la politique AUTO peut exécuter une séquence CPU ou Vulkan, pas qu'elle réserve les deux simultanément et pas un tag de backend. L'override CPU explicite persiste la même forme de ressources en classeCPU. La signature durable ORB Vulkan courante demande CPU1/GPU1, lot 1..12 et 640 Kio; son maximum 12 reste une sûreté durable/benchmark, pas le plafond utile AUTO. L'enveloppe AUTO normale borne le lot à 8 et fige inflight 1; sur UMA ce payload est débité une seule fois du budget RAM. Le backend est créé sans payload mappé, initialise et conserve exactement 640 Kio pour depth 1. La couture privée de sûreté/benchmark peut porter 1,25 Mio pendant un contrat forcé depth 2. Il ne redimensionne jamais avec une requête pending; la fin de séquence libère le second slot avant la prochaine admission depth 1. Une croissance échouée restaure la capacité antérieure, etbackend_inforapporte le payload réellement retenu, non le maximum de l'enveloppe. Les anciennes formes CPU8/GPU0 et CPU1/GPU1 à lot maximal 8, puis les formes plus anciennes CPU12 à 10 Mio fixes, sont seulement des signatures exactes de reprise. Elles sont normalisées éphémèrement vers la forme courante correspondante; toute forme voisine est rejetée. La production normale ORB reconstruit désormais une enveloppe privée CPU/Vulkan et demande au Governor un choixAUTOavant chaque séquence. Une fois admis, ce choix ne varie jamais dans la séquence. La nouvelle signatureMIXEDreconstruit AUTO, y compris dans un build portable où son enveloppe n'expose que CPU. Pour la compatibilité et la sûreté des overrides, toutes les signatures ORB historiques/courantes de classeCPUreconstruisent un CPU fixe ; une signature Vulkan reste fixe Vulkan. Aucun champ de ressource ne sert de faux tag et aucun backend ou matériel n'entre dans l'identité scientifique ou le payload Project DB. La création et la reconstruction AUTO exposent une capacité Vulkan depuis les seules métadonnées build/backend/GPU, sans appeler le driver ni pré-dimensionner la RAM. Le Governor évalue ensuite l'enveloppe exacte contre son snapshot MemAvailable/PSI/swap et sa charge UMA. Le premier begin et donc toute initialisation se déroulent sur le worker Queue après application de son affinité. La politique de cache Mesa est déjà établie au démarrage, avant ce worker : aucun balayage post-init, latch de Task ou appel d'affinité par TID auxiliaire n'existe. Une paire localement inéligible n'initialise toujours pas le backend. Le diagnostic de séquence conserve séparément le backend sélectionné et le backend réel (CPU,ORB_VULKANou mix de paires complètes), avec une raison de fallback. Les participants Matcher CPU sont comptés uniquement danscpu_threads;helpersreste zéro. La soumission asynchrone est une couture privée desrc/orb_vulkan_backend_internal.h, absente de l'ABI public. Le backend conserve deux slots maximum et un handle exactslot+generationpar requête;finishne peut consommer que ce handle et les comptes soumis. Une génération arrivée àUINT64_MAXne boucle jamais : le slot est retiré avant toute nouvelle soumission, de sorte qu'un ancien handle ne peut redevenir courant. Toute sortie/capacité invalide libère son slot, et un échec d'attente de fence détruit/met en échec la session avant toute nouvelle soumission. Les temporaires et objets pending sont nettoyés sur erreur, exception C++, annulation et échec de publication. Une trace de test bornée prouve sans temporisation, à depth 2,SUBMIT(i) < SUBMIT(i+1) < FINISH(i) < PUBLICATION_START(i) < PUBLICATION_FINISH(i)pour deux paires 769×769, sans modifier leur sortie.track_builder.runréserve un worker CPU, aucun GPU et aucun fan-out GVR. L'estimation est4 MiB + raw_inlier_edges * (48 + 2*160)avec facteur 2 jusqu'à 400000 arêtes inclusivement et facteur 8 au-delà, après vérification d'overflow. Le facteur élevé protège la transition mémoire observée à grande échelle ; le Governor reste l'unique propriétaire de l'admission et de la pression.
Limites actuelles
- Worker unique (pas de pools multiples)
- Pas de priorités entre tâches
- Pas de persistance des métriques
- Inflight Vulkan normal est fixé à 1, helpers GPU reste 0 et le lot AUTO maximal utile est 8. Batch 12 et depth 2 demeurent des capacités privées de sûreté/benchmark, chacune rejetée comme politique normale faute du gain de débit durable de 5 %. La publication canonique reste owner-only et représente environ 29,5 s dans les cohortes contrôlées; avec depth 2 sous 5 %, aucune preuve ne justifie un helper supplémentaire face à cette frontière durable.
- Pas de communication avec d'autres gouverneurs
Les corrections dérivables G-D01 (UINT64_MAX est le dernier ID valide et la
création suivante échoue sans réservation ni charge comptable),
G-D02 (saturation des compteurs de streak) et G-D03 (identité DRM identique
entre capacité et usage) sont implémentées. Les sept décisions G-B01 à G-B07
sont gelées ; il ne reste aucune décision humaine Gate G.
Statut
GATE G — PASS / FROZEN.