Современный, высокопроизводительный фильтр bloom в Python?
Я ищу реализацию фильтра цветения качества продукции в Python для обработки довольно большого количества элементов (скажем, от 100M до 1B с 0,01% ложноположительной скоростью).
Pybloom является одним из вариантов, но, похоже, показывает свой возраст, поскольку он регулярно выдает ошибки предупреждения об устаревании на Python 2.5. Джо Грегорио также имеет реализация.
требования - быстрая производительность и стабильность поиска. Я также открыт для создание интерфейсов Python для особенно хороших реализаций c/C++ или даже для Jython, если есть хорошая реализация Java.
отсутствие этого, любые рекомендации по представлению битового массива / битового вектора, которые могут обрабатывать ~16E9 бит?
6 ответов
недавно я тоже пошел по этому пути; хотя похоже, что мое приложение немного отличается. Мне было интересно аппроксимировать операции множества на большом количестве строк.
вы делаете ключевое замечание, что a быстро требуется битовый вектор. В зависимости от того, что вы хотите поместить в фильтр bloom, вам также может потребоваться подумать о скорости используемого алгоритма(ов) хэширования. Вы можете найти это библиотека полезное. Вы также можете хотите возиться с методом случайных чисел, используемым ниже, который только хэширует ваш ключ один раз.
С точки зрения реализаций битового массива не Java:
- Boost имеет dynamic_bitset
- Java имеет встроенный BitSet
Я построил свой фильтр цветения, используя BitVector. Я провел некоторое время, профилируя и оптимизируя библиотеку и внося свои исправления в Avi. Идти к тем BitVector ссылка и прокрутите вниз до подтверждения в v1.5 чтобы увидеть детали. В конце концов, я понял, что производительность не является целью этого проекта и решил не использовать его.
вот какой-то код, который у меня был. Я могу поместить это в код google на python-bloom. Предложения приветствуются.
from BitVector import BitVector
from random import Random
# get hashes from http://www.partow.net/programming/hashfunctions/index.html
from hashes import RSHash, JSHash, PJWHash, ELFHash, DJBHash
#
# ryan.a.cox@gmail.com / www.asciiarmor.com
#
# copyright (c) 2008, ryan cox
# all rights reserved
# BSD license: http://www.opensource.org/licenses/bsd-license.php
#
class BloomFilter(object):
def __init__(self, n=None, m=None, k=None, p=None, bits=None ):
self.m = m
if k > 4 or k < 1:
raise Exception('Must specify value of k between 1 and 4')
self.k = k
if bits:
self.bits = bits
else:
self.bits = BitVector( size=m )
self.rand = Random()
self.hashes = []
self.hashes.append(RSHash)
self.hashes.append(JSHash)
self.hashes.append(PJWHash)
self.hashes.append(DJBHash)
# switch between hashing techniques
self._indexes = self._rand_indexes
#self._indexes = self._hash_indexes
def __contains__(self, key):
for i in self._indexes(key):
if not self.bits[i]:
return False
return True
def add(self, key):
dupe = True
bits = []
for i in self._indexes(key):
if dupe and not self.bits[i]:
dupe = False
self.bits[i] = 1
bits.append(i)
return dupe
def __and__(self, filter):
if (self.k != filter.k) or (self.m != filter.m):
raise Exception('Must use bloom filters created with equal k / m paramters for bitwise AND')
return BloomFilter(m=self.m,k=self.k,bits=(self.bits & filter.bits))
def __or__(self, filter):
if (self.k != filter.k) or (self.m != filter.m):
raise Exception('Must use bloom filters created with equal k / m paramters for bitwise OR')
return BloomFilter(m=self.m,k=self.k,bits=(self.bits | filter.bits))
def _hash_indexes(self,key):
ret = []
for i in range(self.k):
ret.append(self.hashes[i](key) % self.m)
return ret
def _rand_indexes(self,key):
self.rand.seed(hash(key))
ret = []
for i in range(self.k):
ret.append(self.rand.randint(0,self.m-1))
return ret
if __name__ == '__main__':
e = BloomFilter(m=100, k=4)
e.add('one')
e.add('two')
e.add('three')
e.add('four')
e.add('five')
f = BloomFilter(m=100, k=4)
f.add('three')
f.add('four')
f.add('five')
f.add('six')
f.add('seven')
f.add('eight')
f.add('nine')
f.add("ten")
# test check for dupe on add
assert not f.add('eleven')
assert f.add('eleven')
# test membership operations
assert 'ten' in f
assert 'one' in e
assert 'ten' not in e
assert 'one' not in f
# test set based operations
union = f | e
intersection = f & e
assert 'ten' in union
assert 'one' in union
assert 'three' in intersection
assert 'ten' not in intersection
assert 'one' not in intersection
кроме того, в моем случае я нашел полезным иметь более быструю функцию count_bits для BitVector. Поместите этот код в BitVector 1.5, и он должен дать вам более эффективный бит метод подсчета:
def fast_count_bits( self, v ):
bits = (
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 )
return bits[v & 0xff] + bits[(v >> 8) & 0xff] + bits[(v >> 16) & 0xff] + bits[v >> 24]
в ответ на Parand, говоря: "общая практика, похоже, использует что-то вроде SHA1 и разделяет биты, чтобы сформировать несколько хэшей", хотя это может быть правдой в том смысле, что это обычная практика (PyBloom также использует ее), это все еще не означает, что это правильно; -)
для фильтра Bloom единственное требование хэш-функции заключается в том, что ее выходное пространство должно быть равномерно распределено с учетом ожидаемого ввода. Хотя криптографический хэш, безусловно, выполняет это требование, это также немного похоже на стрельбу мухой с базукой.
попробуйте FNV Hash который использует только один XOR и одно умножение на входной байт, которое я оцениваю в несколько сотен раз быстрее, чем SHA1:)
хэш FNV не является криптографически безопасным, но вам это не нужно. Он имеет немного несовершенное поведение лавины, но вы не используете его для проверки целостности либо.
о равномерности, обратите внимание, что вторая ссылка только сделала тест Хи-квадрат для 32-битного хэша FNV. Лучше использовать больше битов и вариант FNV-1, который меняет местами шаги XOR и MUL для лучшей битовой дисперсии. Для фильтра Bloom есть еще несколько уловов, таких как равномерное отображение выходных данных в диапазон индексов битового массива. Если возможно, я бы сказал, округлить размер битового массива до ближайшей степени 2 и настроить k соответственно. Таким образом, вы получите лучшую точность, и вы можете использовать простой XOR-складывание для отображения диапазона.
кроме того, вот ссылка, объясняющая, почему вы не хотите SHA1 (или любой криптографический хэш), когда вам нужно общего назначения хэш -.
посмотреть массив модуль.
class Bit( object ):
def __init__( self, size ):
self.bits= array.array('B',[0 for i in range((size+7)//8)] )
def set( self, bit ):
b= self.bits[bit//8]
self.bits[bit//8] = b | 1 << (bit % 8)
def get( self, bit ):
b= self.bits[bit//8]
return (b >> (bit % 8)) & 1
FWIW, все //8
и % 8
операции можно заменить >>3
и &0x07
. Это мая привести к немного лучшей скорости с риском некоторой неясности.
кроме того, изменение 'B'
и 8
to 'L'
и 32
должно быть быстрее на большинстве аппаратных средств. [Переход на 'H'
и 16 может быть быстрее на некоторых аппаратных средствах, но это сомнительно.]
В конце концов я нашел pybloomfiltermap. Я им не пользовался, но, похоже,он подойдет.
Я поставил реализацию фильтра Python bloom в http://stromberg.dnsalias.org / ~strombrg / drs-bloom-фильтр/
это в чистом python, имеет хорошие хэш-функции, хорошие автоматические тесты, выбор бэкэндов (диск, массив, mmap, больше) и более интуитивные аргументы для __init__
метод, поэтому вы можете указать идеальное количество элементов и желаемую максимальную частоту ошибок вместо несколько эфирных, специфичных для datastructure перестроек.
меня очень интересуют варианты фильтров Bloom, их производительность и понимание их вариантов использования. Существует так много хорошо цитируемых исследовательских работ по вариантам фильтров Bloom (в том числе опубликованных на первоклассных конференциях,таких как SIGCOMM, SIGMETRICS), но я не думаю, что их присутствие сильно в основных языковых библиотеках. Почему вы думаете, что это так?
в то время как мой интерес является языковым агностиком, я хотел поделиться статьей, которую я написал о вариантах фильтра Bloom, и применения фильтра цветения.
http://appolo85.wordpress.com/2010/08/03/bloom-filter/
Я хотел бы узнать больше об их использовании вариантов фильтров Bloom, их дизайне/реализации и библиотеках на других языках.
Как вы думаете, что большинство публикаций и (код?) на вариантах фильтров Блума служат только для увеличения количества опубликованных работ выпускника PhD?
Или дело в том, что большинство людей не хотите возиться с готовой к производству стандартной реализацией фильтра bloom, которая "работает просто отлично": D