web-dev-qa-db-ja.com

Pythonの最新の高性能ブルームフィルター?

Pythonで、かなり多数のアイテム(たとえば、0.01%の偽陽性率で1億から10億のアイテム)を処理するための生産品質のブルームフィルターの実装を探しています。

Pybloom は1つのオプションですが、Python 2.5で定期的にDeprecationWarningエラーをスローするため、その経過時間を示しているようです。JoeGregorioにも 実装

要件は、高速ルックアップのパフォーマンスと安定性です。また、特に優れたc/c ++実装へのPythonインターフェイス、または優れたJava実装がある場合は、Jythonへのインターフェイス)を作成することもできます。

それが欠けていると、〜16E9ビットを処理できるビット配列/ビットベクトル表現に関する推奨事項はありますか?

50
Parand

最終的に私は pybloomfiltermap を見つけました。私は使ったことがありませんが、法案に合うようです。

10
Parand

私も最近この道を進みました。私のアプリケーションは少し異なっているように聞こえますが。多数の文字列の集合演算を概算することに興味がありました。

fastビットベクトルが必要であるという重要な観察を行います。ブルームフィルターに何を入れたいかによっては、使用するハッシュアルゴリズムの速度についても考慮する必要があります。これが library 便利だと思うかもしれません。また、キーを1回だけハッシュする、以下で使用する乱数手法をいじくり回すこともできます。

Java以外のビット配列の実装に関して:

BitVector を使用してブルームフィルターを作成しました。ライブラリのプロファイリングと最適化、およびパッチのAviへの提供に時間を費やしました。そのBitVectorリンクに移動し、v1.5の確認応答まで下にスクロールして、詳細を確認します。結局、パフォーマンスはこのプロジェクトの目標ではないことに気づき、それを使用しないことにしました。

これが私がうそをついたコードです。私はこれを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


#
# [email protected] / 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 __== '__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

また、私の場合、BitVectorに対してより高速なcount_bits関数があると便利だと思いました。このコードをBitVector1.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]
28
Ryan Cox

パランドへの反応として、「一般的な慣行はSHA1のようなものを使用し、ビットを分割して複数のハッシュを形成しているようです」と言っていますが、それは一般的な慣行(PyBloomも使用しています)という意味では真実かもしれませんが、それでもそうではありません。 tはそれが正しいことを意味します;-)

ブルームフィルターの場合、ハッシュ関数に必要な唯一の要件は、期待される入力が与えられた場合に、その出力スペースが均一に分散されている必要があることです。暗号化ハッシュは確かにこの要件を満たしていますが、バズーカでハエを撃つようなものでもあります。

代わりに、 FNVハッシュ を試してください。これは1つのXORと、入力バイトごとに1つの乗算を使用します。これはSHA1よりも数百倍高速です:)

FNVハッシュは暗号的に安全ではありませんが、暗号的に安全である必要はありません。わずかに 不完全な雪崩動作 がありますが、整合性チェックにも使用していません。

均一性については、2番目のリンクが32ビットFNVハッシュのカイ2乗検定のみを行ったことに注意してください。より多くのビットと、XORとMULステップを交換してビット分散を改善するFNV-1バリアントを使用することをお勧めします。ブルームフィルターの場合、マッピングなど、さらにいくつかのキャッチがあります。出力はビット配列のインデックス範囲に均一になります。可能であれば、ビット配列のサイズを最も近い2の累乗に切り上げて、kしたがって、精度が向上し、単純なXORフォールディングを使用して範囲をマッピングできます。

さらに、必要なときにSHA1(または暗号化ハッシュ)が不要な理由を説明するリファレンスがあります 汎用ハッシュ

24
Kazeng

array モジュールを見てください。

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に置き換えることができます。このmayは、速度がわずかに向上しますが、あいまいになるリスクがあります。

また、'B'8'L'32に変更すると、ほとんどのハードウェアで高速になります。 [ハードウェアによっては、'H'と16に変更する方が速い場合がありますが、疑わしいです。]

8
S.Lott

pythonブルームフィルターの実装を http://stromberg.dnsalias.org/~strombrg/drs-bloom-filter/ に配置しました

純粋なPythonであり、優れたハッシュ関数、優れた自動テスト、選択されたバックエンド(ディスク、配列、mmapなど)、および__init__メソッドへのより直感的な引数を備えているため、理想的な要素数とややエーテル的なデータ構造固有の調整可能オブジェクトではなく、必要な最大エラー率。

2
user1277476

私はブルームフィルターのバリエーションとそのパフォーマンスに強い関心を持っており、それらのユースケースを理解しています。ブルームフィルターの変種(SIGCOMM、SIGMETRICSなどの一流の会議で発表されたものを含む)に関するよく引用された研究はたくさんありますが、主流の言語ライブラリではそれらの存在が強いとは思いません。なぜそうだと思いますか?

私の興味は言語に依存しませんが、ブルームフィルターのバリエーションとブルームフィルターのアプリケーションについて書いた記事を共有したいと思いました。

http://appolo85.wordpress.com/2010/08/03/bloom-filter/

ブルームフィルターバリアントのユースケース、設計/実装、および他の言語のライブラリについて詳しく知りたいと思います。

ブルームフィルターのバリエーションに関するほとんどの出版物、および(コード?)は、博士課程の卒業生の出版された論文数を増やすのに役立つだけだと思いますか?
それとも、ほとんどの人が「問題なく動作する」本番環境に対応した標準のブルームフィルターの実装を台無しにしたくないということですか:D

0
Arvind