Hi,
I’m trying to use Ristrettro for my application. While writing the benchmarks for comparison, I’m facing an issue where the Get is not able to find the value even after a time.Sleep(10 * time.Millisecond)
My use case is - At first, I’m setting a lot of values with SetWithTTL and then after some sleep time in between, I’m trying to Get those values. That’s where its failing.
My Benchmark is as follows :
func BenchmarkCacheSetWithRistretto(b *testing.B) {
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 10000000 * 10,
MaxCost: 10000000,
BufferItems: 64,
})
if err != nil {
panic(err)
}
for n := 0; n < b.N; n++ {
cache.SetWithTTL(strconv.Itoa(n%1000000)+strconv.Itoa(n%1000000), "value", 1, time.Duration(5*time.Second))
}
time.Sleep(50 * time.Millisecond)
for n := 0; n < b.N; n++ {
_, found := cache.Get(strconv.Itoa(n%1000000) + strconv.Itoa(n%1000000))
if !found {
panic("Value not found.")
}
}
}
The issue is that iterators in these loops (ones used for benchmarking) are non-deterministic.
Quoting from here:
Each benchmark is run for a minimum of 1 second by default. If the second has not elapsed when the Benchmark function returns, the value of b.N is increased in the sequence 1, 2, 5, 10, 20, 50, … and the function run again.
What you could potentially do is something like this:
And similarly search for all the keys when you are benchmarking the get part of the code.
@Anurag But since I’m running both the iteration in the same benchmark, the value of b.n shouldn’t vary, right?
Tried setting a fixed bound myself. Did not work.
I think you should not run these two loops inside one function. Can you take a look at this file here, where I try to do something similar inside two sub-benchmarks?
To answer your question, b.N is adjusted based on how much time the code inside that loop takes to run for one iteration. Therefore it need not be same if you nest two different things inside this loop.
@muskan_sethia The following benchmark runs fine. Set slowInsertions if you don’t want cache to drop anything.
func BenchmarkCacheSetWithRistretto(b *testing.B) {
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 10000000 * 10,
MaxCost: 10000000,
BufferItems: 64,
})
if err != nil {
panic(err)
}
failedSets := 0
// Set slow insertions if you want all the entries to be inserted.
slowInsertions := true
for n := 0; n < b.N; n++ {
if slowInsertions {
time.Sleep(time.Microsecond)
}
key := strconv.Itoa(n%1000000) + strconv.Itoa(n%1000000)
// Cache.Set returns a boolean denoting if the set was successful or
// not. The cache can choose to drop keys in times of high load.
if !cache.SetWithTTL(key, "value", 1, time.Duration(5*time.Second)) {
failedSets++
}
}
// All sets should be succesful if we're inserting slowing.
if slowInsertions {
require.Zero(b, failedSets)
}
time.Sleep(50 * time.Millisecond)
notFoundCount := 0
for n := 0; n < b.N; n++ {
_, found := cache.Get(strconv.Itoa(n%1000000) + strconv.Itoa(n%1000000))
if !found {
notFoundCount++
}
}
require.Equal(b, failedSets, notFoundCount)
}
Thanks, ibrahim. I understand now why the Sets were getting dropped.
slowInsertions works but it essentially delays the number of operations of the Benchmark since this time is also added to the delay. I will see how I can remove that time delay from benchmark results during Set.