StyloBot Release Series: Trouver et corriger la croissance sans limite dans les services de réseau longs
Long-les services qui fonctionnent ont tendance à se déplacer lentement vers la mémoire non liée .Cette est la façon dont je trouve cette dérive,la manière dont j’envisage de le corriger sans en papierer sur elle–, et un exemple travaillé du StyloBotMSC5, couche de similitude vectorielle qui l’a ramenée de M SK6 GB sur le Large Object Heap à sous 6 MBMST8
StyloBot Release Series
- Comportement, Non identité: pourquoi StyloBot modele les clients de façon comportementale
- Behaviour-Aware ASP.NET UI: le serveur - remet la surface sur ce résultat de détection
- Trouver et rétablir une croissance sans limites dans les services longs-Running .NET: la discipline de fiabilité
- Behaviour-Aware TypeScript UI: ExpressM SK1 Fastify, et composants de navigateur
- L'architecture de la Sidecar: comment le moteur de détection se connecte aux piles NET non -.
- Apprentissage pour devenir plus rapide: le système d'apprentissage adaptatif , quatre- mémoire de niveauM SK3 et cache des verdicts
- Étudier ce qui ne va pas se tenir: la discipline de vérification : un fichier BDF conduit à la régressionM SK2 charge, et à l'étalonnage
- StyloExtract - un convertisseur HTML d'apprentissage local à Markdown: la couche de marquage HTML→ qui se joigne au détecteur , le bug lucidVIEW du fauteuil roulant qui a capté l’image
L'exemple travaillé est la couche de vector StyloBot, mais la discipline n'est pas't Stylo BotM SK2specificMSC3 Tout service longMST4running MSST5NET qui accumule l'état du trafic augmente finalement une classe d'erreurs que vous pouvezMst6t catch dans les essais ou les courts essais de chargementM st7 Il apparaît seulement après des jours de trafic réelMstr8 Ce post est le guide que j'utilise pour le trouverM str9
Le modèle comportemental se trouve dans Comportement, Non identité; la surface ASP.NET dans Behaviour-Aware ASP.NET UI; source à github.comM SK1scottgal/stylobot.
La classe du problème
Long-services opérationnels s’accumulent. Chaque cache, , chaque magasin d’apprentissage, , chaque M SK4nous, MSC5 nous garderons seulement les dernières demandes de N." le buffer est un petit accumulateur,. chacun semble bien sur son propre compte,. Ensemble,M SK9 dans un processus qui, depuis une semaine, a été mis à jour par ' , ils forment une forme de mémoire qu’aucun essai ne reproduit jamais
- fonctionne bien dans dev, CI, et le premier jour de production
- se déplace graduellement vers OOM, puis soit s'écrase, soit commence à naviguer
Vous l'apercevez en cherchant délibérément, sur un processus qui ' a duré assez longtemps pour que la forme apparaisse
Étape 1: Examens périodiques de la fiabilité (l'acte d'examiner les données
Le premier élément de la discipline est :
Chaque quelques sorties, Je cesse d’ajouter des fonctionnalités et je regarde simplement le système en cours sous un trafic réaliste, demandant Tout cela semble-t-il mauvais? Aucune erreur spécifique, aucun objectif . Je regarde simplement. Les systèmes qui fonctionnent se déplacent silencieusementM SK3 ils ne faillent pasMSC4ils n’échouent pas fortement jusqu’à ce qu’ils échouent de façon catastrophiqueMST5 Si vous regardez seulement lorsque quelque chose se romptMst6 vousMSt7vous avez déjà perduM st8
La révision la plus récente a capté le bug ce post est sur: Mostlylucid.BotDetection.Demo Processus de sitting at a 20 GB resident set under synthetic test traffic . That' is the kind of number that should not survive thirty seconds of attention
Règle: Mettez un espace récurrent sur le calendrier pour lire vos propres mesures.
Étape 2: Laissez les compteurs vous surprendre
L’outil approprié pour " est quelque chose de mal ?" sur un processus .NET est dotnet-counters. Celle-ci, ', est gratuite, c'est déjà installée ' ce qui vous dit la vérité sur ce que votre processus fait actuellement
Quelques commandes plus tard sur le StyloBot demo:
dotnet.gc.last_collection.heap.size[loh] 13,393,217,096 bytes (13.4 GB)
dotnet.gc.heap.total_allocated 97 MB/sec
dotnet.exceptions[SqliteException] 4/sec
Que' est suffisant pour savoir ce qui est errant, sans lire de code encore. Grand trou d'objets a été de 13.4 GB et s’est accrue à près d’M SK1 MB / sec. Et quelque chose affectait des objets énormes continuellementMSC4 et le GenMSSK5 GC ne pouvait pas se tenir avec euxMST6
Un rafraîchissement rapide sur le LOH ( car chaque .NET dev touche ce dernier en bout de ligne
Le Large Object Heap est l’une des surprises les plus courantes lorsque vous présentez pour la première fois un long service -running .NET. Il vaut la peine de le redéfinir parce que presque tout le monde l’obtient de la manière difficile.
- Le GC est composé de trois générations :
- Tout ce qui est plus grand que 85 KB ne va pas dans ces générations. Il va directement sur le Grand trou d'objets.
- Le LOH n'est recueilli qu'au cours d'un Gen2 GC, qui est la collecte la plus coûteuse .NET fait. GenM SK3 se produit fréquemmentMSC4 et le temps de démarrage essaie dur d'y éviter
- pire, par défaut le LOH est non compacte quand il est recueilli; il libère simplement les trous. Donc même après une génétique 2, le LOH se fragmenteM SK3 vous avez de l’espace libreMSC4 mais c’est ce qui est incorrectMST5 des trous de taille appropriée pour la prochaine allocationMSP7 De nouveaux grands objets élargissent le chevauchement plutôt que de le réutiliserMSV8
- Efficacité nette: continuer à attribuer de grands objets à un rythme constantM SK1 et votre processus ' sa empreinte de mémoire marche vers le haut, que les objets soient ou non encore renvoyés. Il semble comme une fuite même s’il n’est pas une
Les sources les plus importantes de croissance accidentelle des LOH dans les services réels .NETM SK1 selon ma expérience:
| Source | Pourquoi finit-il sur le LOH ? | |
|---|---|---|
JsonSerializer.Serialize(obj) Retour d'un string |
Un objet de 100 MB devient une chaîne UTF de \200 MB de | -16 d'une seule allocation. |
MemoryStream vous laissez augmenter sans limite |
Le tampon interne double au-delà de 85 KB et demeure sur le LOH | |
byte[] buf = new byte[n] pour le grand n |
Répartition directe du LOH ; très courante dans le fichier | |
List<T> qui augmente depuis ~10 K référence-articles typés |
croisement de l’arrée d’appui 85 KB et débarque sur le LOH S | |
string.Concat / StringBuilder sur un grand texte |
Final ToString() est une seule allocation énorme |
|
XmlSerializer / DataContractSerializer de grands graphiques |
La même forme que le cas JSON |
Si vous ne vous souvenez qu’une règle de la colonne d’oeil: être soupçonné de tout chemin de code qui produit un grand objet contigu unique sur le temps-temps ou par demande. Il s’agit de la forme qui écrase les longs services d’exploitation, - et ,; elle est exactement celle que nous allons trouver dans StyloBot.
flowchart LR
classDef input fill:none,stroke:#3b82f6,stroke-width:2px
classDef store fill:none,stroke:#f59e0b,stroke-width:2px
classDef async fill:none,stroke:#a855f7,stroke-width:2px
classDef problem fill:none,stroke:#ef4444,stroke-width:2px
A["Per-request handler<br/>Add to collection"]:::input --> B["Long-lived List / Dict / Buffer"]:::store
C["Timer / autosave<br/>every N min"]:::async --> D["JsonSerializer / MemoryStream<br/>single >85 KB allocation"]:::problem
B --> D
D --> E["Large Object Heap"]:::problem
E --> F["Gen2 GC<br/>infrequent, expensive"]:::async
F --> G["LOH not compacted by default<br/>fragments build up"]:::problem
G --> H["Process RSS marches upward"]:::problem
Outils qui aident à le diagnostiquer:
dotnet-counterspour les numéros vivants (la jauge LOH au-dessus ).dotnet-gcdumpetdotnet-dumppour des snapshots que vous pouvez ouvrir dans Visual Studio ou PerfView.PerfView elle-même pour tracer les allocations jusqu’à un stack.
Profileurs de JetBrains CLI ( ce que j’ai réellement utilisé pour cette révision de travail). Maintenant emballé comme appropriée . Outils mondiaux du NETM SK3 si silencieux
JetBrains.dotMemory.GlobalTools: attacher , prendre une photo de mémoire.dmwplus tard pour voir quelles racines sont maintenues les allocations de LOH.JetBrains.dotTrace.GlobalTools: la même forme pour l'échantillonnage / le traçage ♫ / les données de lignes horaires ♫
dotnet tool install -g JetBrains.dotMemory.GlobalTools dotnet tool install -g JetBrains.dotTrace.GlobalTools dotMemory get-snapshot <pid> --save-to-dir=./snapshots dotTrace attach <pid> --profiling-type=Sampling --timeout=60s --save-to=./trace.dtpDivision de la main-d’œuvre pour ce réaménagement:
dotnet-countersje lui ai dit que le LOH était le problème; la photo de l’image de souvenir me a dit que la cause était le timer d’auto-conservation.
Si vous en avez vraiment besoin, il y a des cages d’évacuation. GCSettings.LargeObjectHeapCompactionMode = CompactOnce force une compaction LOH de 1 - off . RecyclableMemoryStream des tampons de base Microsoft pour éviter la répartition en premier lieu. Les deux sont des plasters. La bonne solution est presque toujours d’arrêter de produire l’objet géant en premier placeM SK2
Retour au diagnostic
Ainsi, chaque fois que vous voyez une croissance constante du LOH dans un service de longue durée, la question est presque toujours la même. Quels sont les critères d’attribution des objets très grands, quelle cadence et pourquoi? La forme de la réponse (timer? gestionnaire des demandes ? serialisateurM SK3 pool de tamponMSC4 vous dit où regarder dans le code
Les exceptions sont également importantes. 4/sec de SqliteException est assez bas pour ne pas apparaître dans les registres quiconque lit, mais suffisamment élevé pour vous dire qu’un chemin de code se rétryne silencieusement . C’est ce type d’aiguillage que l’examen périodique repère la réponse à un incident qui n’y arrive jamais
Règle: lorsque quelque chose semble mal, confirmer avec dotnet-counters before guessing. Les chiffres atténueront le rayon de recherche d’une ordre de grandeur
Étape 3: La mauvaise approcheM SK1l'odeur d'abstraction (exemplaire amélioré: HNSW pour le cachingMSC4
C’est là que la discipline devient intéressante, car la temptation est toujours de ajustement le symptôme plutôt que de remettre en question l’abstraction.
Vocab rapide (pour les prochaines sectionsM SK1
- Vector: un ensemble fixe de floats d’une taille de - - eM SK3g.
float[64]. Chacune des fentes est une propriété mesurée de la demande.float[].- Comparaison des vecteurs: habituellement similitude des cosine - la cosine de l'angle entre deux vecteursM SK1 Plus proche de 1.0 = plus similaire. La mathématique réelle est un produit ponctuel divisé par deux normesMSC5 une méthode appeléeMNK6
- Environ le voisin le plus proche (ANNM SK1: " étant donné ce vecteurM SK2 trouver le N le plus proche sur les millions, rapideMSC4 bruteMST5 la comparaison de force est OMSSK6NMSS7 par demandeMSL8 Les algorithmes ANN obtiennent des résultats sub--milliseconds au prix occasionnel d’avoir manqué le TÉLÉCOPIEURS match le plus proche.
- HNSW: l’un de ces algorithmes ANN. Il construit un graphe stratifié où la couche supérieure a quelques puits - des nodes connectés et les couches inférieures remplissent en détailM SK3 Vous entrez au sommet et zoomez dansMSC4 Excellent pour indexer une grande Établissement stable ensemble de vecteurs. Non conçu pour "toute demande ajoute un et les anciens vont disparaîtreM SK2 - le point pertinent de ce tout postMSC4
- Centres: la moyenne d'un groupe de vecteurs - un vecteur qui résume le regroupement. Si vous avez des vecteurs dont l'apparence est presque identique à celle de 10,000, vous pouvez les jeter et conserver un centroidM SK5 le centroid est un suppléant défaillant mais compactMSC6 Les stratégies de compression utilisent cela pour transformer une grande partie de l'histoire brute en une petite histoire sommaire.
Pour StyloBot,, la croissance du LOH a entraîné trois classes structuralement identiques dans le processus HNSW (Hiérarchique de petits mondes navigables géographiquesoriginal Malkov & papier Yashunin) utilisé pour la recherche de similitude au-dessus de la signatureM SK1 session , et vecteurs d'intention. Chacun avait un champ commeMSC4
private readonly List<float[]> _graphVectors = new();
Chacune a été fournie par un gestionnaire d'apprentissage qui s'est abonné à LearningEventType.FullDetection, qui déclenche un feu toutes les demandes HTTP. Chacune des demandes a ajouté un vecteur
Et tous les cinq minutes, ,, un Timeur de sauvegarde automatique a serialisé l’ensemble du graphique à JSON et l’a écrit sur le disque.
private readonly TimeSpan AutoSaveInterval = TimeSpan.FromMinutes(5);
À l’échelle de démonstration, signatures.vectors.json était 104 MBM SK1 En production: intent.meta.json à 70 MBM SK1 intent.vectors.json à 51 MBM SK1 La serialisation de JSON crée une chaîne contiguë dans la mémoire à |100+ MB. Cette chaîne va directement vers le LOH\ .\ Trois indices\ ,\ tous les cinq minutes\ MSC6\ croissant continuellement\ M SK7\
flowchart LR
classDef input fill:none,stroke:#3b82f6,stroke-width:2px
classDef async fill:none,stroke:#a855f7,stroke-width:2px
classDef problem fill:none,stroke:#ef4444,stroke-width:2px
R["HTTP request"]:::input --> H["LearningHandler<br/>FullDetection event"]:::async
H --> L["List<float[]><br/>graph vectors<br/>(no eviction)"]:::problem
L -.5 min timer.-> J["JsonSerializer.Serialize<br/>~100 MB string"]:::problem
J --> LOH["Large Object Heap"]:::problem
L -->|grows every request| L
LOH -.fragmentation.-> RSS["Process RSS climbs"]:::problem
Maintenant: la solution facile est d’ajouter un cap. MaxVectors = 10_000, Évacuation de la LRU , l’expédition à bord de celle-ci . Il aurait pu fonctionnerM SK3 Les chiffres seraient dépassés, le tableau de bord serait apparu bienMNK5 Il serait également erronéMRK6
HNSW est une excellente technologie et je l’utilise délibérément ailleurs (Self-Databases de vecteurs hébergées avec Qdrant, Recherche et indexation hybrides de RAG, GraphRAG minimal viable). Pinecone, Favoriser, pgvector, et Qdrant tous les utilisateurs l’utilisent à l’interne. "pour chaque empreinte digitale active, conserver une petite fenêtre de vecteurs comportementaux récents afin que la détection puisse comparer la demande actuelle avec le comportement antérieur d’un vecteur similaire Les empreintes digitales des bottes se répètent ( demeurer chaude ); les empreintements digitales humaines ne sont pas non plus visibles Le plus simple possible Post list the in-process HNSW index as a feature ; this post is the followingM SK2up admitting where that was the wrong fit.
Il y a un test rapide de l’humidité, :, si vous imaginez couper une bouchonneuse sur la structure. , est le résultat sémantiquement toujours ce que vous voulez.
Règle: Lorsque vous voyez une croissance sans limites, la question n’est pas "comment capter cette structure ?" mais "est-ce la bonne structure pour ce que nous faisons? La première dissimule le symptôme; la seconde entend ce qu’elle vous dit.
Étape 4: Choisissez la forme appropriée , pas seulement un cap
Une fois que vous avez désigné la mauvaise abstraction, le remplacement écrit habituellement lui-même. Pour StyloBot, il y avait deux couches.
La couche chaude: a délimitée BoundedVectorCache<TEntry>, un enveloppement épais autour ConcurrentDictionary avec accès-évacuation prioritaire de la fréquence. Le coefficient de retenue donne aux entrées classifiées par le bot -un poids de survie MSC3xM SK4 de sorte que l’auto-cache
retentionScorer: (_, entry) => entry.WasBot ? 2.0 : 1.0
La couche persistante (FOSS): trois nouvelles tables SQLite (signature_centroids, session_centroids, intent_centroids) entreposage de centrides comprimés à partir d'un VectorCompactionService. Vektors stockés sous forme de floats bruts MemoryMarshal.AsBytes:
internal static byte[] PackFloats(float[] v) =>
MemoryMarshal.AsBytes(v.AsSpan()).ToArray();
Système de serialisation binaire compacte. Non 100+ Strings JSON MBM SK2 Aucun LOH.
Note de niveau d’entreposage. SQLite est la version FOSS / single-binary backend - le bon appel pour "mettre l’exe sur un Pi et marcher à l’extérieurM SK4 StyloBot commercial utilise PostgreSQL avec pgvector, vous donne une recherche de similitude indexée appropriée (HNSWM SK2IVFFlat dans la base de données - sur un corps stable , où l’HNSW appartient, à une échelle horizontaleM SK3 et état partagé dans toute la flottilleMSC4 La même règle architecturaleMska5 un backend différentM Ska6 sqlite-vss est une pierre angulaire si vous éloignez SQLite mais que vous voulez rester un seul utilisateur.
La recherche de similitude sur la couche persistante FOSS est brute-force cosine sur toutes les lignesM SK1 accéléré avec SIMD (System.Numerics.Tensors.TensorPrimitives). À l'échelle compacteM SK1centroïde ( centaines à quelques milliers de rangées), un balayage complet prend mSSK4 ms sur une PiMSC5 Celle-ci
Une note sur L1/LM SK1 compaction, depuis "compaction" fait du travail réel dans cette sectionM SK3 Le service de compaction fonctionne tous les soirs et réduit l'historique en deux passes
- L1: prenez tous les vecteurs bruts écrits aujourd'hui , groupez ceux qui sont très similaires M SK2 et remplacez chaque groupe par un centroïde ( la moyenne MNK4 CheapMNK5 se produit fréquemment MMK6 modeste réduction de taille
- L2: prendre LM SK1 centroïdes qui sont eux-mêmes semblables au cours des jours, / semaines et les fusionner de nouveau, MSC3 aggressif, ; se produit moins fréquemment,; réduction de grande envergure.
Il s'agit du même modèle LSM-moteurs de stockage d'arbres (RocksDB,LevelDB+, Cassandra=)Utilisation pour SSTablesM SK5Niveau 0contient des données récentes finesMSC7dont les données sont consolidéesMNK8les niveaux inférieurs contiennent des données compactesMMK9donts sommairesMNK10En empruntant, cela fonctionne parce que le mode d'accès est le mêmeMRK11la plupart des lectures atteignent les données récenteMRK12une petite fraction des lecturements ont besoin de la longue queueM SK13 Rien ne permet de garder chaque ligne brute pour toujours.
flowchart LR
classDef input fill:none,stroke:#3b82f6,stroke-width:2px
classDef store fill:none,stroke:#f59e0b,stroke-width:2px
classDef async fill:none,stroke:#a855f7,stroke-width:2px
classDef good fill:none,stroke:#22c55e,stroke-width:2px
R["HTTP request"]:::input --> FP["Fast path<br/>BoundedVectorCache.TryGet"]:::good
FP -->|hit| S["Use similarity signal"]:::good
FP -->|miss| N["null<br/>other detectors still run"]:::good
R -.post-response.-> BG["Background learning handler"]:::async
BG --> DB["SQLite (FOSS)<br/>or Postgres+pgvector (paid)"]:::store
BG --> WARM["Warm cache for next time"]:::async
WARM --> FP
NC["Nightly VectorCompactionService<br/>L1 then L2"]:::async --> DB
DB --> NC
La forme générale (Cache chaude limitée pour l'état stable + entrepôt compact persistant de l'histoire + compacteur périodique entre eux) apparaît de plus en plus dans les systèmes d’apprentissage à long terme -en cours. Il vaut la peine de garder le ' dans votre trousse d’outilsM SK4 Lorsque quelqu’un s’adresse à l’HNSW ou au pgvector pour une charge de travail qui, selon lui, est en fait un cache, , c’est la chose la moins coûteuse
Règle: La structure de données la plus simple qui convient au modèle de temps d'exécution, non celui qui convient à l'ensemble de données ' sa taille sur papier
Étape 5: Faire en sorte que le chemin rapide tolére les erreurs
C’est une chose subtile, car il s’agit de retenue plutôt que d’addition.
Si le chemin de détectation rapide dans StyloBot est synchrone, et que la vérification des similitudes n’est pas une étude du dictionnaire qui bloque-, il y a temptation à bloquer ce chemin.
if (!_cache.TryGet(signatureId, out var entry))
return null; // no signal this request - other 48 detectors still run
Une erreur signifie Aucun signal de similitude pour cette demande. Les autres détecteurs continuent de fonctionner. Un gestionnaire d'arrière-plan demande SQLite après que la requête a été remplie et que le cache est chauffé pour la prochaine fois.
C'est là où la plupart des systèmes de cache vont mal.
Règle: si vous pouvez't tolérer une erreur , vous n’avez pas'il n’y a pas de cacheM SK3 Vous avez une foule fantaisie
Étape 6: Vérification tous les accumulateur, non seulement l'actuelle forte
Une fois que vous avez trouvé une structure non liée, supposez qu’il y en a d’autres.
La vérification est mécanique. qui décide quand les entrées quittent la zone, quel est le cas le pire dans un trafic hostile? Si vous pouvez't répondre à toutes les trois dans une seule phrase , c’est la même discipline que le Signales éphémères Modèle: tout ce qui s'accumule doit se dégraderM SK1 évacuer , ou compacter.)
Pour StyloBot, la plupart des accumulateurs étaient déjà bien munis
EphemeralPatternReputationCache: cap dur à 10,000 entrées avec décomposition de fond et évacuation des LRUNeutral→Suspect→ConfirmedBadla machine étatique, et l'hysterèse asymétrique vivent dans Apprentissage pour devenir plus rapide)BehavioralPatternAnalyzer: IMemoryCache avec des limites d'identité par - (50 voies de routesM SK3 | | 100 horaires, \ 15-min TTLMSC7DriftDetectionHandler: 10,000 échantillonsSessionEscalationService: 35-minute TTL avec horlogeM SK2évacuation dirigée
On a besoin d’une attention au-delà des classes HNSW: MarkovTracker._cohortBaselines. Le Chaîne Markov (separée des chaînes de signatures perM SK2, qui avaient déjà été évacuées par les LRU à MaxTrackedSignatures). Les lignes de base des cohortes (une par cohorte de trafic, comme "centre de données -nouveau"ou S"résidentiel-retournement") n'ont pas eu d'évacuation du tout. La fixationM SK9 évacuer les cohorts les plus froides SSK10les transitions totales les moins élevées) lorsque le dictionnaire dépasse SelfMaintenanceOptions.MarkovCohortSize.
Règle: Chaque collecte d’un seul tonne répond à trois questions ou c’est-à-dire :
Étape 7: Faire chaque lien configurable
Le mode de défaillance suivant après "no bound" est "bound thatM SK3s wrong for this hardware MaxEntries = 10_000 est bon jusqu’à ce que quelqu’un effectue votre service sur Pi4, ou dans un conteneur avec 256 MBM SK2
Pour StyloBot, chaque limite a atterri sous un seul SelfMaintenanceOptions blocage dans appsettings.json. Les paramètres par défaut fonctionnent pour un serveur standard. LowMemory Présélection statique:
public static SelfMaintenanceOptions LowMemory => new()
{
SignatureCacheSize = 1_000,
SessionCacheSize = 500,
IntentCacheSize = 300,
MarkovCohortSize = 2_000,
CacheSlidingExpiration = TimeSpan.FromHours(1),
};
builder.Services.AddBotDetection(opts =>
{
opts.SelfMaintenance = SelfMaintenanceOptions.LowMemory;
});
Ou par l'entremise appsettings.json pour l'environnement-ajustement spécifiqueM SK1
{
"BotDetection": {
"SelfMaintenance": {
"SignatureCacheSize": 1000,
"SessionCacheSize": 500,
"IntentCacheSize": 300,
"CentroidRetentionDays": 14
}
}
}
Le point plus profond, :, une limite que ' fixe en un seul endroit est quelque chose dont les opérateurs peuvent raisonnablement penser. const int les déclarations est quelque chose que personne ne peut expliquer.
Règle: tous les limites que l’opérateur pourrait vouloir changer pour leur matériel vie dans un bloc de configuration , ne pas éparpillée à travers la base de code .
À quoi ressemble "fixed"
Pour StyloBot spécifiquement (FOSS buildM SK1 LowMemory preset):
| Composante | Component | Avant | Après M |
|---|---|---|---|
| Indice de la signature HNSW | LOH non lié | ||
| Indice de l'HNSW des séances | LOH non lié | ||
| Indice de l'intensité du HNSW | LOH non lié | ||
| Les tampons de sauvegarde automatique du JSON | MSC2 LOH MB tous les MSC3 min SMC4 | ||
| Lignes de référence de la cohorte de Markov | Non lié | ||
| La couche de vecteur totale | 13+ GB LOH | <6 Mo |
Le modèle de détection est inchangé. Ce qui a changé où l'on trouve des preuves de similitude et quand il est permis de modifier le chemin rapide. Centroïdes survivants remarrages dans SQLite (FOSS) ou PostgresM SK3pgvector MSC4paidMSC5 la compaction nocturne produit encore LMST6LMSS7 aucun n'exige une mémoire non limitéeMSST8
Sur un Pi4 avec le LowMemory preset, la construction FOSS se situe sous M SK2 MB RSS après le réchauffement , indéfinimentMNK4 Postgres payésMMK5 les déploiements appuyés héritent de la même chaleurMRK6 discipline des caches MNK7 la couche persistante s'élargit simplement horizontalement au lieu d'être vivant à côté du processusMZK8 En tout état de cause
La leçon générale
L’ajout d’un cap limite la mémoire sans changer l’architecture. La mauvaise abstraction demeure erronéeM SK1 le symptôme devient simplement plus silencieux . La personne suivante qui touche le système hérite d’une structure qui pratiquement fonctionne, ce qui est pire que celle qui ne fonctionne pas de toute évidence.
Le modèle qui se répète dans les systèmes d’apprentissage à long terme
- Capter le chemin chaud: boundedM SK1 in-memory , conçu pour tolérer les missesMSC4 politique d'évacuation alignée sur la charge de travail
- Compression de l'histoire: store binaire persistante compacte , comprimée périodiquement , accessible hors du fil de demande
- Vérification de tout le reste: chaque accumulateur répond à ce qui l'oblige , ce qui le évacue
- Centraliser les boutons: un bloc de configuration , pas quinze
constLa plupart des enquêtes menées dans le cadre de l’enquête ont été effectuées par les autorités locales. - Calendrier de la recherche: le bug n'existe que dans le système en cours
Fixer la forme, non le symptôme.
Autres lectures
StyloBot : série de sorties
- Comportement, Non identité: le modèle comportemental et le regroupement Leiden de cette couche est la mémoire pour
- Behaviour-Aware ASP.NET UI: la surface qui consomme les verdicts de détection dans Razor
- Apprentissage pour devenir plus rapide: le système d’apprentissage de quatre niveaux et la cache des verdicts que cette discipline de mémoire maintient limitée
- Bot Detection Part 2: Signature Pipeline and StyloBot Architecture: la détection originale-poste d'architecture de l'engine
- Partie de détection des bottes 3: aussi simple que possible: la diminution des deux lignes de l’indice HNSW dans le processus -, où il a été introduit pour la première fois
HNSW et recherche vectorielle dans ce blog
- Self-Databases de vecteurs hébergées avec Qdrant: plongée profonde de l’HNSW : couches graphiquesM SK2
M,ef_construct, réglage - Recherche et indexation hybrides de RAG: HNSW comme la moitié dense d'un pipeline hybride de récupération
- GraphRAG minimal viable: DuckDB VSS HNSW vous aperçoit (
array_cosine_distancecontrearray_cosine_similarity) - Recherche sémantique avec ONNX et Qdrant: Configuration de l'HNSW via le client Qdrant .NET
- GraphRAG: Pourquoi la recherche par vecteur se détériore au niveau du corpus: pourquoi la pure ANN n’est pas suffisante
Références externes
- HNSW (Wikipedia)
- Malkov & YashuninM SK1 Recherche efficace et robuste approximative du voisin le plus proche à l'aide de graphiques hiérarchiques Navigables Small World: le papier original 2016
- Pinecone: Faiss M SK1 Expliqueur de l'HNSW
- Concepts d'indexation Qdrant
- .NET Large Object Heap docs
dotnet-countersRéférence: l’outil qui a en premier lieu survolé le LOH- Commande JetBrains dotMemory-profileur de ligne: le profileur de mémoire d'outils CLI / global- utilisé pour cette révision
- Commande JetBrains dotTrace-profileur de ligne: la même forme pour le rendement / profilage des lignes de temps
- sqlite-vss: SQLite + Faiss, si et quand la couche persistante dépasse la force brute
Source de la mise en œuvre: github.comM SK1scottgal/stylobot. moteur en direct , tableaux de bord M SK2 et contrôles commerciaux à stylobot.net.