fix(level-hash): allow configuring load factor threshold for auto expand

This commit is contained in:
Akash Yadav
2024-08-01 18:12:34 +05:30
parent 77bc0e2fdd
commit b18628f270
6 changed files with 33 additions and 8 deletions
@@ -39,7 +39,7 @@ import androidx.annotation.FloatRange
* as well. This results in decreased access performance and insertion failures.
* It is **recommended** to enable [AbstractLevelHashBuilder.autoExpand] which
* will automatically expand the level hash when the load factor reaches a certain
* threshold (specifically, when the top level is full).
* threshold ([LevelHash.loadFactor]).
*
* @param K The key type.
* @param V The value type.
@@ -164,6 +164,11 @@ interface LevelHash<K : Any, V : Any?> : AutoCloseable {
*/
const val BUCKET_SIZE_DEFAULT = 4
/**
* Default load factor threshold for auto expand.
*/
const val AUTO_EXPAND_LOAD_FACTOR_THRESHOLD: Float = 0.92f
/**
* Build an in-memory level hash.
*/
@@ -33,6 +33,7 @@ abstract class AbstractLevelHashBuilder<K : Any, V : Any?> {
protected var hashFn: LevelHashFn<K>? = null
protected var uniqueKeys = false
protected var autoExpand = true
protected var loadFactorForAutoExpand = LevelHash.AUTO_EXPAND_LOAD_FACTOR_THRESHOLD
protected var seedGenerator: SeedGeneratorFn = ::levelHashDefaultSeeds
/**
@@ -112,7 +113,8 @@ class InMemoryHashBuilder<K : Any, V : Any?> :
levelHashFn = hashFn!!,
seeds = seedGenerator(),
uniqueKeys = uniqueKeys,
autoExpand = autoExpand
autoExpand = autoExpand,
loadFactorForAutoExpand = loadFactorForAutoExpand,
)
}
}
@@ -170,6 +172,7 @@ class PersistentHashBuilder<K : Any, V : Any?> :
bucketSize = bucketSize,
uniqueKeys = uniqueKeys,
autoExpand = autoExpand,
loadFactorForAutoExpand = loadFactorForAutoExpand,
levelHashFn = hashFn!!,
seeds = seeds,
keyExternalizer = keyExternalizer,
@@ -38,6 +38,7 @@ internal abstract class AbstractLevelHash<K : Any, V : Any?> internal constructo
final override val bucketSize: Int,
protected val uniqueKeys: Boolean,
protected val autoExpand: Boolean,
protected val loadFactorForAutoExpand: Float,
protected val levelHashFn: LevelHashFn<K>,
protected val seeds: Pair<HashT, HashT>,
) : LevelHash<K, V> {
@@ -123,7 +124,7 @@ internal abstract class AbstractLevelHash<K : Any, V : Any?> internal constructo
override fun insert(key: K, value: V): Boolean {
if (loadFactor() >= 0.92 && autoExpand) {
if (loadFactor() >= loadFactorForAutoExpand && autoExpand) {
check(expand()) {
"Failed to expand the level hash"
}
@@ -34,10 +34,18 @@ internal class InMemoryLevelHash<K : Any, V : Any?>(
bucketSize: Int,
uniqueKeys: Boolean,
autoExpand: Boolean,
loadFactorForAutoExpand: Float,
levelHashFn: LevelHashFn<K>,
seeds: Pair<HashT, HashT>,
) : AbstractLevelHash<K, V>(levelSize, bucketSize, uniqueKeys, autoExpand,
levelHashFn, seeds) {
) : AbstractLevelHash<K, V>(
levelSize,
bucketSize,
uniqueKeys,
autoExpand,
loadFactorForAutoExpand,
levelHashFn,
seeds
) {
private val levels = Array(LEVEL_COUNT) { levelIndex ->
allocateLevel(levelSize, levelIndex, bucketSize)
@@ -34,14 +34,21 @@ internal class PersistentLevelHash<K : Any, V : Any?>(
bucketSize: Int,
uniqueKeys: Boolean,
autoExpand: Boolean,
loadFactorForAutoExpand: Float,
levelHashFn: LevelHashFn<K>,
seeds: Pair<HashT, HashT>,
keyExternalizer: DataExternalizer<K>,
valueExternalizer: DataExternalizer<V>,
indexFile: File,
) : AbstractLevelHash<K, V>(levelSize = levelSize, bucketSize = bucketSize,
uniqueKeys = uniqueKeys, autoExpand = autoExpand, levelHashFn = levelHashFn,
seeds = seeds) {
) : AbstractLevelHash<K, V>(
levelSize = levelSize,
bucketSize = bucketSize,
uniqueKeys = uniqueKeys,
autoExpand = autoExpand,
loadFactorForAutoExpand = loadFactorForAutoExpand,
levelHashFn = levelHashFn,
seeds = seeds
) {
@VisibleForTesting
internal val io = PersistentLevelHashIO(indexFile, levelSize, bucketSize, keyExternalizer,
@@ -42,6 +42,7 @@ private val logger = LoggerFactory.getLogger(PersistentLevelHashIO::class.java)
// improved performance when seeking through the file.
// TODO: The above is also applicable for the keymap file when the level hash is
// expanded and new regions for the levels are allocated.
// TODO: Allow concurrent access to the level hash.
/**
* This class is responsible for performing the IO operations for the level hash