fast_bloom_filter 1.0.0 β†’ 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6821c8b6ccb023e3f803243eaed3c2612159be3eb384817f6b79b5f1a2e7de72
4
- data.tar.gz: 2e0fb03a4c1ba7fb9ad43f20bc83ab6b6f33fe302dca4854af7c640a8edef5c7
3
+ metadata.gz: 124ed9c861897621021ba516be4389a0c5304282147406fd1d79a68264041ebf
4
+ data.tar.gz: 17324726d1f5eaad49a362334499d79c72ed3af924abe9d84a81023f942ac056
5
5
  SHA512:
6
- metadata.gz: '09c38dcf72f4c1f5dee778099d8a7ea9b4f5fa20d865d23dc0ba7fca7f7f77fc6533f35c314906749208e47eb4dd495571d3ce0d46edbcf2b63bc461e6333e69'
7
- data.tar.gz: '0729565d307f3a19811fcf0126fda7ae20776124dd6ceb41f60f03808118ff8d294124da01df7c30598092d4f223d7fc7b5b838d6f248c1a16d6d1dbe4c81d0c'
6
+ metadata.gz: eb1437aec23308784ebb440f46815cee965c1d530ca957d3edb97de2cc361db5987f2a73f32d10884ce744eb87b6eabe9a44b6f0cd91acbbea44d62514c35b8b
7
+ data.tar.gz: 776703bb0bf4b3cd6f243dfb1b87a8402e2e72f2c483396a9001f91656b975d4c44641dbddf99c41f0d98cb2a83db8e8954460787e08e0a63e5c2d787a4a2c56
data/CHANGELOG.md CHANGED
@@ -5,6 +5,81 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.1.0] - 2026-03-24
9
+
10
+ ### ⚑ Performance Optimizations
11
+
12
+ ### Changed
13
+ - **Performance improvements**: Optimized C extension implementation
14
+ - **Memory efficiency**: Improved memory usage from ~332KB to ~242KB for 100K elements (~27% reduction)
15
+ - **Speed boost**: Add operations now consistently ~5x faster than Ruby Set (up from ~4.7x)
16
+ - Better overall stability and performance characteristics across multiple benchmark runs
17
+
18
+ ### Technical Details
19
+ - Enhanced C code optimization in hash functions and bit operations
20
+ - More efficient memory allocation and management
21
+ - Improved layer scaling algorithm for better memory utilization
22
+ - Reduced temporary allocations during hash computation
23
+
24
+ ### Benchmarks (100K elements)
25
+ - **Before**: Add: ~5.5ms, Memory: ~332KB
26
+ - **After**: Add: ~5.4ms, Memory: ~242KB
27
+ - Consistent 5x+ speedup vs Ruby Set across multiple runs
28
+
29
+ ## [2.0.0] - 2026-02-12
30
+
31
+ ### πŸš€ Major Release - Scalable Bloom Filter
32
+
33
+ This is a **breaking change** that transforms FastBloomFilter into a scalable, dynamic data structure.
34
+
35
+ ### Added
36
+ - **Scalable Architecture**: Filter now grows automatically by adding layers
37
+ - **No Upfront Capacity**: No need to specify capacity - just set error_rate
38
+ - **Multi-Layer System**: Each layer has progressively tighter error rates
39
+ - **Smart Growth Strategy**: Growth factor starts at 2x and decreases (like Go slices)
40
+ - **Layer Statistics**: Detailed per-layer stats via `stats` method
41
+ - **New API**: `Filter.new(error_rate: 0.01, initial_capacity: 1024)`
42
+ - `num_layers` method to check how many layers are active
43
+ - Enhanced `merge!` to combine filters with all their layers
44
+
45
+ ### Changed
46
+ - **BREAKING**: Constructor now uses keyword arguments: `Filter.new(error_rate: 0.01)` instead of `Filter.new(capacity, error_rate)`
47
+ - **BREAKING**: `stats` now returns multi-layer information with `:layers` array
48
+ - **BREAKING**: Helper methods changed: `for_emails(error_rate: 0.001)` instead of `for_emails(capacity)`
49
+ - Memory allocation is now dynamic and grows on-demand
50
+ - `inspect` output now shows layer count and total elements
51
+
52
+ ### Technical Details
53
+ - Based on "Scalable Bloom Filters" (Almeida et al., 2007)
54
+ - Each layer uses error_rate * (1 - r) * r^i formula
55
+ - Default tightening factor (r) = 0.85
56
+ - Growth factors: 2x β†’ 1.75x β†’ 1.5x β†’ 1.25x as layers increase
57
+ - Layers are checked from newest to oldest for better cache locality
58
+
59
+ ### Migration Guide
60
+
61
+ **v1.x code:**
62
+ ```ruby
63
+ bloom = FastBloomFilter::Filter.new(10_000, 0.01)
64
+ bloom = FastBloomFilter.for_emails(100_000)
65
+ ```
66
+
67
+ **v2.x code:**
68
+ ```ruby
69
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01, initial_capacity: 1000)
70
+ bloom = FastBloomFilter.for_emails(error_rate: 0.001, initial_capacity: 10_000)
71
+ # Or simply:
72
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01) # starts small, grows as needed
73
+ ```
74
+
75
+ ### Performance
76
+ - Same O(k) complexity for add/lookup
77
+ - Slightly higher memory overhead due to layer management
78
+ - Better memory efficiency for unknown/growing datasets
79
+ - No performance degradation as filter grows
80
+
81
+ ---
82
+
8
83
  ## [1.0.0] - 2026-02-09
9
84
 
10
85
  ### Added
data/README.md CHANGED
@@ -1,17 +1,26 @@
1
- # FastBloomFilter
1
+ # FastBloomFilter v2 πŸš€
2
2
 
3
- [![CI](https://github.com/yourusername/fast_bloom_filter/actions/workflows/ci.yml/badge.svg)](https://github.com/yourusername/fast_bloom_filter/actions/workflows/ci.yml)
4
3
  [![Gem Version](https://badge.fury.io/rb/fast_bloom_filter.svg)](https://badge.fury.io/rb/fast_bloom_filter)
5
4
 
6
- A high-performance Bloom Filter implementation in C for Ruby. Perfect for Rails applications that need memory-efficient set membership testing.
5
+ A **scalable** Bloom Filter implementation in C for Ruby. Grows automatically without requiring upfront capacity! Perfect for Rails applications that need memory-efficient set membership testing with unknown dataset sizes.
6
+
7
+ ## What's New in v2? πŸŽ‰
8
+
9
+ - **πŸ”„ Scalable Architecture**: No need to guess capacity upfront - the filter grows automatically
10
+ - **πŸ“Š Multi-Layer System**: Adds new layers dynamically as data grows
11
+ - **🎯 Smart Growth**: Growth factor adapts (2x β†’ 1.75x β†’ 1.5x β†’ 1.25x)
12
+ - **πŸ’‘ Simpler API**: Just specify error rate, not capacity
13
+ - **πŸ“ˆ Better for Unknown Sizes**: Perfect when you don't know how much data you'll have
14
+
15
+ Based on ["Scalable Bloom Filters" (Almeida et al., 2007)](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=10.1.1.725.390)
7
16
 
8
17
  ## Features
9
18
 
10
19
  - **πŸš€ Fast**: C implementation with MurmurHash3
11
20
  - **πŸ’Ύ Memory Efficient**: 20-50x less memory than Ruby Set
12
- - **🎯 Configurable**: Adjustable false positive rate
13
- - **πŸ”’ Thread-Safe**: Safe for concurrent operations
14
- - **πŸ“Š Statistics**: Built-in performance monitoring
21
+ - **πŸ”„ Auto-Scaling**: Grows dynamically as you add elements
22
+ - **🎯 Configurable**: Adjustable false positive rate per layer
23
+ - **πŸ“Š Statistics**: Detailed per-layer performance monitoring
15
24
  - **βœ… Well-Tested**: Comprehensive test suite
16
25
 
17
26
  ## Installation
@@ -36,15 +45,19 @@ gem install fast_bloom_filter
36
45
 
37
46
  ## Usage
38
47
 
39
- ### Basic Operations
48
+ ### Basic Operations (v2 API)
40
49
 
41
50
  ```ruby
42
51
  require 'fast_bloom_filter'
43
52
 
44
- # Create a filter for 10,000 items with 1% false positive rate
45
- bloom = FastBloomFilter::Filter.new(10_000, 0.01)
53
+ # Create a scalable filter - NO CAPACITY NEEDED!
54
+ # Just specify your desired error rate
55
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01)
46
56
 
47
- # Add items
57
+ # Or with an initial capacity hint (optional)
58
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01, initial_capacity: 1000)
59
+
60
+ # Add items - filter grows automatically
48
61
  bloom.add("user@example.com")
49
62
  bloom << "another@example.com" # alias for add
50
63
 
@@ -52,6 +65,13 @@ bloom << "another@example.com" # alias for add
52
65
  bloom.include?("user@example.com") # => true
53
66
  bloom.include?("notfound@test.com") # => false (probably)
54
67
 
68
+ # Add thousands or millions - it scales!
69
+ 100_000.times { |i| bloom.add("user#{i}@test.com") }
70
+
71
+ # Check stats
72
+ bloom.count # => 100002
73
+ bloom.num_layers # => 8 (grew automatically!)
74
+
55
75
  # Batch operations
56
76
  emails = ["user1@test.com", "user2@test.com", "user3@test.com"]
57
77
  bloom.add_all(emails)
@@ -67,50 +87,99 @@ bloom.clear
67
87
 
68
88
  ```ruby
69
89
  # For email deduplication (0.1% false positive rate)
70
- bloom = FastBloomFilter.for_emails(100_000)
90
+ bloom = FastBloomFilter.for_emails(error_rate: 0.001)
71
91
 
72
92
  # For URL tracking (1% false positive rate)
73
- bloom = FastBloomFilter.for_urls(50_000)
93
+ bloom = FastBloomFilter.for_urls(error_rate: 0.01)
94
+
95
+ # With initial capacity hint
96
+ bloom = FastBloomFilter.for_emails(error_rate: 0.001, initial_capacity: 10_000)
74
97
  ```
75
98
 
76
99
  ### Merge Filters
77
100
 
78
101
  ```ruby
79
- bloom1 = FastBloomFilter::Filter.new(1000, 0.01)
80
- bloom2 = FastBloomFilter::Filter.new(1000, 0.01)
102
+ bloom1 = FastBloomFilter::Filter.new(error_rate: 0.01)
103
+ bloom2 = FastBloomFilter::Filter.new(error_rate: 0.01)
81
104
 
82
105
  bloom1.add("item1")
83
106
  bloom2.add("item2")
84
107
 
85
108
  bloom1.merge!(bloom2) # bloom1 now contains both items
109
+ # Merges all layers from bloom2 into bloom1
86
110
  ```
87
111
 
88
112
  ### Statistics
89
113
 
90
114
  ```ruby
91
- bloom = FastBloomFilter::Filter.new(10_000, 0.01)
115
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01)
116
+ 1000.times { |i| bloom.add("item#{i}") }
117
+
92
118
  stats = bloom.stats
93
119
 
94
120
  # => {
95
- # capacity: 10000,
96
- # size_bytes: 11982,
97
- # num_hashes: 7,
98
- # fill_ratio: 0.0
121
+ # total_count: 1000,
122
+ # num_layers: 2,
123
+ # total_bytes: 2500,
124
+ # total_bits: 20000,
125
+ # total_bits_set: 6543,
126
+ # fill_ratio: 0.32715,
127
+ # error_rate: 0.01,
128
+ # layers: [
129
+ # {
130
+ # layer: 0,
131
+ # capacity: 1024,
132
+ # count: 1024,
133
+ # size_bytes: 1229,
134
+ # num_hashes: 7,
135
+ # bits_set: 5234,
136
+ # total_bits: 9832,
137
+ # fill_ratio: 0.532,
138
+ # error_rate: 0.0015
139
+ # },
140
+ # # ... more layers
141
+ # ]
99
142
  # }
100
143
 
101
144
  puts bloom.inspect
102
- # => #<FastBloomFilter::Filter capacity=10000 size=11.7KB hashes=7 fill=0.0%>
145
+ # => #<FastBloomFilter::Filter v2 layers=2 count=1000 size=2.44KB fill=32.72%>
146
+ ```
147
+
148
+ ## How Scalable Bloom Filters Work
149
+
150
+ Traditional Bloom Filters require you to specify capacity upfront. **Scalable Bloom Filters** solve this by:
151
+
152
+ 1. **Starting Small**: Begin with a small initial capacity (default: 1024 elements)
153
+ 2. **Adding Layers**: When a layer fills up, add a new layer with larger capacity
154
+ 3. **Tightening Error Rates**: Each new layer has a tighter error rate to maintain overall FPR
155
+ 4. **Smart Growth**: Growth factor decreases over time (2x β†’ 1.75x β†’ 1.5x β†’ 1.25x)
156
+
157
+ ### Error Rate Distribution
158
+
159
+ Each layer `i` gets error rate: `total_error_rate Γ— (1 - r) Γ— r^i`
160
+
161
+ Where `r` is the tightening factor (default: 0.85). This ensures the sum of all layer error rates converges to your target error rate.
162
+
163
+ ### Example Growth Pattern
164
+
165
+ ```
166
+ Layer 0: capacity=1,024 error_rate=0.0015 (initial)
167
+ Layer 1: capacity=2,048 error_rate=0.0013 (2x growth)
168
+ Layer 2: capacity=3,584 error_rate=0.0011 (1.75x growth)
169
+ Layer 3: capacity=5,376 error_rate=0.0009 (1.5x growth)
170
+ Layer 4: capacity=6,720 error_rate=0.0008 (1.25x growth)
171
+ ...
103
172
  ```
104
173
 
105
174
  ## Performance
106
175
 
107
176
  Benchmarks on MacBook Pro M1 (100K elements):
108
177
 
109
- | Operation | Bloom Filter | Ruby Set | Speedup |
110
- |-----------|--------------|----------|---------|
111
- | Add | 45ms | 120ms | 2.7x |
112
- | Check | 8ms | 15ms | 1.9x |
113
- | Memory | 120KB | 2000KB | 16.7x |
178
+ | Operation | Bloom Filter v2 | Ruby Set | Speedup |
179
+ |-----------|-----------------|----------|---------|
180
+ | Add | 48ms | 120ms | 2.5x |
181
+ | Check | 9ms | 15ms | 1.7x |
182
+ | Memory | 145KB | 2000KB | 13.8x |
114
183
 
115
184
  Run benchmarks yourself:
116
185
 
@@ -120,11 +189,12 @@ ruby demo.rb
120
189
 
121
190
  ## Use Cases
122
191
 
123
- ### Rails: Prevent Duplicate Email Signups
192
+ ### Rails: Prevent Duplicate Email Signups (No Capacity Guessing!)
124
193
 
125
194
  ```ruby
126
195
  class User < ApplicationRecord
127
- SIGNUP_BLOOM = FastBloomFilter.for_emails(1_000_000)
196
+ # No need to guess how many users you'll have!
197
+ SIGNUP_BLOOM = FastBloomFilter.for_emails(error_rate: 0.001)
128
198
 
129
199
  before_validation :check_duplicate_signup
130
200
 
@@ -140,12 +210,13 @@ class User < ApplicationRecord
140
210
  end
141
211
  ```
142
212
 
143
- ### Track Visited URLs
213
+ ### Track Visited URLs (Scales to Millions)
144
214
 
145
215
  ```ruby
146
216
  class WebCrawler
147
217
  def initialize
148
- @visited = FastBloomFilter.for_urls(10_000_000)
218
+ # Starts small, grows as needed
219
+ @visited = FastBloomFilter.for_urls(error_rate: 0.01)
149
220
  end
150
221
 
151
222
  def crawl(url)
@@ -153,6 +224,11 @@ class WebCrawler
153
224
 
154
225
  @visited.add(url)
155
226
  # ... crawl logic
227
+
228
+ # Check growth
229
+ if @visited.count % 10_000 == 0
230
+ puts "Crawled #{@visited.count} URLs, #{@visited.num_layers} layers"
231
+ end
156
232
  end
157
233
  end
158
234
  ```
@@ -162,7 +238,7 @@ end
162
238
  ```ruby
163
239
  class CacheWarmer
164
240
  def initialize
165
- @warmed = FastBloomFilter::Filter.new(100_000, 0.001)
241
+ @warmed = FastBloomFilter::Filter.new(error_rate: 0.001)
166
242
  end
167
243
 
168
244
  def warm(key)
@@ -174,27 +250,31 @@ class CacheWarmer
174
250
  end
175
251
  ```
176
252
 
177
- ## How It Works
178
-
179
- A Bloom Filter is a space-efficient probabilistic data structure that tests whether an element is a member of a set:
253
+ ## Migration from v1.x
180
254
 
181
- - **No false negatives**: If it says "no", the item is definitely not in the set
182
- - **Possible false positives**: If it says "yes", the item is probably in the set
183
- - **Memory efficient**: Uses bit arrays instead of storing actual items
184
- - **Fast**: O(k) for add and lookup, where k is the number of hash functions
255
+ **v1.x (Fixed Capacity):**
256
+ ```ruby
257
+ bloom = FastBloomFilter::Filter.new(10_000, 0.01)
258
+ bloom = FastBloomFilter.for_emails(100_000)
259
+ ```
185
260
 
186
- ### Parameters
261
+ **v2.x (Scalable):**
262
+ ```ruby
263
+ # Recommended: Let it scale automatically
264
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01)
187
265
 
188
- - **Capacity**: Expected number of elements
189
- - **Error Rate**: Probability of false positives (default: 0.01 = 1%)
266
+ # Or with initial capacity hint
267
+ bloom = FastBloomFilter::Filter.new(error_rate: 0.01, initial_capacity: 1000)
190
268
 
191
- The filter automatically calculates optimal bit array size and number of hash functions.
269
+ # Helper methods also changed
270
+ bloom = FastBloomFilter.for_emails(error_rate: 0.001, initial_capacity: 10_000)
271
+ ```
192
272
 
193
273
  ## Development
194
274
 
195
275
  ```bash
196
276
  # Clone the repository
197
- git clone https://github.com/yourusername/fast_bloom_filter.git
277
+ git clone https://github.com/roman-haidarov/fast_bloom_filter.git
198
278
  cd fast_bloom_filter
199
279
 
200
280
  # Install dependencies
@@ -210,7 +290,7 @@ bundle exec rake test
210
290
  gem build fast_bloom_filter.gemspec
211
291
 
212
292
  # Install locally
213
- gem install ./fast_bloom_filter-1.0.0.gem
293
+ gem install ./fast_bloom_filter-2.0.0.gem
214
294
  ```
215
295
 
216
296
  ### Quick Build Script
@@ -225,6 +305,15 @@ gem install ./fast_bloom_filter-1.0.0.gem
225
305
  - C compiler (gcc, clang, etc.)
226
306
  - Make
227
307
 
308
+ ## Technical Details
309
+
310
+ - **Hash Function**: MurmurHash3 (32-bit)
311
+ - **Bit Array**: Dynamic allocation per layer
312
+ - **Growth Strategy**: Adaptive (2x β†’ 1.75x β†’ 1.5x β†’ 1.25x)
313
+ - **Tightening Factor**: 0.85 (configurable)
314
+ - **Memory Management**: Ruby GC integration with proper cleanup
315
+ - **Thread Safety**: Safe for concurrent reads (writes need external synchronization)
316
+
228
317
  ## Contributing
229
318
 
230
319
  1. Fork it
@@ -239,14 +328,15 @@ The gem is available as open source under the terms of the [MIT License](LICENSE
239
328
 
240
329
  ## Credits
241
330
 
242
- - MurmurHash3 implementation based on Austin Appleby's original work
243
- - Bloom Filter algorithm by Burton Howard Bloom (1970)
331
+ - Scalable Bloom Filters algorithm: Almeida, Baquero, PreguiΓ§a, Hutchison (2007)
332
+ - MurmurHash3 implementation: Austin Appleby
333
+ - Original Bloom Filter: Burton Howard Bloom (1970)
244
334
 
245
335
  ## Support
246
336
 
247
- - πŸ› [Report bugs](https://github.com/yourusername/fast_bloom_filter/issues)
248
- - πŸ’‘ [Request features](https://github.com/yourusername/fast_bloom_filter/issues)
249
- - πŸ“– [Documentation](https://github.com/yourusername/fast_bloom_filter)
337
+ - πŸ› [Report bugs](https://github.com/roman-haidarov/fast_bloom_filter/issues)
338
+ - πŸ’‘ [Request features](https://github.com/roman-haidarov/fast_bloom_filter/issues)
339
+ - πŸ“– [Documentation](https://github.com/roman-haidarov/fast_bloom_filter)
250
340
 
251
341
  ## Changelog
252
342