lardon3d/docs/architecture/resource_governor.md

44 KiB
Raw Permalink Blame History

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.

PORTABLE_HOST_POLICY_MAINTENANCE — IMPLEMENTED / VALIDATED / REVIEWED. La maintenance ultérieure retire les plafonds matériels globaux issus de l'hôte de preuve, sans changer la science, les formats, les fingerprints ni la durabilité v2. Son lifecycle global reste suivi par GLOBAL_MAINTENANCE_AUDIT.

Le Governor lit le masque d'affinité permis et la topologie package/core Linux. La politique par défaut demande quatre CPU logiques de réserve lorsque c'est praticable et conserve au moins un CPU de calcul sur un petit hôte. Avec une topologie complète, elle réserve des groupes de coeurs physiques complets, frères SMT inclus, en choisissant le sous-ensemble déterministe dont le dépassement de la cible logique est minimal. Les CPU déjà exclus par un masque externe comptent dans la réserve hôte, donc la même capacité n'est jamais soustraite deux fois. Sans affinité/topologie exploitable, le budget de compte portable subsiste sans fabriquer de masque ni d'ID CPU.

Sur le profil de preuve Ryzen 7 8845HS unrestricted 015, la cible logique de quatre donne le pool lourd 0-5,8-13 et la réserve 6,7,14,15 pour Arch/Sway, l'audio et l'interaction ordinaire. Ces IDs sont une observation historique de cet hôte, pas une politique portable. 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 politique par défaut conserve environ 3 GiB de MemAvailable comme réserve dure d'admission sur un hôte capable. Entre 3 et 4 GiB, elle entre en prudence YELLOW et bloque la croissance, mais ne soustrait jamais 4 GiB à la capacité et ne transforme pas cette bande stable en RED. Un hôte de moins de 3 GiB dégrade la réserve en fraction déterministe afin de garder une capacité bornée. 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 double depuis 1 et inclut toujours le maximum exact de la capacité lorsqu'il n'est pas une puissance de deux. Elle est bornée par l'enveloppe et le compute-pool, sans plafond global 12. 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 la réserve dure de 3 Gio et la prudence 34 Gio, ce qui 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 16 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 Dimension consommée et raison
raw.develop MIXED; CPU 1; lot 1; hôte 2 Gio + contexte, 0/item; I/O 1; GPU 0 Un Capture atomique. Le garde applique/restaure CPU1 au pool OpenCV global. Les 2 Gio sont une allowance de travail opérationnelle, pas une limite de dataset.
raw.develop.batch MIXED; CPU 1..8; lot 1..8; hôte 0 fixe + 896 Mio/item (contexte propriétaire, workspace LibRaw 40 MP, copies RGB/BGR, PNG/validation, pile enfant 1 Mio et marge allocateur/codec); I/O 1; GPU 0 Une fenêtre de Captures indépendants. 896 Mio × 8 = 7 Gio, donc le budget post-réserve de l'hôte de référence peut admettre la fenêtre sûre complète. OpenCV reste à 1 thread interne, tous les enfants sont joints, puis le propriétaire publie le préfixe ordonné avant sequence_break. Le plafond est opérationnel, jamais une limite scientifique de sélection.
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 ; garde OpenCV CPU1. Aucun scaling utile déterministe n'est acquis.
acquisition_campaign.run JPEG: IMPORT ; RAW: MIXED. CPU 1; lot 1; hôte contexte retenu + requête transitoire exacte + 256 Kio + 64 Kio/item ; DEVELOP_RAW ajoute 2 Gio; I/O 1; GPU 0 Un groupe S3-E par séquence, sans Task imbriqué. La création et la reprise dérivent la même estimation ; la forme historique exacte est normalisée seulement en mémoire.
import.images IMPORT; CPU 1; lot 1..32; hôte 128 Kio + NAME_MAX+64/item; I/O 1; GPU 0 Copie/hash I/O-bound. Le callback consomme le lot admis et réadmet entre lots.
features.extract CPU; CPU 1..compute-pool; lot 1; hôte 64 Mio + 512 Mio/item; I/O 1; GPU 0 La demande durable emploie le maximum int positif de l'API OpenCV ; le Governor la borne à l'hôte. Le garde applique/restaure exactement le CPU admis.
features.extract.batch CPU; CPU 1..12; lot 1..12; hôte 64 Mio + 512 Mio/item; I/O 1; GPU 0 Images sélectionnées indépendantes, OpenCV CPU1 par participant, enfants joints et publication owner-only ordonnée. Le plafond 12 est une capacité opérationnelle de mesure ; le feedback ≥5 % établit le palier utile sans devenir une limite scientifique.
features.extract.sift CPU; CPU 1..compute-pool; lot 1; hôte 64 Mio + 1 Gio/item; I/O 1; GPU 0 Même contrat OpenCV. Les formes CPU12 et CPU1 historiques complètes sont acceptées et normalisées en mémoire.
features.extract.rootsift CPU; CPU 1..compute-pool; lot 1; hôte 64 Mio + 1 Gio/item; I/O 1; GPU 0 Même contrat que SIFT ; aucune couture GPU scientifiquement compatible n'est validée.
visual_index.update CPU; CPU 1..16; lot 1..16; hôte 8 Mio + 2 Mio/item; I/O 1; GPU 0 Le segment contient au plus 16 Feature Sets indépendants. Au plus cpu_threads-1 enfants sont joints avant publication owner-only.
candidate_pair.generate CPU; CPU 1..64; lot 1..64; hôte 256 Kio + 8 Mio/item; I/O 1; GPU 0 Le batch de 64 est la borne algorithmique et de ressources. CPU/lot sont couplés pour que chaque palier de feedback exerce ses participants; fenêtre min(2*CPU, 64, reste_du_lot), un handle DB privé par participant, piles enfants de 4 Mio facturées.
matcher.run CPU: CPU 1..12, lot sûr 1..12, hôte 0 + 10 Mio/item, I/O 1, GPU 0. ORB Vulkan AUTO: CPU 1, lot utile 1..8, même hôte/item, GPU 1 + 640 Kio, inflight 1. Le batch/participant 12 est une borne intrinsèque mesurée du Matcher. Batch 12 et depth 2 (1,25 Mio) restent sûrs pour preuves privées, mais insuffisamment utiles en AUTO. CPU complet est le fallback.
geometric_verifier.run CPU; CPU utile 1..8, fenêtre sûre 16; lot 1..16; hôte 0 + 8 Mio/item; I/O 1; GPU 0 Des parents indépendants sont préparés en parallèle, puis publiés/checkpointés en ordre par le propriétaire. L'USAC scientifique 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 DSU atomique et publication owner-only ; aucune partition scientifiquement validée n'est acquise.
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 FROZEN ; Gate D/E et BA restent à un thread.
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 Recalcul atomique FROZEN depuis les entrées immuables, sans état solveur persistant.

Ces bornes sont opérationnelles, externes-library, algorithmiques, mesurées ou scientifiques selon la dernière colonne. Elles ne créent aucune limite de cardinalité scientifique, identité ou migration Project DB. Les formes historiques CPU1/CPU8/CPU12 citées sont des signatures exactes de reprise, pas des plafonds portables de production.

SIFT v1A

Une extraction SIFT demande jusqu'au compute-pool hôte, un slot IO, aucun GPU, pour une image. Sa demande durable utilise INT_MAX, borne valide de l'API OpenCV, mais ce nombre n'est jamais une admission matérielle. 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 positif admis 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 ; 12 reste une preuve de l'hôte courant, pas un maximum portable. 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 quatre threads logiques lorsque praticable et au moins un thread de calcul sur un petit hôte. Sur 16 threads, cela donne 4 threads de headroom ; avec une topologie fiable, la sélection porte sur des coeurs physiques complets et peut dépasser minimalement la cible logique. 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é.

La réserve dure/admission RAM vaut environ 3 GiB sur un hôte capable. La bande 34 GiB place le Governor au minimum en YELLOW et remet la croissance à 1 sans soustraire 4 GiB de la capacité. La hard floor place immédiatement en RED. Le premier snapshot swap constitue seulement une baseline ; un delta actif produit YELLOW, puis RED s'il persiste. L'occupation totale du swap n'est pas un signal d'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. Le contrat d'admission 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.

Registre de stockage externe et leases scratch

EXTERNAL_STORAGE_OPERATIONAL_INTEGRATION — CURRENT / VALIDATED OPERATIONAL. Cette couture additive postérieure ne modifie pas Gate G core, Lardon3DResourceEstimate, les budgets RAM/GPU/CPU/I/O ni une identité scientifique. Elle permet au contrôleur physique SSD exact d'enregistrer sous le mutex Governor une Lardon3DResourceExternalStorage bornée : génération source, état ABSENT/DETECTED/AVAILABLE/IN_USE/DRAINING/SAFE/ERROR, permission de nouvelle allocation, total/libre scratch connus ou inconnus, swap connu ou inconnu, nombre de leases, identité stable et raison.

L'enregistrement est exclusif à un objet contrôleur emprunté. Les updates stale ne peuvent pas restaurer une disponibilité ; une observation malformée ou indéterminée devient ERROR conservateur et bloque les nouvelles allocations. Un changement matériel incrémente la génération Governor et réveille les waiters. L'état n'est ni persisté, ni relié à Project DB, ni interprété comme capacité RAM. Les détails physiques et les capacités F10 restent la propriété du contrôleur ; le panneau ressources lit l'usage enregistré auprès du Governor.

La conversion contrôleur→Governor est fail-closed par état. Toute paire ou autorité exige détection courante du Drive et des deux partitions, identité Drive+deux UUID exacte/non vide, tailles de partition connues et positives, ainsi que mount, activité, leases, drain et capacités cohérents. ABSENT interdit tout fait détecté/actif/monté/loué/capacitaire ; DETECTED incomplet reste seulement observable. Un ERROR sticky peut conserver le tuple original malgré la disparition, mais n'alloue rien ; il n'offre un drain qu'après preuve du même tuple complètement reconnecté. Aucun état amical ni bit pairing_valid ne remplace cette preuve physique.

Les wrappers lardon3d_resource_governor_acquire_scratch() et lardon3d_resource_governor_release_scratch() sont l'unique entrée de production pour les leases. Ils exigent le contrôleur exactement enregistré, son identité courante et l'autorité explicite d'allocation. DRAINING, ERROR, absence, remplacement, état stale ou non-enregistré refusent une acquisition ; une release exacte reste possible pendant le drain ou l'erreur. Le Governor ne tient jamais son mutex pendant un appel contrôleur, et le contrôleur ne rappelle jamais le Governor. La génération et l'état sont revalidés avant publication afin que la transition ne crée ni inversion de verrou ni lease non comptée.

UINT64_MAX est une saturation source valide, pas un sentinel. L'API publique continue de refuser une update matériellement différente au même watermark et ne peut donc pas réaccorder une autorité stale. Seule la complétion du wrapper exact, déjà sérialisé pour l'objet contrôleur et l'adresse de lease enregistrés, peut réconcilier à UINT64_MAX sa propre acquisition/libération. Le compte interne fondé sur les adresses reste l'autorité ; une erreur de complétion peut retirer l'autorité mais ne peut jamais recopier un ancien compte physique.

Le teardown production suit strictement : Queue détruite/jointe et chaque lease Task rendu, worker SSD joint puis unregister vérifié, contrôleur détruit, Governor détruit. Un unregister est refusé tant qu'une opération wrapper ou un lease exact subsiste. Les seize Task kinds courants n'ont aucun consommateur scratch : le compte normal est donc réellement zéro et la capacité disponible n'autorise aucun usage implicite. Une future Task consommatrice devra définir son propre contrat d'éligibilité et son ownership sans transformer scratch ou swap en RAM.

API principale

Création et destruction

  • lardon3d_resource_governor_create() - Créer un gouverneur
  • lardon3d_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 ressources
  • lardon3d_resource_governor_reserve_available() - Réserver les ressources disponibles
  • lardon3d_resource_governor_release() - Libérer une réservation
  • lardon3d_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 lot
  • lardon3d_resource_governor_generation() - Obtenir la génération actuelle
  • lardon3d_resource_governor_wait_for_change() - Attendre un changement
  • lardon3d_resource_governor_pressure() - Lire GREEN, YELLOW ou RED

Stockage externe additif

  • lardon3d_resource_governor_register_external_storage() / lardon3d_resource_governor_update_external_storage() / lardon3d_resource_governor_unregister_external_storage() / lardon3d_resource_governor_get_external_storage() - Copier et observer l'état physique borné d'un contrôleur exact
  • lardon3d_resource_governor_acquire_scratch() / lardon3d_resource_governor_release_scratch() - Posséder un lease scratch de production sous l'orchestration du Governor

Invariants

  1. La Queue/runtime ne décide jamais des ressources
  2. Le Resource Governor est l'unique propriétaire des budgets
  3. Les réservations sont obligatoires avant toute exécution
  4. Les réservations sont libérées exactement une fois
  5. Les estimations de ressources sont immuables
  6. L'historique des métriques est strictement borné (8 entrées par classe)
  7. Un contrat de séquence est immutable jusqu'à sa libération; seule la séquence suivante peut être adaptée
  8. Swap, scratch et espace SSD n'augmentent jamais la capacité RAM
  9. Un lease scratch de production passe par le Governor et reste attaché à l'objet lease exact jusqu'à sa libération

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.images est 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 == 0 signifie 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.extract réserve un lot de 1, demande la borne positive int d'OpenCV 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..compute_pool et le callback applique ce nombre immutable au pool OpenCV. record_batch couvre la validation source, le décodage, ORB, la publication et la finalisation DB ; peak_memory_bytes == 0 signifie « mesure inconnue ».
  • visual_index.update demande jusqu'à seize 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 plus cpu_threads - 1 enfants, 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_batch compte uniquement les memberships commités et conserve la mémoire inconnue à zéro.
  • candidate_pair.generate demande jusqu'à soixante-quatre threads CPU, un slot I/O, 256 Kio fixes et 8 Mio par Feature Set, par lots de 1 à 64. Le GPU vaut zéro. La reconstruction reconnaît la forme immédiatement antérieure exacte (256 Kio fixes, 64 Kio par item, CPU12) et la plus ancienne forme sérielle exacte (128 Kio fixes, 64 Kio par item, CPU1), toutes deux à lot 1..64, I/O1, GPU0, classe CPU. Elle les normalise éphémèrement vers la forme courante 256 Kio + 8 Mio/item, CPU64 ; aucun checkpoint d'estimation seule n'est publié et une forme voisine n'est jamais réinterprétée comme legacy. record_batch compte le nombre de paires générées par séquence et la durée réelle du lot ; peak_memory_bytes == 0 signifie « 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 64 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.
  • geometric_verifier.run réserve 8 Mio par Match Result admis, un slot I/O, CPU 1..8 utile et lot 1..16. La fenêtre/participant est indépendamment sûre jusqu'à 16, mais CPU12 n'a ajouté que 2,68 % sur CPU8 dans la preuve réelle de 4113 parents, sous le deadband 5 %. Les enfants préparent des parents indépendants et sont tous joints avant la publication owner-only, le curseur contigu, le checkpoint et sequence_break. L'USAC interne garde isParallel=false; CPU/lot ne modifient ni fingerprint ni GVR.
  • matcher.run demande 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, KNN k=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 plus cpu_threads - 1 enfants 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ête MIXED avec 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 classe CPU. 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, et backend_info rapporte 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 choix AUTO avant chaque séquence. Une fois admis, ce choix ne varie jamais dans la séquence. La nouvelle signature MIXED reconstruit 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 classe CPU reconstruisent 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_VULKAN ou mix de paires complètes), avec une raison de fallback. Les participants Matcher CPU sont comptés uniquement dans cpu_threads; helpers reste zéro. La soumission asynchrone est une couture privée de src/orb_vulkan_backend_internal.h, absente de l'ABI public. Le backend conserve deux slots maximum et un handle exact slot+generation par requête; finish ne peut consommer que ce handle et les comptes soumis. Une génération arrivée à UINT64_MAX ne 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.run réserve un worker CPU, aucun GPU et aucun fan-out GVR. L'estimation est 4 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.

L'intégration opérationnelle SSD/Governor ci-dessus est CURRENT / VALIDATED OPERATIONAL. Le jalon global qui la contient est GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN. Les validations fraîches portable/Vulkan, ASan/UBSan, LSan qualifié, TSan et ABI sont acquises ; l'unique revue finale indépendante a conclu PASS sans finding bloquant. Le gel porte sur la frontière auditée et ne crée aucun consommateur scratch ni budget RAM.