Comprendre les filtres de Bloom par l’exemple
(llimllib.github.io)- Un filtre de Bloom est une structure de données qui permet de filtrer rapidement l’appartenance à un grand ensemble avec peu de mémoire, en distinguant uniquement « certainement absent » et « peut-être présent »
- Son principe repose sur un vecteur de bits et plusieurs fonctions de hachage ; lors de l’insertion, les bits aux positions indiquées par les résultats de hachage sont mis à 1
- À la consultation, si l’une de ces mêmes positions vaut 0, l’élément peut être exclu ; mais même si elles valent toutes 1, la possibilité d’un faux positif demeure
- Les fonctions de hachage doivent être indépendantes, proches d’une distribution uniforme et rapides ; un cas rapporte un gain de vitesse d’environ 800 % en passant de md5 à murmur
- La précision et le coût du filtre dépendent de l’équilibre entre le nombre attendu d’éléments n, le nombre de bits m et le nombre de hachages k ; l’insertion comme la recherche sont toutes deux en O(k)
Fonctionnement d’un filtre de Bloom
- Un filtre de Bloom est une structure de données probabiliste qui détermine rapidement et efficacement en mémoire si un élément appartient à un ensemble
- Le résultat se limite à deux possibilités
- l’élément est certainement absent de l’ensemble
- l’élément est peut-être présent dans l’ensemble
- Sa structure interne est un vecteur de bits ; lorsqu’on ajoute un élément, l’entrée est passée dans plusieurs fonctions de hachage
- L’insertion se termine en mettant à 1 les indices de bits pointés par chaque valeur de hachage
- Dans l’exemple, Fnv et Murmur sont utilisés comme fonctions de hachage simples
Vérification d’appartenance et faux positifs
- La recherche utilise les mêmes fonctions de hachage que lors de l’insertion
- Si l’un des bits pointés par les valeurs de hachage vaut 0, l’élément est certainement absent de l’ensemble
- Si tous les bits concernés valent 1, l’élément est peut-être présent
- Ces mêmes bits peuvent déjà avoir été positionnés par un autre élément, ou par une combinaison de plusieurs éléments
- À cause de ces collisions, un filtre de Bloom peut produire des faux positifs (false positives)
Critères de choix des fonctions de hachage
- Les fonctions de hachage d’un filtre de Bloom doivent être indépendantes, proches d’une distribution uniforme et aussi rapides que possible
- Les hachages cryptographiques comme sha1 sont très répandus, mais ne constituent pas toujours un bon choix pour un filtre de Bloom
- Voici des exemples de hachages rapides et simples
- Il existe un cas où le passage de md5 à murmur dans une implémentation de filtre de Bloom a permis d’obtenir environ 800 % de gain de vitesse
Hachages utilisés dans les implémentations réelles
- Différentes implémentations utilisent différentes fonctions de hachage pour les filtres de Bloom
- Chromium : utilise murmur
- Plan9 : utilise le hachage simple proposé dans Mitzenmacher 2005
- Sdroege Bloom filter : utilise fnv1a
- Squid : utilise MD5
- RedisBloom : utilise murmur
- Apache Spark : utilise murmur
- influxdb : utilise xxhash
- bloomd : les deux premiers hachages sont murmur, les deux suivants SpookyHash, puis les hachages suivants utilisent une combinaison des deux
- fleur, flor, bloom : utilisent fnv
- Sqlite : ajoute un filtre de Bloom pour les requêtes analytiques
- RocksDB : configurable ; le code source indique que xxh3, de la famille xxhash, donnait les meilleurs résultats
- ScyllaDB : utilise murmur
Déterminer la taille du filtre et le nombre de fonctions de hachage
- Un filtre de Bloom permet d’ajuster le taux de faux positifs
- Un filtre plus grand réduit les faux positifs
- Un filtre plus petit augmente les faux positifs
- Le taux de faux positifs se calcule approximativement avec
(1-e^-kn/m)^k- n : nombre d’éléments que l’on prévoit d’insérer
- m : nombre de bits du filtre
- k : nombre de fonctions de hachage
- Plus il y a de fonctions de hachage, plus la recherche et l’insertion sont lentes, et plus le filtre se remplit rapidement
- À l’inverse, s’il y a trop peu de fonctions de hachage, les faux positifs peuvent devenir excessifs
- Pour des valeurs données de m et n, la valeur optimale de k peut être choisie avec
(m/n)ln(2) - La taille du filtre peut être ajustée dans l’ordre suivant
- Estimer grossièrement la valeur attendue de n
- Choisir une valeur de m
- Calculer la valeur optimale de k
- Calculer le taux d’erreur avec les valeurs choisies de n, m et k
- Si le taux d’erreur est difficile à accepter, modifier m et recalculer
Performances et conditions d’utilisation adaptées
- Dans un filtre de Bloom avec m bits et k fonctions de hachage, l’insertion comme la vérification d’appartenance sont en O(k)
- Lorsqu’on ajoute ou recherche un élément, il suffit de le passer dans k fonctions de hachage, puis de positionner ou vérifier les bits correspondants
- L’efficacité en espace dépend du taux d’erreur acceptable
- Si l’ensemble des éléments pouvant être insérés est très limité, un vecteur de bits déterministe peut être préférable
- S’il est impossible d’estimer, même approximativement, le nombre d’éléments qui seront insérés, une table de hachage ou un scalable Bloom filter peut être plus adapté
Références et exemples d’usage
- Des exemples d’utilisation des filtres de Bloom sont disponibles dans les exemples de Bloom filter sur Wikipedia
- La présentation de C. Titus Brown traite de l’usage des filtres de Bloom en bio-informatique
- Principales références
1 commentaires
Avis sur Hacker News
Cet article vise exactement les gens comme moi. J’avais déjà entendu le nom filtre de Bloom, et chaque fois qu’il était mentionné je me disais qu’il faudrait que je me renseigne ; en lisant cet article, je l’ai enfin fait, et c’était l’introduction parfaite que je cherchais :)
En 2009, à l’université, j’ai créé un filtre de Bloom en CUDA, et mon directeur de recherche était un ancien de Nvidia. Mais ensuite, dans ma carrière, je n’ai plus jamais fait de programmation GPU
Si j’avais fait d’autres choix à l’époque, j’aurais peut-être pu gagner 100 millions de dollars
Puis je suis parti faire autre chose, et j’ai raté le gros lot
Il y a 10 ans, j’ai créé une implémentation de hashcash sur GPU, mais aujourd’hui ça n’a probablement presque aucune valeur
Note à l’auteur : la partie interactive est vraiment excellente. Pour rendre le point central encore plus évident, ce serait bien de donner un exemple de deux chaînes qui provoquent une collision de hachage, d’en faire saisir une dans le premier champ, puis de vérifier l’autre dans le second
Cela montrerait pourquoi la réponse est toujours « peut appartenir à l’ensemble », et non « appartient »
"bloom"et"demonstrators "entrent en collision. Attention à l’espace à la fin de la seconde chaîneLes deux entrent en collision avec
fnv: 7,murmur: 12J’ai une astuce que j’aime bien. Quand on doit faire beaucoup de tests d’appartenance sur un ensemble qui peut être petit, on peut ajouter de façon spéculative un filtre de Bloom 64 bits avec une fonction de hachage très simple
Ça a l’air complètement idiot, mais le coût est tellement faible que ça vaut le pari. Même si ça ne marche pas bien, ça ajoute environ 10 ns aux insertions et aux tests d’appartenance ; si ça marche, en revanche, ça peut éviter une quantité énorme de travail
Par exemple pour
querySelector()dans certains cas, pour préfiltrer des recherches par hachage dans des buckets CSS, ou pour rejeter rapidement des éléments lorsqu’on cherche certains attributs Aria pour l’accessibilité. C’est surprenant que des filtres minuscules de 32 ou 64 bits fonctionnent, mais en pratique ils sont souvent efficaces. Il existe aussi quelques filtres de Bloom plus grands. J’en ai ajouté plusieursJ’ai demandé à ChatGPT de m’en faire un en Python, et il a utilisé la méthode consistant à découper le digest md5 de base pour l’utiliser comme plusieurs hachages. Pour des usages peu critiques, ça me semble acceptable
On peut voir une autre visualisation des filtres de Bloom à la fin de cette page :
https://www.chrislaux.com/hashtable.html
Je pensais l’avoir déjà vue, mais en réalité c’était cette autre page : https://bdupras.github.io/filter-tutorial/
Cette page ajoute un peu plus d’informations en comparant les filtres de Bloom et les filtres cuckoo
Récemment, j’ai implémenté une fonctionnalité de prévention du spam dans les messages de log avec un filtre de Bloom. Dans le logger, on hachait le message et on l’ajoutait au filtre ; si l’élément y était déjà, on n’affichait pas le message
Toutes les quelques secondes, on parcourait le filtre pour effacer tous les bits, et ça convenait bien parce qu’il n’était pas nécessaire de se soucier d’effacer atomiquement tous les bits du filtre. Même si seulement une partie des bits était effacée pendant l’arrivée d’un message, cela suffisait pour qu’il soit de nouveau journalisé. L’ancienne implémentation comptait le nombre de messages vus jusqu’à saturation à N, ce qui avait pour effet que lorsqu’un message précis se répétait, il n’apparaissait plus qu’à un rythme inférieur à celui de réinitialisation du filtre
C’était assez satisfaisant de connaître les filtres de Bloom depuis longtemps, puis de leur trouver naturellement un vrai cas d’usage qui apportait une nette amélioration
Pour aller plus loin, l’article d’Eli Bendersky sur les filtres de Bloom était également très bon :
https://eli.thegreenplace.net/2025/bloom-filters/
Les concepts nécessaires pour comprendre les filtres de Bloom, les ensembles et les tables de hachage se recoupent, à mon avis, à environ 95 %. Un ensemble est une table de hachage pour tests d’appartenance qui ne s’intéresse qu’aux clés, pas aux valeurs ; un filtre de Bloom est un ensemble qui exploite le fait que le hachage plusieurs-vers-un « compresse » l’espace des clés via les collisions
En quelque sorte, on utilise volontairement des fonctions de hachage qui provoquent beaucoup de collisions. Si une clé donnée a été hachée au moins une fois, le résultat sera forcément positif, mais une autre clé ayant produit le même hachage peut aussi exister. Ce n’est pas un bug, c’est une fonctionnalité
Cela réduit la probabilité de faux positifs dus aux collisions, tout en conservant la garantie d’absence de faux négatifs