@@ -159,10 +159,87 @@ PyAPI_FUNC(_PyExecutorObject*) _Py_GetExecutor(PyCodeObject *code, int offset);
159159
160160int _Py_ExecutorInit (_PyExecutorObject * , const _PyBloomFilter * );
161161void _Py_ExecutorDetach (_PyExecutorObject * );
162- void _Py_BloomFilter_Init (_PyBloomFilter * );
163- void _Py_BloomFilter_Add (_PyBloomFilter * bloom , void * obj );
164162PyAPI_FUNC (void ) _Py_Executor_DependsOn (_PyExecutorObject * executor , void * obj );
165163
164+ /* We use a bloomfilter with k = 6, m = 256
165+ * The choice of k and the following constants
166+ * could do with a more rigorous analysis,
167+ * but here is a simple analysis:
168+ *
169+ * We want to keep the false positive rate low.
170+ * For n = 5 (a trace depends on 5 objects),
171+ * we expect 30 bits set, giving a false positive
172+ * rate of (30/256)**6 == 2.5e-6 which is plenty
173+ * good enough.
174+ *
175+ * However with n = 10 we expect 60 bits set (worst case),
176+ * giving a false positive of (60/256)**6 == 0.0001
177+ *
178+ * We choose k = 6, rather than a higher number as
179+ * it means the false positive rate grows slower for high n.
180+ *
181+ * n = 5, k = 6 => fp = 2.6e-6
182+ * n = 5, k = 8 => fp = 3.5e-7
183+ * n = 10, k = 6 => fp = 1.6e-4
184+ * n = 10, k = 8 => fp = 0.9e-4
185+ * n = 15, k = 6 => fp = 0.18%
186+ * n = 15, k = 8 => fp = 0.23%
187+ * n = 20, k = 6 => fp = 1.1%
188+ * n = 20, k = 8 => fp = 2.3%
189+ *
190+ * The above analysis assumes perfect hash functions,
191+ * but those don't exist, so the real false positive
192+ * rates may be worse.
193+ */
194+
195+ #define _Py_BLOOM_FILTER_K 6
196+ #define _Py_BLOOM_FILTER_SEED 20221211
197+
198+ static inline uint64_t
199+ address_to_hash (void * ptr ) {
200+ assert (ptr != NULL );
201+ uint64_t uhash = _Py_BLOOM_FILTER_SEED ;
202+ uintptr_t addr = (uintptr_t )ptr ;
203+ for (int i = 0 ; i < SIZEOF_VOID_P ; i ++ ) {
204+ uhash ^= addr & 255 ;
205+ uhash *= (uint64_t )PyHASH_MULTIPLIER ;
206+ addr >>= 8 ;
207+ }
208+ return uhash ;
209+ }
210+
211+ static inline void
212+ _Py_BloomFilter_Init (_PyBloomFilter * bloom )
213+ {
214+ for (int i = 0 ; i < _Py_BLOOM_FILTER_WORDS ; i ++ ) {
215+ bloom -> bits [i ] = 0 ;
216+ }
217+ }
218+
219+ static inline void
220+ _Py_BloomFilter_Add (_PyBloomFilter * bloom , void * ptr )
221+ {
222+ uint64_t hash = address_to_hash (ptr );
223+ assert (_Py_BLOOM_FILTER_K <= 8 );
224+ for (int i = 0 ; i < _Py_BLOOM_FILTER_K ; i ++ ) {
225+ uint8_t bits = hash & 255 ;
226+ bloom -> bits [bits >> _Py_BLOOM_FILTER_WORD_SHIFT ] |=
227+ (_Py_bloom_filter_word_t )1 << (bits & (_Py_BLOOM_FILTER_BITS_PER_WORD - 1 ));
228+ hash >>= 8 ;
229+ }
230+ }
231+
232+ static inline bool
233+ bloom_filter_may_contain (const _PyBloomFilter * bloom , const _PyBloomFilter * hashes )
234+ {
235+ for (int i = 0 ; i < _Py_BLOOM_FILTER_WORDS ; i ++ ) {
236+ if ((bloom -> bits [i ] & hashes -> bits [i ]) != hashes -> bits [i ]) {
237+ return false;
238+ }
239+ }
240+ return true;
241+ }
242+
166243#define _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS 3
167244#define _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS 6
168245
0 commit comments