Skip to content

Commit

Permalink
fix simulator's bloom filter table minumum sizing
Browse files Browse the repository at this point in the history
The table index is calculated by shifting the hashed value. As no
modulus is applied, the negation bit may lead the minimum index
to be 0 or 1, while the table size is one. Either the index has
to be modulated (via a mask) or the smallest table should be 2
to avoid this off-by-one error. Increasing the minimum allows for
simplifying the code.
  • Loading branch information
ben-manes committed Nov 29, 2020
1 parent 5cc2c7d commit 64b50b6
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 14 deletions.
1 change: 1 addition & 0 deletions caffeine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ dependencies {
javaAgent libraries.jamm

jmh libraries.jamm
jmh libraries.guava
jmh libraries.tcache
jmh libraries.cache2k
jmh libraries.ehcache3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public final class Cache2k<K, V> implements BasicCache<K, V> {

@SuppressWarnings("unchecked")
public Cache2k(int maximumSize) {
cache = Cache2kBuilder.forUnknownTypes()
cache = (Cache<K, V>) Cache2kBuilder.forUnknownTypes()
.entryCapacity(maximumSize)
.eternal(true)
.build();
Expand Down
2 changes: 1 addition & 1 deletion gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
ext {
versions = [
akka: '2.6.10',
cache2k: '1.9.1.Alpha',
cache2k: '1.9.3.Alpha',
checkerFramework: '3.7.1',
coherence: '20.06',
collision: '0.3.3',
Expand Down
8 changes: 2 additions & 6 deletions gradle/jmh.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ idea.module {
scopes.PROVIDED.plus += [ configurations.jmh ]
}

plugins.withType(EclipsePlugin) {
project.eclipse.classpath.plusConfigurations += [ configurations.jmh ]
}

eclipse.classpath.file.whenMerged {
entries.find { it.path == 'src/jmh/java' }.entryAttributes['test'] = 'true'
}
Expand Down Expand Up @@ -57,9 +53,9 @@ jmh {
resultsFile = file("${buildDir}/reports/jmh/results.json")

jvmArgs = '-server -Xmx2G -XX:+UseG1GC -XX:-UseBiasedLocking'
warmupIterations = 10
warmupIterations = 3
failOnError = true
iterations = 10
iterations = 3
forceGC = true
fork = 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public final class BloomFilter implements Membership {
int tableShift;
long[] table;

/**
* Creates a lazily initialized membership sketch, requiring {@link #ensureCapacity} be called
* when the expected number of insertions and the false positive probability have been determined.
*/
public BloomFilter() {}

/**
* Creates a membership sketch based on the expected number of insertions and the false positive
* probability.
Expand All @@ -61,18 +67,15 @@ public BloomFilter(Config config) {
* @param expectedInsertions the number of expected insertions
* @param fpp the false positive probability, where 0.0 > fpp < 1.0
*/
void ensureCapacity(@NonNegative long expectedInsertions, @NonNegative double fpp) {
public void ensureCapacity(@NonNegative long expectedInsertions, @NonNegative double fpp) {
checkArgument(expectedInsertions >= 0);
checkArgument(fpp > 0 && fpp < 1);

double optimalBitsFactor = -Math.log(fpp) / (Math.log(2) * Math.log(2));
int optimalNumberOfBits = (int) (expectedInsertions * optimalBitsFactor);
int optimalSize = optimalNumberOfBits >>> BITS_PER_LONG_SHIFT;
int optimalSize = Math.max(2, optimalNumberOfBits >>> BITS_PER_LONG_SHIFT);
if ((table != null) && (table.length >= optimalSize)) {
return;
} else if (optimalSize == 0) {
tableShift = Integer.SIZE - 1;
table = new long[2];
} else {
int powerOfTwoShift = Integer.SIZE - Integer.numberOfLeadingZeros(optimalSize - 1);
tableShift = Integer.SIZE - powerOfTwoShift;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public final class Rewriter implements Runnable {
private OutputFormat outputFormat;

@Override
@SuppressWarnings("PMD.ForLoopCanBeForeach")
public void run() {
Stopwatch stopwatch = Stopwatch.createStarted();
try (OutputStream output = new BufferedOutputStream(Files.newOutputStream(outputFile));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@
import static org.hamcrest.Matchers.lessThan;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.IntStream;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import com.github.benmanes.caffeine.cache.simulator.membership.FilterType;
import com.github.benmanes.caffeine.cache.simulator.membership.Membership;
import com.github.benmanes.caffeine.cache.simulator.membership.bloom.BloomFilter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jakewharton.fliptables.FlipTable;
Expand Down Expand Up @@ -74,6 +77,18 @@ public void bloomFilterTest(FilterType filterType) {
}
}

@Test(dataProvider = "ensureCapacity")
public void caffeine_ensureCapacity(int expectedInsertions, double fpp) {
BloomFilter filter = new BloomFilter();
filter.ensureCapacity(expectedInsertions, fpp);
filter.put(-1);
}

@DataProvider(name = "ensureCapacity")
public Iterator<Object[]> providesExpectedInsertions() {
return IntStream.range(0, 25).boxed().map(i -> new Object[] { i, FPP }).iterator();
}

@DataProvider(name = "filterTypes")
public Object[] providesFilterTypes() {
return FilterType.values();
Expand Down

0 comments on commit 64b50b6

Please sign in to comment.