<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Chris Hegarty - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Chris Hegarty - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/fr/search-labs/author/chris-hegarty</link>
    </image>
    <link>https://www.elastic.co/fr/search-labs/author/chris-hegarty</link>
    <atom:link href="https://www.elastic.co/fr/search-labs/rss/author/chris-hegarty.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[fr]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 03:58:46 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Comment nous avons construit Elasticsearch simdvec pour faire de la recherche vectorielle l'une des plus rapides au monde]]></title>
    <description><![CDATA[Comment nous avons conçu Elasticsearch simdvec, la bibliothèque de noyaux SIMD optimisée manuellement qui alimente chaque requête de recherche vectorielle dans Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch simdvec est le moteur de calcul de distance vectorielle dans Elasticsearch. Il fournit des noyaux AVX-512 et NEON ajustés manuellement pour chaque type de vecteur pris en charge par Elasticsearch. Son architecture de calcul par lots masque la latence mémoire grâce à un prefetching explicite sur x86 et un chargement entrelacé sur ARM, surpassant jusqu'à 4 fois les performances de bibliothèques comme FAISS et jvector lorsque le volume de données dépasse la capacité du cache du processeur. Dans cet article, nous expliquons les raisons de sa création, son fonctionnement interne et comment il contribue à faire de la recherche vectorielle d'Elasticsearch l'une des plus rapides au monde.</p><h2>Comment nous avons conçu Elasticsearch simdvec</h2><p>Chaque requête de recherche vectorielle dans Elasticsearch, qu'il s'agisse d'un balayage transversal <a href="https://arxiv.org/abs/1603.09320">Hierarchical Navigable Small World (HNSW)</a>, d'un balayage de fichier inversé (IVF) ou d'une passe de reclassement, se résume au même problème : calculer les distances entre les vecteurs, des millions de fois par requête. Elasticsearch prend en charge un large éventail de types de données et de stratégies de quantification, de float32 à int8, bfloat16, binaire et quantification binaire améliorée (BBQ). Chacune présente des compromis différents entre mémoire, débit et rappel. Derrière tout cela se cache un moteur unique : simdvec.</p><p>Nous avons conçu simdvec pour rendre chaque calcul de distance aussi rapide que le permet le matériel. Dans cet article, nous expliquons pourquoi nous l'avons conçu, ce qu'il contient et où il a le plus d'impact.</p><h3>Construit comme une voiture de course</h3><p>En tant que passionnés de Formule 1 (l'un d'entre nous a travaillé pour l'écurie Ferrari), nous constatons un parallèle évident. Une Formule 1 est conçue dans un seul but : réaliser le meilleur temps au tour. La puissance du moteur, l'aérodynamisme et la conception du châssis n'ont d'importance que dans la mesure où ils contribuent à cet objectif. Il en va de même pour une base vectorielle, où le débit d'indexation, la latence des requêtes et le rappel sont les facteurs déterminants de sa performance.</p><p>Bien que le résultat final soit ce qui compte, atteindre les plus hauts niveaux de performance nécessite que chaque composant contribue de façon optimale. Il ne doit pas être simplement <em>suffisant</em>, mais <em>le meilleur </em>de sa catégorie. Simdvec est conçu dans cet esprit, en se concentrant sur une partie critique du système : le moteur. C'est une bibliothèque de noyaux dédiée, optimisée pour le <a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">Single Instruction Multiple Data</a> (SIMD), qui fournit des fonctions de distance C++ natives optimisées et appelées depuis Java via l'interface de fonction étrangère (FFI) <a href="https://openjdk.org/projects/panama/">Panama</a>. Il prend en charge le scoring en lots, le prefetching des lignes de cache et tous les types et mises en page de vecteurs utilisés dans Elasticsearch.</p><p>C'est le moteur derrière chaque requête.</p><h3>Pourquoi nous avons créé le nôtre</h3><p>Nous avons commencé en 2023 avec l'API Panama Vector dans Apache Lucene. Elle fonctionnait bien pour les produits scalaires de nombres flottants 32 bits, mais les besoins d'Elasticsearch ont rapidement dépassé ses capacités. Elasticsearch prend en charge une large gamme de types de vecteurs quantifiés : int8, int4, bfloat16, mono-bit et BBQ asymétrique. Chacun possède des stratégies SIMD, des compositions de packs et des exigences d'accumulateur différents. Au-delà de la couverture des types, les méthodes de scoring d'Elasticsearch exigent un débit supérieur à celui d'une simple paire : HNSW doit évaluer plusieurs voisins du graphe en une seule passe, IVF nécessite un scoring en lots de milliers de candidats avec prefetching, et le scoring sur disque doit fonctionner directement sur la mémoire mappée en mémoire sans copie. Nous avons examiné les solutions disponibles, mais aucune ne couvrait l'ensemble des besoins.</p><p>Nous avons donc développé simdvec : des noyaux C++ natifs optimisés manuellement, appelés depuis Java via FFI, avec scoring par lots, prefetching et prise en charge de tous les types vectoriels utilisés par Elasticsearch. En étant propriétaires de la bibliothèque, nous maîtrisons l'intégralité de la pile. Lorsque nous ajoutons un nouveau type de quantification comme BBQ, il bénéficie d'un noyau SIMD optimisé intégré à l'ensemble du système. Nous n'attendons pas qu'une bibliothèque tierce le prenne en charge et nous ne faisons aucun compromis sur les performances, quel que soit le type. Chaque requête vectorielle dans Elasticsearch – HNSW, IVF, de reclassement ou hybride – s'exécute sur ce moteur, conçu autour des opérations et des types que nous utilisons réellement.</p><p>Simdvec possède des bibliothèques natives distinctes pour x86 et ARM, chacune comportant plusieurs niveaux d'architecture du jeu d'instructions (ISA) sélectionnés au démarrage. La surcharge des appels depuis Java via FFI est très faible, de l'ordre de quelques <a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md#ffm-downcall-overhead-measurements">nanosecondes</a>.</p><h3>Le panorama</h3><p>Nous ne sommes pas les seuls à développer des noyaux de distance vectorielle optimisés pour SIMD. L'écosystème est riche et nous souhaitions comprendre les performances de simdvec. Non pas pour classer les projets, mais pour contextualiser et situer le moteur d'Elasticsearch. Nous avons sélectionné trois projets comme points de référence, chacun représentant une approche différente :</p><ul><li><p><strong>jvector</strong> : une bibliothèque Java de recherche de plus proches voisins approximatifs (ANN) qui utilise l'API Panama Vector pour le calcul de distance vectorisé, avec une accélération C native optionnelle sur x86.</p></li><li><p><strong>FAISS</strong> : un framework de recherche vectorielle open source largement déployé, avec des noyaux AVX2/AVX-512 ajustés manuellement.</p></li><li><p><strong>NumKong</strong> (anciennement SimSIMD) : une suite complète de plus de 2 000 noyaux SIMD ajustés manuellement, couvrant les fonctions de distance, les opérations matricielles et le calcul géospatial.</p></li></ul><p>Chaque projet répond à un objectif différent et fait l'objet de compromis différents. Nous incluons des numéros de référence provenant d'eux pour donner du contexte à la performance de simdvec sur les opérations spécifiques dont Elasticsearch a besoin.</p><h3>Comment nous mesurons</h3><p>Les <a href="https://github.com/ChrisHegarty/jvector-kernel-benchmarks">benchmarks simdvec et jvector</a> sont écrits en Java avec JMH, le harnais de microbenchmark JVM standard, avec la surcharge FFI incluse. Pour les <a href="https://github.com/ldematte/simsimd-benchmarks">benchmarks NumKong</a> et <a href="https://github.com/ChrisHegarty/faiss-kernel-benchmarks">FAISS</a>, nous avons écrit de petits programmes C/C++ utilisant Google Benchmark, qui est le framework standard de microbenchmark C++. Ces deux frameworks mesurent les temps d'exécution en nanosecondes, après une phase de warmup et un étalonnage des itérations. Nous avons vérifié, grâce à des compteurs de performance matériels, que toutes les bibliothèques utilisent SIMD sur les deux plateformes. L'ensemble du code des benchmarks est disponible publiquement dans les référentiels GitHub associés (et, pour simdvec, dans le référentiel <a href="https://github.com/elastic/elasticsearch">elasticsearch</a>).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90a7d66553be3c9d/6a1701d166c4f942caf8bea5/aee116772df161cf86b7668f575ac34c733a23c5-1580x238.png" alt="Tableau listant deux plateformes : x86 avec AMD EPYC Turin (Zen 5), AVX2 et AVX‑512, AWS c8a.4xlarge, et ARM avec Graviton 4 (Neoverse V2), NEON et SVE2, AWS c8g.4xlarge." /><p><strong>Logiciel :</strong> JDK 25.0.2, JMH 1.37, GCC 14, Google Benchmark (dernière version).</p><h2>Un vecteur à la fois</h2><p>L'opération fondamentale de la recherche vectorielle consiste à calculer la distance entre deux vecteurs. Chaque évaluation de voisinage HNSW, chaque score de candidat IVF, chaque comparaison de reclassement ramène à cette boucle interne.</p><p>Nous avons mesuré le débit d'une seule paire à 1 024 dimensions sur les deux plateformes, en commençant par le type float32, le type de référence et celui où l'écosystème est le plus compétitif. Nous avons comparé simdvec à FAISS et jvector ; nous avons exclu NumKong car il utilise des accumulateurs float64 pour float32, ce qui le rend 3,2 à 5,3 fois plus lent (selon la plateforme), privilégiant la précision numérique au débit. Pour une comparaison équitable, nous avons testé NumKong sur int8, où il utilise la même stratégie d'accumulateurs que simdvec.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3bc2ecaf3ad2b61/6a1701d2ab7f08039ddb9d0f/352cfa0fc18f123140f746d843404f80127fb1b7-1500x675.png" alt="Graphique à barres horizontales intitulé &quot;float32 Dot Product – AMD Turin&quot; qui compare cinq implémentations : FAISS AVX‑512 à 23,2 ns/op, ES simdvec AVX‑512 à 28,3 ns/op, FAISS AVX2 à 36,4 ns/op, ES simdvec AVX2 à 38,9 ns/op et jvector à 43,9 ns/op." /><p>Sur x86, FAISS AVX-512 est le noyau à paire unique le plus rapide à 23 ns. Simdvec AVX-512 suit à 28 ns, un écart qui reflète la surcharge d'appel FFI. Les deux utilisent le FMA 512 bits avec déroulement par accumulateurs multiples. Au niveau AVX2, les deux sont beaucoup plus proches, 36 ns et 39 ns respectivement, tous deux limités par le registre de 256 bits et les largeurs de chargement en mémoire. jvector arrive à 44 ns grâce à l'API Java Panama Vector. Panama génère un bon code SIMD, mais les intrinsèques C++ optimisées manuellement conservent un avantage.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcbbbe442631a575d/6a1701d42b835f81bcf4b083/95e44d9767f21ec4bccaed835e3c99aa180431ee-1500x495.png" alt="Graphique à barres horizontales intitulé &quot;float32 Dot Product – Graviton 4 (ARM)&quot; montrant ES simdvec à 70,2 ns/op, jvector à 110,0 ns/op et FAISS à 155,6 ns/op." /><p>Sur ARM, simdvec affiche le meilleur temps d'exécution (70 ns), devançant largement jvector (110 ns) et FAISS (156 ns). Simdvec utilise des noyaux NEON optimisés manuellement pour aarch64. Jvector, quant à lui, ne possède aucun code ARM natif et repose sur Panama. FAISS s'appuie sur la vectorisation automatique du compilateur plutôt que sur des fonctions intrinsèques NEON explicites, ce qui explique l'écart plus important. Ceci illustre l'avantage pratique de posséder la bibliothèque de noyaux : lors du passage d'Elasticsearch à Graviton, nous avons intégré des noyaux NEON dédiés. Ni jvector ni FAISS n'ont accordé la même priorité au code natif ARM.</p><p>Mais Elasticsearch ne se limite pas aux nombres à virgule flottante 32 bits. La quantification d'<strong>Int8</strong> réduit la mémoire d'un facteur 4, la quantification bfloat16 d'un facteur 2 et la quantification BBQ d'un facteur 32. Chaque type nécessite sa propre stratégie SIMD, et simdvec fournit des noyaux natifs optimisés manuellement pour chacun d'entre eux.</p><p>Parmi les bibliothèques que nous avons comparées, seule NumKong possède des noyaux comparables pour int8. Nous avons mesuré le produit scalaire int8, la distance euclidienne au carré et le cosinus pour le format int8 sur 1 024 dimensions.</p><p><strong>Score Int8 pour une seule paire (1 024 dimensions, ns/vec op – plus bas est mieux)</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ac255d158e93504/6a1701d5a292997e48d00eb1/a0b852fd5f51d57bd2488886600472ee65ab64da-1594x378.png" alt="Tableau comparant les performances x86 et ARM pour le produit scalaire, la distance euclidienne au carré et les opérations cosinus, en listant les valeurs ES, NumKong et diff pour chaque opération sur les deux architectures." /><p>Sur les deux architectures, NumKong est aussi performant, voire plus rapide, pour les petites et moyennes dimensions, la différence étant principalement due à une surcharge d'appels réduite (appel direct en C contre FFI Java). Pour les grandes dimensions, simdvec rattrape son retard, grâce à une implémentation noyau plus efficace (qui utilise le déroulement en cascade) qui amortit le coût des appels : à mesure que la dimension augmente, <a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md#single-pair-i8-nsop-2">cet écart se réduit et finit par s'inverser</a>. Le point de bascule se situe entre 768 et 1 536 dimensions, selon la fonction et l'architecture.</p><p>Malgré la surcharge légèrement supérieure de l'interface FFI Java, simdvec rivalise avec les bibliothèques C/C++ fortement optimisées. Non seulement c'est la seule bibliothèque dotée de noyaux optimisés pour float32 <em>et</em> int8, mais elle est également en tête sur ARM et juste derrière FAISS sur x86 (pour float32), et très proche de NumKong sur les deux architectures (pour int8). Enfin, pour bfloat16, int4, binary et BBQ, bien qu'il existe des alternatives, simdvec se distingue par un SIMD ajusté manuellement et adapté à la structure des données de chaque type.</p><p>Cependant, un moteur de recherche en production n'évalue pas un vecteur à la fois ; il en évalue des milliers par requête. La question suivante est de savoir ce qui se passe à cette échelle.</p><h3>Des milliers à la fois</h3><p>Les performances sur une seule paire ne représentent qu'une partie du problème. En pratique, c'est le comportement des systèmes sous charge qui est important. Une simple requête HNSW peut évaluer des centaines de voisins dans le graphe. Une analyse IVF peut évaluer des milliers d'entrées de listes de publication. Une passe de reclassement peut évaluer des dizaines de milliers de candidats. Le débit sur une seule paire est important, mais ce qui compte davantage, c'est la rapidité avec laquelle il est possible d'évaluer de nombreux vecteurs et la façon dont les performances se dégradent lorsque l'ensemble de travail déborde des caches du processeur.</p><p>Simdvec propose un scoring par lots pour tous les types de données. Il ne s'agit pas simplement de boucles sur des noyaux de distance à une seule paire, mais de boucles internes dotées de plusieurs accumulateurs qui chargent le vecteur de requête une fois par pas de dimension et le partagent entre plusieurs vecteurs de documents, avec un prefetching explicite des lignes de cache pour le lot suivant. Ni jvector ni FAISS n'offrent d'équivalent (à l'heure actuelle). Jvector ne dispose pas d'API Bulk ; les appelants calculent donc le score d'une paire à la fois dans une boucle. FAISS expose <code>fvec_inner_products_ny</code>, qui, à l'heure actuelle, est implémenté comme une boucle sur sa fonction de distance à une seule paire, sans amortissement ni prefetching des requêtes.</p><p><strong>Float32.</strong> Pour mesurer l'impact au niveau du noyau, nous avons évalué une requête unique sur un nombre croissant de vecteurs de documents float32 de 1 024 dimensions, en utilisant des modèles d'accès aléatoire simulant des recherches de voisins dans un graphe dispersé de type HNSW. Les trois tailles d'ensemble de données (32, 625 et 32 500 vecteurs) ont été choisies de manière à ce que l'ensemble de travail dépasse respectivement les caches L1, L2 et L3.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2df64ec77a1921ae/6a1701d714b270393ae3c4de/1d90267be1c63ac82b8ba588617ebedb0be0d1b6-1334x558.png" alt="Deux graphiques à barres comparant les temps de scoring du produit scalaire Float32 en lots pour Elasticsearch simdvec, FAISS et jvector sur AMD Turin (x86, AVX-512) et Graviton 4 (ARM, NEON) sur trois tailles : 32 vecteurs, 625 vecteurs et 32 500 vecteurs." /><p>Lorsque les données tiennent dans le cache, simdvec est le plus rapide sur les deux plateformes, mais l'écart reste modeste, car l'arithmétique du noyau est prépondérante. La différence est flagrante lorsque la taille de l'ensemble de travail dépasse le cache L3. Sur x86, simdvec atteint 95 ns par vecteur, contre 165 ns pour FAISS et 412 ns pour jvector. Sur ARM, le constat est identique : simdvec se maintient à 162 ns, tandis que FAISS grimpe à 347 ns et jvector à 476 ns. Le prefetching et l'amortissement des requêtes dans simdvec masquent la latence mémoire, contrairement à une simple boucle sur des noyaux à paire unique. Cet avantage est encore plus manifeste précisément là où les véritables charges de travail de recherche s'exécutent, au cœur de la mémoire principale.</p><p><strong>Int8.</strong> Le même schéma s'applique aux types quantifiés. Nous avons mesuré le scoring par lots du produit scalaire int8 à 1 024 dimensions avec des tailles d'ensemble de données choisies pour dépasser les mêmes limites de cache L1, L2 et L3, en comparant le scoring par lots de simdvec au scoring de paires individuelles de NumKong dans une boucle.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda85e10007188d0/6a1701d9cf4f25d722b2cff7/9ee97d98b40d13b19370b76b33e67ea66bfb3250-1580x338.png" alt=" Tableau intitulé &quot;x86 – Bulk scoring, int8 dot product (ns/op, lower is better)&quot; comparant ES simdvec et NumKong sur trois tailles de vecteurs – 128, 2 500 et 130 000 – avec les valeurs ns/op correspondantes et les ratios d'accélération." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt972c311b7d08ba73/6a1701dadc55dec02ee00c87/601f50a03fa3e6263cbac9700281dfe3e511de60-1580x338.png" alt="Tableau intitulé &quot;ARM – Bulk scoring, int8 dot product (ns/op, lower is better)&quot; comparant ES simdvec et NumKong sur trois tailles de vecteurs – 128, 2 500 et 130 000 – avec les valeurs ns/op correspondantes et les ratios d'accélération" /><p>Sur x86, simdvec est de 1,2 à 1,9 fois plus rapide, grâce à la combinaison du prefetching explicite et du traitement par lots. Sur ARM, simdvec l'emporte également (de 1,7 à 1,9 fois plus rapide) quelle que soit la taille des ensembles de données. Cet avantage provient du traitement par lots de quatre vecteurs simultanément, offrant un parallélisme au niveau mémoire via un modèle d'accès entrelacé. Dans les deux cas, le résultat le plus frappant se situe au niveau des ensembles de données les plus volumineux, là où il est le plus significatif.</p><p>Les résultats concernant la distance au carré et le cosinus montrent un schéma similaire, avec des accélérations de 1,4 à 1,8 fois pour ARM, et de 1,3 à 3,0 fois pour x86 (détails <a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md">ici</a>).</p><h3>Quand la mémoire est essentielle</h3><p>Les index vectoriels de production ne tiennent généralement pas dans le cache du processeur. Un index vectoriel de 10 millions d'éléments (int8) à 1 024 dimensions pèse 10 Go. Le scoring des candidats implique le traitement en continu des données depuis la DRAM, et c'est là que l'architecture de scoring par lots fait toute la différence.</p><p>Nous avons utilisé des compteurs de performances matérielles pour mesurer ce qui se passe à l'intérieur du processeur pendant le scoring par lots et avons constaté que masquer la latence de la mémoire nécessite deux stratégies fondamentalement différentes, une par architecture.</p><p><strong>Sur x86, le prefetching explicite élimine les défauts de cache. </strong>Le noyau principal traite les vecteurs séquentiellement, chaque vecteur étant entièrement calculé avant le suivant, tout en émettant des instructions de prefetching pour le lot suivant. Les données futures sont chargées dans le cache L1 avant que le processeur n'en ait besoin.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt368f29b5143d0f5a/6a1701dcc1e8a51780f8817b/a39548f8060c2a4a5154521a4047dd92d8cd77be-1580x309.png" alt="Tableau intitulé « x86 (AMD Turin) — Compteurs matériels par opération int8 » comparant les modes simple et groupé pour les pertes de cache L1, l'IPC et les pertes de dTLB, avec les facteurs d'amélioration correspondants." /><p>Sur ARM, la même approche séquentielle s'est avérée peu performante, même avec prefetching. En revanche, le <strong>noyau de traitement par lots entrelace les charges</strong> de quatre vecteurs à chaque position d'itération, offrant ainsi au moteur hors séquence quatre flux mémoire indépendants. Le processeur ne récupère pas les données plus rapidement, mais le temps d'attente est réduit en ayant toujours une autre opération à traiter pendant que les requêtes mémoire sont en cours de traitement. Une analyse détaillée est disponible dans <a href="https://github.com/elastic/elasticsearch/issues/145412">ce ticket GitHub</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8751ac5b20b75508/6a1701deab7f08db83db9d13/832de3bc0d556a493b7bf3f250196018acfc1585-1580x238.png" alt="Tableau intitulé &quot;ARM (Graviton 4) – Hardware counters per int8 operation&quot; comparant les modes unique et groupé pour les défauts de cache L1 et les blocages du backend, avec les notes d'amélioration correspondantes" /><p>Les chiffres racontent deux histoires différentes :</p><ol><li><p>Sur x86, le prefetching réduit les défauts de cache de 139 000 à 19 000 et double le nombre d'instructions par cycle (IPC). L'avantage en termes de traitement par lots s'accroît avec la taille des données, passant de 1,2 fois pour le cache L2 à 2,8 fois pour les caches au-delà du cache L3, car le prefetching masque les allers-retours DRAM de plus en plus coûteux.</p></li><li><p>Sur ARM, le nombre d'échecs de cache reste pratiquement inchangé. Ce qui change, c'est le taux d'utilisation : les blocages du backend diminuent de 40 % car le modèle d'accès entrelacé assure l'alimentation continue du pipeline. Cet avantage reste constant, à 1,8 fois, quelle que soit la taille de l'ensemble de données, car le parallélisme au niveau de la mémoire s'applique que les données proviennent du cache ou de la DRAM.</p></li></ol><p>Deux architectures, deux stratégies, un résultat : à l'échelle de la production, simdvec maintient le pipeline du processeur occupé même lorsque les vecteurs sont dispersés dans la mémoire principale.</p><h2>Ce que cela signifie pour les utilisateurs d'Elasticsearch</h2><p>Ces capacités au niveau du noyau s'additionnent. Une simple requête de recherche vectorielle peut calculer des millions d'opérations de distance : parcours de graphe HNSW, scoring des candidats, reclassement. Sur des milliers de requêtes simultanées, chaque opération, même en nanosecondes, se traduit directement par une latence de requête et un débit de cluster optimaux. Que vous utilisiez float32, int8, bfloat16 ou BBQ, que votre index soit en mémoire ou sur disque, simdvec est le moteur sous-jacent, et chacune de ces opérations s'exécute sur ce même moteur, optimisé à la nanoseconde près.</p><p>L'essentiel à retenir est qu'à l'échelle de la production, les performances de la recherche vectorielle ne sont pas principalement déterminées par le débit SIMD brut. Elles dépendent surtout de la capacité du système à masquer efficacement la latence mémoire tout en maintenant la capacité de calcul sur des millions de petites opérations.</p><p>Les noyaux simdvec s'améliorent à quasiment chaque nouvelle version d'Elasticsearch. Dès l'apparition de nouveaux types de quantification et de plateformes matérielles, des noyaux optimisés sont intégrés. De plus, les types existants continuent de gagner en vitesse grâce à l'amélioration des implémentations déjà disponibles.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-vector-search-simdvec-engine</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-vector-search-simdvec-engine</guid>
    <category><![CDATA[Base vectorielle]]></category>
    <category><![CDATA[À l'intérieur d'Elastic]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Lorenzo Dematte,Simon Cooper]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18088409621369b/6a1701dfdc55de7297e00c8b/df9646091bafbbf0a6dfd212ff8a6bd1e8589708-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Statistiques ES|QL plus rapides avec des tables de hachage de style suisse]]></title>
    <description><![CDATA[Comment le hachage d'inspiration suisse et la conception compatible SIMD permettent d'obtenir des accélérations constantes et mesurables dans le langage de requête Elasticsearch Query Language (ES|QL).]]></description>
    <content:encoded><![CDATA[<p>Nous avons récemment remplacé des éléments clés de l'implémentation des tables de hachage d'Elasticsearch par une conception de type suisse et constaté des temps de construction et d'itération jusqu'à 2 à 3 fois plus rapides sur des charges de travail uniformes à forte cardinalité. Il en résulte une latence réduite, un meilleur débit et des performances plus prévisibles pour les opérations statistiques et analytiques du langage de requête Elasticsearch (ES|QL).</p><h2>Pourquoi c'est important</h2><p>La plupart des workflows analytiques classiques se résument finalement à regrouper des données. Qu'il s'agisse de calculer la consommation moyenne de données par hôte, de compter les événements par utilisateur ou d'agréger des indicateurs selon différentes dimensions, l'opération de base reste la même : associer des clés à des groupes et mettre à jour les agrégats en cours.</p><p>À petite échelle, presque n'importe quelle table de hachage convenable fonctionne bien. À grande échelle (des centaines de millions de documents et des millions de groupes distincts), les détails commencent à avoir leur importance. Les facteurs de charge, la stratégie de sondage, l'organisation de la mémoire et le comportement du cache peuvent faire la différence entre des performances linéaires et une avalanche d'erreurs de cache.</p><p>Elasticsearch prend en charge ces charges de travail depuis des années, mais nous cherchons constamment à moderniser ses algorithmes de base. C'est pourquoi nous avons évalué une nouvelle approche inspirée des tables suisses et l'avons appliquée au calcul des statistiques par ES|QL.</p><h2>Que sont exactement les tables suisses ?</h2><p>Les tables suisses sont une famille de tables de hachage modernes popularisées par SwissTable de Google, puis adoptées par Abseil et d'autres bibliothèques.</p><p>Les tables de hachage traditionnelles passent beaucoup de temps à rechercher des pointeurs ou à charger des clés pour finalement constater qu'elles ne correspondent pas. La caractéristique principale des tables suisses est leur capacité à rejeter la plupart des requêtes grâce à une structure de tableau en cache de petite taille, stockée séparément des clés et des valeurs et appelée <em>octets de contrôle</em>, ce qui réduit considérablement le trafic mémoire.</p><p>Chaque octet de contrôle représente un emplacement unique et, dans notre cas, encode deux éléments : si l'emplacement est vide et une courte empreinte numérique dérivée du hachage. Ces octets de contrôle sont disposés de manière contiguë en mémoire, généralement par groupes de 16, idéal pour le traitement SIMD (<a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">Single Instruction, Multiple Data</a>).</p><p>Au lieu de sonder un emplacement à la fois, les tables suisses parcourent un bloc entier d'octets de contrôle à l'aide d'instructions vectorielles. En une seule opération, le processeur compare l'empreinte de la clé entrante à 16 emplacements et élimine les entrées vides. Seuls les candidats retenus après ce parcours rapide nécessitent le chargement et la comparaison des clés réelles.</p><p>Cette conception privilégie une meilleure localité du cache et réduit considérablement les chargements aléatoires, au détriment d'une petite quantité de métadonnées supplémentaires. À mesure que la table s'agrandit et que les chaînes de détection s'allongent, ces avantages deviennent de plus en plus précieux.</p><h2>SIMD au centre</h2><p>La vraie star de la série est l'architecture SIMD.</p><p>Les octets de contrôle sont non seulement compacts, mais aussi conçus spécifiquement pour être traités par des instructions vectorielles. Une seule comparaison SIMD peut vérifier simultanément 16 empreintes, transformant ainsi une boucle classique en quelques opérations étendues. Exemple :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f710e87dd749ab3/6a170cc46234e052dadb1a49/bd418778f0c6144f8f5f18419f6220ac0c935c7a-903x407.png" alt="Le SIMD au centre d'Elasticsearch" /><p>En pratique, cela signifie :</p><ul><li><p>Moins de branches.</p></li><li><p>Des chaînes de détection plus courtes.</p></li><li><p>Moins de chargements depuis la mémoire des clés et des valeurs.</p></li><li><p>Une bien meilleure utilisation des unités d'exécution du processeur.</p></li></ul><p>La plupart des recherches ne dépassent jamais l'étape de l'analyse de l'octet de contrôle. Lorsqu'elles y parviennent, le travail restant est ciblé et prévisible. C'est précisément le type de charge de travail pour lequel les processeurs modernes excellent.</p><h2>SIMD sous le capot</h2><p>Pour les lecteurs qui aiment jeter un œil sous le capot, voici ce qui se passe lors de l'insertion d'une nouvelle clé dans la table. Nous utilisons l'API Panama Vector avec des vecteurs de 128 bits, opérant ainsi sur 16 octets de contrôle en parallèle.</p><p>L'extrait suivant montre le code généré sur un processeur Intel Rocket Lake avec AVX-512. Bien que les instructions reflètent cet environnement, la conception ne dépend pas d'AVX-512. Les mêmes opérations vectorielles de haut niveau sont émises sur d'autres plateformes en utilisant des instructions équivalentes (par exemple, AVX2, SSE ou NEON).</p>; Load 16 control bytes from the control block
vmovdqu xmm0, XMMWORD PTR [r9+r10*1+0x10]

; Broadcast the 7-bit fingerprint of the new key across the vector
vpbroadcastb xmm1, r11d

; Compare all 16 control bytes to the new fingerprint
vpcmpeqb k7, xmm0, xmm1
kmovq rbx, k7

; Check if any matches were found
test rbx, rbx
jne &lt;handle_match&gt;<p>Chaque instruction a un rôle clair dans le processus d'insertion :</p><ul><li><p><code>vmovdqu</code>: Charge 16 octets de contrôle consécutifs dans le registre <code>xmm0</code> de 128 bits.</p></li><li><p><code>vpbroadcastb</code>: Réplique l'empreinte 7 bits de la nouvelle clé sur toutes les voies du registre <code>xmm1</code>.</p></li><li><p><code>vpcmpeqb</code>: Compare chaque octet de contrôle à l'empreinte diffusée, produisant un masque de correspondances potentielles.</p></li><li><p><code>kmovq</code> + <code>test</code> : Déplace le masque vers un registre à usage général et vérifie rapidement si une correspondance existe.</p></li></ul><p>Enfin, nous avons opté pour le sondage de groupes de 16 octets de contrôle à la fois, car les tests comparatifs ont montré que l'extension à 32 ou 64 octets avec des registres plus larges n'apportait aucun avantage mesurable en termes de performances.</p><h2>Intégration dans ES|QL</h2><p>L'adoption du hachage suisse dans Elasticsearch n'a pas été une simple solution de remplacement. ES|QL impose des exigences strictes de gestion de la mémoire, de sécurité et d'intégration au reste du moteur de calcul.</p><p>Nous avons étroitement intégré la nouvelle table de hachage à la gestion de la mémoire d'Elasticsearch, notamment au recycleur de pages et à la comptabilisation des coupures, afin de garantir la visibilité et la limitation des allocations. Les agrégations d'Elasticsearch sont stockées de manière dense et indexées par un identifiant de groupe, ce qui optimise la structure de la mémoire et la rapidité d'itération, tout en autorisant certaines optimisations de performance grâce à l'accès aléatoire.</p><p>Pour les clés binaires de longueur variable, nous mettons en cache le hachage complet ainsi que l'identifiant du groupe. Cela évite le recalcul coûteux des codes de hachage lors du sondage et améliore la localité du cache en regroupant les métadonnées associées. Lors du rehachage, nous pouvons nous appuyer sur le hachage et les octets de contrôle mis en cache sans avoir à examiner les valeurs elles-mêmes, ce qui réduit les coûts de redimensionnement.</p><p>Une simplification importante de notre implémentation est que les entrées ne sont jamais supprimées. Cela élimine le besoin de <em>marqueurs</em> (pour identifier les emplacements précédemment occupés) et permet aux emplacements vides de rester véritablement vides, ce qui améliore encore le comportement de sondage et maintient l'efficacité des analyses d'octets de contrôle.</p><p>Le résultat est une conception qui s'intègre naturellement dans le modèle d'exécution d'Elasticsearch tout en préservant les caractéristiques de performance qui rendent les tables suisses attrayantes.</p><h2>Quelles sont ses performances ?</h2><p>Pour les petites cardinalités, les tables suisses offrent des performances globalement équivalentes à celles de l'implémentation existante. Ce résultat est normal : lorsque les tables sont petites, les effets de cache sont moins importants et il y a peu de détections à optimiser.</p><p>À mesure que la cardinalité augmente, la situation change rapidement.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09b13af2fe162f59/6a170cc66f7f04485c9148b8/24900afc47ab07b0e9933f6117b99d0f4613f794-962x599.png" alt="Statistiques ES|QL avec tables de hachage de type suisse" /><p>La carte thermique ci-dessus montre les facteurs d'amélioration du temps de traitement pour différentes tailles de clés (8, 32, 64 et 128 octets) pour des cardinalités allant de 1 000 à 10 000 000 de groupes. À mesure que la cardinalité augmente, le facteur d'amélioration s'accroît régulièrement, jusqu'à 2 à 3 fois pour les distributions uniformes.</p><p>Cette tendance correspond exactement aux prévisions de conception. Une cardinalité plus élevée entraîne des chaînes de détection plus longues dans les tables de hachage traditionnelles, tandis que le mode de sondage de type suisse continue de résoudre la plupart des recherches dans des blocs d'octets de contrôle compatibles SIMD.</p><h2>Le comportement du cache raconte l'histoire</h2><p>Pour mieux comprendre les gains de vitesse, nous avons exécuté le même JMH <a href="https://github.com/elastic/elasticsearch/pull/139343/files#diff-d0e0cc91a7495bf36b2d44eacce95f5185d01879e5f6c38089ac7a89aad17da7"><code>benchmarks</code></a> sous Linux <code>perf</code> et capturé les statistiques du cache et du TLB.</p><p>Comparée à l'implémentation originale, la version suisse effectue environ 60 % d'accès au cache en moins. Les chargements du cache de dernier niveau sont 4 fois moins nombreux, et les défauts de chargement du cache LLC 6 fois moins. Étant donné que ces défauts se traduisent souvent directement par des accès à la mémoire principale, cette réduction explique à elle seule une grande partie de l'amélioration globale des performances.</p><p>Plus on se rapproche du processeur, moins on observe d'échecs de cache de données L1 et près de 6 fois moins d'échecs de TLB de données, ce qui indique une localité spatiale plus étroite et des schémas d'accès à la mémoire plus prévisibles.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb987a5bd98c0d7eb/6a170cc8a929cf9655ae0a25/6e49b7609fba83e33692cb9834552b6ca7e42a83-998x499.png" alt="Comportement du cache : Statistiques originales vs. ES|QL avec tables de hachage de type suisse" /><p>C'est l'avantage concret des octets de contrôle compatibles SIMD. Au lieu de charger sans cesse des clés et des valeurs depuis des emplacements mémoire dispersés, la plupart des requêtes sont résolues par l'analyse d'une structure compacte résidant dans le cache. Moins de mémoire utilisée signifie moins d'échecs de lecture, et moins d'échecs de lecture signifient des requêtes plus rapides.</p><h2>Conclusion</h2><p>En adoptant une conception de table de hachage de style suisse et en misant fortement sur le sondage compatible SIMD, nous avons obtenu des gains de vitesse de 2 à 3 fois pour les charges de travail statistiques ES|QL à cardinalité élevée, ainsi que des performances plus stables et prévisibles.</p><p>Ce travail met en lumière comment les structures de données modernes optimisées pour le processeur peuvent générer des gains substantiels, même pour des problèmes complexes comme les tables de hachage. Il reste encore beaucoup à explorer, notamment en matière de spécialisation des types primitifs et d'utilisation dans d'autres opérations à forte cardinalité, telles que les jointures. Ces pistes s'inscrivent dans le cadre d'un effort plus vaste et continu de modernisation du fonctionnement interne d'Elasticsearch.</p><p>Si vous êtes intéressé par les détails ou souhaitez suivre le travail, consultez cette <a href="https://github.com/elastic/elasticsearch/pull/139343">pull request</a> et la <a href="https://github.com/elastic/elasticsearch/issues/138799">meta issue</a> qui suit les progrès sur Github.</p><p>Bon hachage !</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-swiss-hash-stats</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-swiss-hash-stats</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Matthew Alp,Nik Everett]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf76dd688c5b737e6/6a170cc9839dfa7fc7dcff40/21036e031070f14faccb2b53b22723de2750c391-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Indexation vectorielle jusqu'à 12 fois plus rapide dans Elasticsearch avec NVIDIA cuVS : accélération GPU : chapitre 2]]></title>
    <description><![CDATA[Découvrez comment Elasticsearch atteint un débit d'indexation près de 12 fois supérieur grâce à l'indexation vectorielle accélérée par GPU et NVIDIA cuVS.]]></description>
    <content:encoded><![CDATA[<p>Plus tôt cette année, Elastic a annoncé la <a href="https://ir.elastic.co/news/news-details/2025/Elastic-Brings-Enterprise-Data-to-NVIDIA-AI-Factories/default.aspx">collaboration</a> avec NVIDIA pour apporter l'accélération GPU à Elasticsearch, en intégrant <a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>—comme détaillé lors d'une <a href="https://www.nvidia.com/en-us/on-demand/session/gtc25-S71286/">session à NVIDIA GTC</a> et dans divers <a href="https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia">blogs</a>. Cet article fait le point sur les efforts de co-ingénierie menés avec l'équipe de recherche vectorielle de NVIDIA.</p><h2>Récapitulatif</h2><p>Tout d'abord, faisons le point sur la situation. Elasticsearch s'est imposé comme une base de données vectorielle puissante, offrant un ensemble complet de fonctionnalités et des performances élevées pour la recherche de similitudes à grande échelle. Avec des capacités telles que la quantification scalaire, la quantification binaire améliorée (<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a>), les opérations vectorielles <a href="https://www.elastic.co/blog/accelerating-vector-search-simd-instructions">SIMD</a> et des algorithmes plus efficaces en termes d'espace disque comme <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>, il offre déjà des options efficaces et flexibles pour gérer les charges de travail vectorielles.</p><p>En intégrant NVIDIA cuVS en tant que module accessible pour les tâches de recherche vectorielle, nous visons à améliorer considérablement les performances et l'efficacité de l'indexation vectorielle afin de mieux prendre en charge les charges de travail vectorielles à grande échelle.</p><h2>Le défi</h2><p>L'un des défis les plus complexes dans la création d'une base de données vectorielle haute performance est la construction de l'index vectoriel, le graphe <a href="https://arxiv.org/abs/1603.09320">HNSW</a>. La construction de l'index est rapidement dominée par des millions, voire des milliards d'opérations arithmétiques, car chaque vecteur est comparé à de nombreux autres. De plus, les opérations liées au cycle de vie des index, telles que la compression et les fusions, peuvent augmenter davantage la charge de calcul globale liée à l'indexation. À mesure que les volumes de données et les intégrations vectorielles associées augmentent de manière exponentielle, les GPU de calcul accéléré, conçus pour le parallélisme massif et les calculs mathématiques à haut débit, sont idéalement positionnés pour gérer ces charges de travail.</p><h2>Installez le plugin Elasticsearch-GPU</h2><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a> est une bibliothèque open source CUDA-X pour la recherche vectorielle accélérée par GPU et le clustering de données, permettant une création rapide d'index et une récupération d'embeddings pour les charges de travail liées à l'IA et aux recommandations.</p><p>Elasticsearch utilise cuVS via <a href="https://mvnrepository.com/artifact/com.nvidia.cuvs/cuvs-java">cuvs-java</a>, une bibliothèque open-source développée par la communauté et gérée par NVIDIA. La bibliothèque cuvs-java est légère et repose sur l'<a href="https://docs.rapids.ai/api/cuvs/nightly/c_api/">API cuVS C</a> en utilisant <a href="https://openjdk.org/projects/panama/">Panama</a> Foreign Function pour exposer les fonctionnalités cuVS d'une manière idiomatique Java, tout en restant moderne et performante.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fd7361099da05a/6a17e920be608670af00477f/5f6daa1eb07f704a6707d9e6b7ccb81d0abaa8c9-566x419.png" alt="Fonctionnement d'Elasticsearch avec NVIDIA cuVS, l'indexation CPU et GPU" /><p>La bibliothèque cuvs-java est intégrée dans un <a href="https://github.com/elastic/elasticsearch/pull/135545">nouveau plug-in Elasticsearch</a> ; par conséquent, l'indexation vectorielle sur le GPU peut être effectuée sur le même node et processus Elasticsearch, sans qu'il soit nécessaire de provisionner du code ou du matériel externe. Lors de la création d'index, si la bibliothèque cuVS est installée et qu'un GPU est présent et configuré, Elasticsearch utilisera le GPU pour accélérer le processus d'indexation vectorielle. Les vecteurs sont transmis au GPU, qui crée un un graphe <a href="https://arxiv.org/abs/2308.15136">CAGRA</a>. Ce graphe est ensuite converti au format HNSW, ce qui le rend immédiatement disponible pour la rechercher vectorielle sur le processeur. Le format final du graphe construit est identique à celui qui serait construit sur le CPU ; cela permet à Elasticsearch d'exploiter les GPU pour une indexation vectorielle à haut débit lorsque le matériel sous-jacent le prend en charge, tout en libérant la puissance du CPU pour d'autres tâches (recherche simultanée, traitement des données, etc.).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt485f55f29d6df5c4/6a17e922be6086dcf3004785/3ea255bd9bfd7983f78143c5eba999d2149d72be-671x356.png" alt="" /><h2>Accélération de la création d'index</h2><p>Dans le cadre de l'intégration de l'accélération GPU dans Elasticsearch, plusieurs améliorations ont été apportées à cuvs-java, en mettant l'accent sur l'efficacité de l'entrée/sortie de données et l'invocation de fonctions. L'une des principales améliorations est l'utilisation de <a href="https://github.com/rapidsai/cuvs/blob/2cf5fa7666d703dccbe655f8214656b0952bb69b/java/cuvs-java/src/main/java/com/nvidia/cuvs/CuVSMatrix.java">cuVSMatrix</a> pour modéliser de manière transparente les vecteurs, qu'ils se trouvent sur le tas Java, hors tas ou dans la mémoire du GPU. Cela permet aux données de se déplacer efficacement entre la mémoire et le GPU, en évitant les copies inutiles de milliards de vecteurs potentiels.</p><p>Grâce à cette abstraction sous-jacente sans copie, le transfert vers la mémoire GPU et la récupération du graphe peuvent être effectués directement. Pendant l'indexation, les vecteurs sont d'abord mis en mémoire tampon sur le tas Java, puis envoyés au GPU pour construire le graphe CAGRA. Le graphe est ensuite récupéré à partir du GPU, converti au format HNSW et persisté sur le disque.</p><p>Au moment de la fusion, les vecteurs sont déjà stockés sur le disque, contournant ainsi entièrement le tas Java. Les fichiers d'index sont mappés en mémoire et les données sont transférées directement dans la mémoire du GPU. La conception s'adapte également facilement à différentes largeurs de bits, telles que float32 ou int8, et s'étend naturellement à d'autres schémas de quantification.</p><h2>Roulement de tambour... alors, comment ça fonctionne ?</h2><p>Avant d'examiner les chiffres, un peu de contexte s'impose. La fusion des segments dans Elasticsearch s'exécute généralement automatiquement en arrière-plan pendant l'indexation, ce qui rend difficile l'évaluation comparative de manière isolée. Pour obtenir des résultats reproductibles, nous avons utilisé la fusion forcée pour déclencher explicitement la fusion des segments dans une expérience contrôlée. Comme la fusion forcée effectue les mêmes opérations de fusion sous-jacentes que la fusion en arrière-plan, ses performances servent d'indicateur utile des améliorations attendues, même si les gains exacts peuvent différer selon les charges de travail d'indexation réelles.</p><p>Passons maintenant aux chiffres.</p><p>Nos premiers résultats de référence sont très prometteurs. Nous avons exécuté une évaluation comparative sur une instance AWS <code>g6.4xlarge</code> avec un stockage NVMe connecté localement. Un seul node d'Elasticsearch a été configuré pour utiliser le nombre optimal par défaut de threads d'indexation (8, soit un pour chaque noyau physique) et pour désactiver la<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/merge"> limitation de fusion</a> (qui est moins applicable avec les disques NVMe rapides).</p><p>Pour l'ensemble de données, nous avons utilisé 2,6 millions de vecteurs avec 1 536 dimensions provenant de l' <a href="https://github.com/elastic/rally-tracks/blob/master/openai_vector/README.md">OpenAI Rally vector track</a>, encodés sous forme <a href="https://github.com/elastic/elasticsearch/pull/137072">de chaînes base64</a> et indexés sous forme de float32 <em>hnsw</em>. Dans tous les scénarios, les graphes créés atteignent des niveaux de rappel allant jusqu'à 95 %. Voici nos conclusions :</p><ul><li><p><strong>Débit d'indexation :</strong> en transférant la construction des graphiques vers le GPU pendant les vidages de mémoire tampon, nous multiplions le débit par environ 12.</p></li><li><p><strong>Fusion forcée :</strong> une fois l'indexation terminée, le GPU continue d'accélérer la fusion des segments, multipliant par environ 7 la vitesse de la phase de fusion forcée.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfea4ee13b5a3b10d/6a17e923e9ea879c6aa9c616/f60ea9ee5996e456f393ffd195ee7eada6e5a7c2-948x387.png" alt="" /><ul><li><p><strong>Utilisation du processeur :</strong> le transfert de la construction du graphe vers le GPU réduit considérablement l'utilisation moyenne et maximale du processeur. Les graphiques ci-dessous illustrent l'utilisation du CPU pendant l'indexation et la fusion, et mettent en évidence à quel point elle est plus faible lorsque ces opérations sont exécutées sur le GPU. La réduction de l'utilisation du CPU pendant l'indexation GPU permet de libérer des cycles CPU qui peuvent être réaffectés à l'amélioration des performances de recherche.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80ff9c53f9b6884a/6a17e925445de9ee4b4d0187/5e680a5fc41700a877f3d8b2e5ce18ebd3f37a0b-1600x562.png" alt="" /><ul><li><p><strong>Rappel :</strong> la précision reste pratiquement identique entre les exécutions CPU et GPU, le graphique généré par le GPU affichant un rappel légèrement supérieur.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5cbe084eca27b8e4/6a17e926faa913317093c8b7/48a2b7758606bd321712b7d8378cd2640e652a4e-1384x544.png" alt="" /><h2>Comparaison selon un autre critère : le prix</h2><p>La comparaison précédente utilisait intentionnellement un matériel identique, la seule différence étant l'utilisation ou non du GPU lors de l'indexation. Cette configuration est utile pour isoler les effets du calcul brut, mais nous pouvons également examiner la comparaison du point de vue des coûts.</p><p>Pour un prix horaire à peu près équivalent à celui de la configuration accélérée par GPU, il est possible de provisionner une configuration uniquement sur processeur avec environ deux fois plus de ressources CPU et mémoire comparables : 32 vCPU (AMD EPYC) et 64 Go de RAM, permettant de doubler le nombre de threads d’indexation à 16.</p><p>Afin de garantir l'équité et la cohérence de la comparaison, nous avons réalisé cette expérience uniquement sur processeur sur une instance AWS g6.8xlarge, avec le GPU explicitement désactivé. Cela nous a permis de maintenir toutes les autres caractéristiques matérielles constantes tout en évaluant le compromis coût-performance entre l'accélération GPU et l'indexation uniquement sur processeur.</p><p>L'instance de processeur plus puissante montre effectivement une performance améliorée par rapport aux benchmarks de la section ci-dessus, comme on pouvait s'y attendre. Cependant, lorsque nous comparons cette instance de processeur plus puissante aux résultats originaux accélérés par GPU, le GPU offre toujours des gains de performance substantiels : <strong>~5x</strong> d'amélioration dans le débit d'indexation, et <strong>~6x </strong>dans la fusion forcée, tout en construisant des graphes qui atteignent des niveaux de rappel allant jusqu'à <strong>95%.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94b5eb6f95ba307d/6a17e928abe0f255d4dfea35/8ffa58cae3ad175ef2932a351aeef4c34a1407b9-948x394.png" alt="" /><h2>Conclusion</h2><p>Dans les scénarios de bout en bout, l'accélération GPU avec NVIDIA cuVS permet d'améliorer de près de 12 fois le débit d'indexation et de réduire de 7 fois la latence de fusion forcée, tout en diminuant considérablement l'utilisation du processeur. Cela démontre que l'indexation vectorielle et les charges de travail de fusion bénéficient considérablement de l'accélération GPU. Sur une comparaison ajustée en fonction des coûts, l'accélération GPU continue d'offrir des gains de performances substantiels, avec un débit d'indexation environ 5 fois supérieur et des opérations de fusion forcée 6 fois plus rapides.</p><p>L'indexation vectorielle accélérée par GPU est actuellement prévue pour la préversion technique dans Elasticsearch 9.3, dont la sortie est prévue début 2026.</p><p>Plus d'informations à venir.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</guid>
    <category><![CDATA[Base vectorielle]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik,Corey Nolet,Manas Singh,Mithun Radhakrishnan,Mayya Sharipova,Lorenzo Dematte,Ben Frederickson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1248d51633bd75d9/6a17e92ae9ea8714b3a9c61a/08f7469a4daaf67b7c5999585aae179b6680c78d-896x746.png" length="0" type="image/png"/>
    <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Exploration de la recherche vectorielle accélérée par le GPU dans Elasticsearch avec NVIDIA : Chapitre I]]></title>
    <description><![CDATA[Basée sur NVIDIA cuVS, cette collaboration vise à fournir aux développeurs une accélération GPU pour la recherche vectorielle dans Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Depuis un certain temps, l'équipe d'Elastic Engineering s'efforce d'optimiser les performances des bases de données vectorielles. Notre mission : faire de Lucene et Elasticsearch la meilleure base de données vectorielle. Grâce à l'accélération matérielle des <a href="https://www.elastic.co/fr/blog/accelerating-vector-search-simd-instructions">instructions SIMD du processeur</a>, à l'introduction de nouvelles innovations en matière de compression de données vectorielles<a href="https://www.elastic.co/fr/search-labs/blog/better-binary-quantization-lucene-elasticsearch">(Better Binary Quantization ou BBQ</a>), puis au dépassement des attentes en mettant à jour l'approche algorithmique de BBQ pour en tirer encore plus d'avantages, et en <a href="https://www.elastic.co/fr/search-labs/blog/filtered-hnsw-knn-search">rendant Filtered HNSW plus rapide.</a> Vous avez compris : nous construisons un système plus rapide, plus performant et plus efficace. base de données vectorielles pour les développeurs qui résolvent ces problèmes de RAG-gedy !</p><p>Dans le cadre de notre mission visant à ne négliger aucune efficacité, nous explorons les possibilités d'accélération avec ces curieuses puces informatiques, dont vous avez peut-être entendu parler - les GPU NVIDIA ! (Sérieusement, vous n'en avez pas entendu parler ?).</p><p>Lorsque nous sommes obsédés par les performances, nous devons explorer plusieurs espaces de problèmes : comment indexer une quantité exponentielle de données, comment en extraire des informations et comment le faire lorsque vos modèles ML sont impliqués. Vous devriez être en mesure d'exploiter tous les avantages disponibles lorsque vous disposez de GPU.</p><p>Dans ce billet, nous nous penchons sur notre collaboration avec l'équipe de recherche vectorielle de NVIDIA pour explorer la recherche vectorielle accélérée par le GPU dans Elasticsearch. Ce travail ouvre la voie à des cas d'utilisation où les développeurs pourraient utiliser un mélange de GPU et de CPU pour des applications réelles basées sur Elasticsearch. Une période passionnante !</p><h2>Elasticsearch GPUs</h2><p>Nous sommes ravis d'annoncer que l'équipe d'ingénieurs d'Elasticsearch participe à l'élaboration de l'API Java cuVS open-source pour les développeurs, qui expose des bindings pour les algorithmes de recherche vectorielle. Ce travail s'appuie sur notre expérience antérieure en matière de FFI au Panama. Elasticsearch et Apache Lucene utilisent l'API NVIDIA cuVS pour construire le graphe pendant l'indexation. D'accord, nous faisons un bond en avant ; revenons un peu en arrière.</p><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>, une bibliothèque C++ open-source, est au cœur de cette collaboration. Il vise à apporter l'accélération GPU à la recherche vectorielle en fournissant un débit plus élevé, une latence plus faible et des temps de construction d'index plus rapides. Mais Elasticsearch et Apache Lucene sont écrits en Java ; comment cela fonctionnera-t-il ?</p><p>C'est là qu'interviennent <a href="https://github.com/SearchScale/lucene-cuvs">lucene-cuvs</a> et la collaboration Elastic-NVIDIA-SearchScale pour l'intégrer à l'écosystème Lucene afin d'explorer la recherche vectorielle accélérée par le GPU dans Elasticsearch. Dans la récente version 25.02 de NVIDIA cuVS, nous avons ajouté une API Java pour cuVS. La nouvelle API est expérimentale et continuera d'évoluer, mais elle est actuellement disponible. La question peut se poser : les appels de fonctions Java à des fonctions natives ne sont-ils pas lents ? Plus maintenant ! Nous utilisons la nouvelle <a href="https://openjdk.org/projects/panama/">interface Panama FFI</a> (Foreign Function Interface) pour les liaisons, ce qui réduit au minimum les frais généraux pour Java par rapport aux appels descendants natifs.</p><p>Nous utilisons <a href="https://www.elastic.co/fr/search-labs/blog/lucene-and-java-moving-forward-together">Panama FFI dans Elasticsearch et Lucene</a> depuis un certain temps déjà. C'est génial ! Mais... il y a toujours un "mais", n'est-ce pas ? FFI est confronté à des problèmes de disponibilité entre les différentes versions de Java. Nous avons surmonté ce problème en compilant l'API cuVS en Java 21 et en encapsulant l'implémentation dans un jar multi-versions ciblant Java 22. Cela permet d'utiliser cuVS Java directement dans Lucene et Elasticsearch.</p><p>Ok, maintenant que nous avons l'API Java cuVS, de quoi d'autre avons-nous besoin ?</p><h2>Deux algorithmes pour l'unité centrale</h2><p>Elasticsearch prend en charge l'<a href="https://arxiv.org/abs/1603.09320">algorithme HNSW</a> pour une recherche KNN approximative évolutive. Cependant, pour tirer le meilleur parti du GPU, nous utilisons un algorithme différent, <a href="https://arxiv.org/pdf/2308.15136">CAGRA [</a><a href="https://arxiv.org/pdf/2308.15136"></a><a href="https://arxiv.org/pdf/2308.15136"><em>CUDA</em></a> <a href="https://arxiv.org/pdf/2308.15136"></a><a href="https://arxiv.org/pdf/2308.15136"><em>ANN</em></a> <a href="https://arxiv.org/pdf/2308.15136"></a><a href="https://arxiv.org/pdf/2308.15136"></a><a href="https://arxiv.org/pdf/2308.15136"></a>GRAph], qui a été spécialement conçu pour les niveaux élevés de parallélisme offerts par le GPU.</p><p>Avant de voir comment nous envisageons d'ajouter la prise en charge de CAGRA, examinons comment Elasticsearch et Lucene accèdent aux données d'index par le biais d'un "format de codec". Il s'agit de</p><ol><li><p>la représentation sur disque,</p></li><li><p>les interfaces de lecture et d'écriture des données,</p></li><li><p>et les mécanismes permettant de gérer l'architecture à base de segments de Lucene.</p></li></ol><p>Nous mettons en œuvre un nouveau <a href="https://lucene.apache.org/core/10_1_0/core/org/apache/lucene/codecs/KnnVectorsFormat.html">format de vecteur</a> KNN (k-nearest neighbors) qui utilise en interne l'API Java cuVS pour l'indexation et la recherche sur le GPU. À partir de là, nous "plongeons" ce type de codec dans les correspondances d'Elasticsearch avec un type de champ dans l'index. Par conséquent, vos requêtes KNN existantes continuent de fonctionner, que l'index de référence utilise un graphique CAGRA ou HNSW. Bien entendu, cela ne tient pas compte de nombreux détails, que nous prévoyons d'aborder dans un prochain blog. Voici l'architecture de haut niveau d'un Elasticsearch accéléré par le GPU.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6197b631a34f8b6/6a170b0da6c2b9e60ce7970b/be6b7356c03df4dee7230625c2c9af3b019f93be-756x510.png" alt="" /><p>Ce nouveau format de codec est par défaut CAGRA. Cependant, il permet également de convertir un graphique CAGRA en un graphique HNSW pour une recherche sur l'unité centrale.</p><h2>Indexation et recherche sur le GPU : Prendre quelques décisions "fondamentales</h2><p>Avec l'<a href="https://www.elastic.co/fr/search-labs/blog/stateless-your-new-state-of-find-with-elasticsearch">architecture</a> sans état d'Elasticsearch Serverless, qui sépare l'indexation et la recherche, les responsabilités sont désormais clairement délimitées. Nous choisissons le meilleur profil de matériel pour remplir chacune de ces responsabilités indépendantes.</p><p>Nous pensons que les utilisateurs envisageront deux stratégies de déploiement principales :</p><ol><li><p>Indexation et recherche sur le GPU : Pendant l'indexation, construire un graphe CAGRA et l'utiliser pendant la recherche - idéal lorsqu'une recherche à très faible latence est requise.</p></li><li><p>Indexation sur GPU et recherche sur CPU : Pendant l'indexation, construire un graphe CAGRA et le convertir en graphe HNSW. Le graphique HNSW est stocké dans l'index, qui peut ensuite être utilisé par l'unité centrale pour effectuer des recherches.</p></li></ol><p>Cette flexibilité permet de proposer différents modèles de déploiement, offrant des compromis entre le coût et la performance. Par exemple, un service d'indexation pourrait utiliser le GPU pour construire et fusionner efficacement des graphes en temps voulu, tout en utilisant un CPU moins puissant pour la recherche.</p><h2>Voici donc le plan pour la recherche vectorielle accélérée par le GPU dans Elasticsearch</h2><p>Nous sommes impatients d'apporter aux utilisateurs des gains de performance et de la flexibilité dans les stratégies de déploiement, en proposant différents boutons pour équilibrer le coût et la performance. <a href="https://www.nvidia.com/gtc/session-catalog/?tab.catalogallsessionstab=16566177511100015Kus&amp;search=Lucene#/">Voici la session de la NVIDIA GTC 2025</a> où ce travail a été présenté en détail.</p><p>Nous tenons à remercier les équipes d'ingénieurs de NVIDIA et de SearchScale pour leur fantastique collaboration. Dans un prochain blog, nous examinerons plus en détail les détails de la mise en œuvre et l'analyse des performances. Accrochez-vous à vos chapeaux de curiosité 🎩!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia</guid>
    <category><![CDATA[Base vectorielle]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt298e839e708ca11c/6a170b0fb339d560c2769fc2/38bc0377a6adce7eae0099f61902fdbbe644eb4a-1440x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Lucene Wrapped 2024]]></title>
    <description><![CDATA[2024 a été une autre année importante pour Apache Lucene. Dans ce blog, nous examinerons les points essentiels.]]></description>
    <content:encoded><![CDATA[<p>Apache Lucene a connu une activité importante en 2024, avec de nombreuses versions, dont la première mise à jour majeure depuis trois ans, riche en améliorations et en nouvelles fonctionnalités. Examinons quelques-uns de ses principaux points forts.</p><h2>Lucene &amp; la communauté</h2><p>La force d'un projet dépend de la communauté qui le soutient. Malgré plus de 20 ans de développement, le projet Lucene reste dynamique et prospère grâce à ses contributeurs passionnés et actifs.</p><p>En 2024, le projet Lucene a fait l'objet de plus de 2 000 modifications de la part de 98 contributeurs uniques, et de près de 800 demandes de modification. Le nombre de contributeurs continue de croître, avec de nouveaux committers et membres du PMC qui rejoignent le projet et contribuent à son succès.</p><h2>Lucene 10</h2><p>2024 a vu la première version majeure depuis près de 3 ans - Lucene 10, avec plus de 2 000 commits de 185 contributeurs uniques. Si le modèle de développement suivi par Lucene permet d'apporter de nombreuses améliorations et fonctionnalités dans des versions mineures, une version majeure offre la possibilité d'apporter des fonctionnalités plus importantes et des modernisations. Par exemple, Lucene 10 nécessite au minimum Java 21. L'augmentation de la version minimale de Java permet à Lucene de continuer à bénéficier des améliorations apportées par la version moderne de Java.</p><p>L'objectif principal de Lucene 10 est de mieux utiliser le matériel sur lequel il fonctionne. Jetons un coup d'œil rapide sur les principaux faits marquants :</p><ul><li><p><strong>Plus de parallélisme</strong> dans les recherches - alors que l'exécution des recherches est déjà parallélisée entre les segments, nous allons maintenant plus loin, en parallélisant à l'intérieur des segments. Cela permet de dissocier la représentation sur disque des performances d'exécution, ce qui permet même à des segments uniques de bénéficier du nombre de cœurs des systèmes modernes.</p></li><li><p><strong>Meilleur parallélisme des E/S</strong> - le modèle d'E/S synchrone simple utilisé par Lucene a été amélioré grâce à une étape de préfixation. Cela permet d'informer le système d'exploitation qu'une région d'un fichier d'index sera nécessaire dans un avenir très proche, sans pour autant bloquer le thread appelant.</p></li><li><p><strong>Meilleure efficacité de l'unité centrale et du stockage grâce à l'indexation épar</strong> se - Lucene 10 introduit la prise en charge de l'indexation éparse, parfois appelée indexation par clé primaire ou indexation par zone dans d'autres magasins de données.</p></li></ul><p>Pour plus d'informations sur Lucene 10, consultez l'<a href="https://www.elastic.co/search-labs/blog/apache-lucene-10-release-highlights">article</a> dédié à Lucene 10.</p><h2>Recherche et innovation dans le domaine de Lucene</h2><p>En 2024, Lucene a connu un essor de la recherche et de l'innovation, en particulier dans les domaines de l'intégration de l'apprentissage automatique, de la recherche vectorielle et de l'optimisation pour les ensembles de données à grande échelle, avec des références à 10 <a href="https://scholar.google.com/scholar?as_ylo=2024&amp;q=lucene&amp;hl=en&amp;as_sdt=0,5">articles et publications de recherche</a> distincts. Voici quelques-uns des principaux domaines de recherche et développements :</p><ul><li><p><strong>Recherche vectorielle et prise en charge de l'intégration</strong> - Lucene offre une solution puissante et évolutive pour la recherche vectorielle, permettant la recherche sémantique à grande échelle. En tirant parti de la solide infrastructure d'indexation et de recherche de Lucene, les utilisateurs peuvent combiner le meilleur de la recherche textuelle traditionnelle avec les capacités avancées de la recherche vectorielle moderne, ce qui fait de Lucene une solution complète pour un large éventail de tâches de recherche et d'extraction d'informations.</p></li><li><p><strong>Modèles de recherche hybrides</strong> - La recherche s'est également penchée sur les techniques de recherche hybrides, Lucene combinant la recherche traditionnelle par mot-clé et la recherche moderne par vecteur. En fusionnant des index basés sur des termes avec des représentations vectorielles denses, Lucene peut fournir des résultats de recherche plus précis et plus pertinents sur le plan contextuel, comblant ainsi le fossé entre la précision des moteurs de recherche traditionnels et la flexibilité de la recherche sémantique.</p></li></ul><p>Les efforts de recherche en cours en 2024 démontrent la capacité d'adaptation de Lucene à l'évolution des besoins des technologies de recherche modernes, en particulier dans le contexte de l'IA, de la recherche sémantique et des applications de big data. Le projet continue de se développer en tant que plateforme puissante, flexible et efficace pour les cas d'utilisation de la recherche traditionnelle et de pointe.</p><h2>2024 versions de Lucene</h2><p>Bien qu'il ne s'agisse pas d'un reflet exact, le simple volume des publications met en évidence le dévouement et l'énergie constants de la communauté. Ces mises à jour comprennent des améliorations majeures des performances et de l'efficacité de la recherche vectorielle, la prise en charge de madvise, des optimisations pour le décodage des listes d'écritures, des améliorations supplémentaires de la vitesse grâce à SIMD, et bien d'autres choses encore.</p><p>Voici la liste complète des sorties :</p><ul><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-1010-available">10.1.0</a> (2024-12-20)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-9121-available">9.12.1</a> (2024-12-13)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-1000-available">10.0.0</a> (2024-10-14)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-9120-available">9.12.0</a> (2024-09-28)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-8114-available">8.11.4</a> (2024-09-24)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-9111-available">9.11.1</a> (2024-06-27)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-9110-available">9.11.0</a> (2024-06-06)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-9100-available">9.10.0</a> (2024-02-20)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-8113-available">8.11.3</a> (2024-02-08)</p></li><li><p><a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-992-available">9.9.2</a> (2024-01-29)</p></li></ul><p>Vous pouvez trouver plus d'informations et les notes de version sur la page <a href="https://projects.apache.org/project.html?lucene-core">Lucene Core</a>. En outre, il existe des versions équivalentes de <a href="https://projects.apache.org/project.html?lucene-pylucene">PyLucene</a>.</p><h2>Conclusion</h2><p>Alors que Lucene arrive à maturité, il continue de prospérer grâce à sa communauté dévouée et dynamique. Comme nous l'avons vu, 2024 a été une année incroyablement productive, et nous nous tournons maintenant vers les développements passionnants que 2025 apportera.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/apache-lucene-wrapped-2024</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/apache-lucene-wrapped-2024</guid>
    <category><![CDATA[Lucene]]></category>
    <dc:creator><![CDATA[Chris Hegarty]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt901211870015335c/6a17ddf6a292998b08d02b93/29a02c89b3c5adb37a5f900de634ff09cb63fdd9-1792x1024.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 03 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>