zcache icon indicating copy to clipboard operation
zcache copied to clipboard

Multi Get and Multi Set

Open Winnetoe24 opened this issue 2 years ago • 11 comments

Winnetoe24 avatar Dec 13 '23 16:12 Winnetoe24

Is there any reason in particular you used two slices in MultiGet() rather than a slice with a struct? It seems to me that's quite a bit more convenient, and this can also work for setting multiple keys:

func (c *cache[K, V]) MultiSet(vals ...struct {
	k K
	v V
}) {
	c.mu.Lock()
	for _, v := range vals {
		c.set(v.k, v.v, c.defaultExpiration)
	}
	c.mu.Unlock()
}

arp242 avatar Dec 13 '23 16:12 arp242

i personaly found the resuling code, that uses the function more readable. But this is personal preference. I can rework this with structs if you like

Winnetoe24 avatar Dec 13 '23 16:12 Winnetoe24

It seems to me that juggling around two variables for one thing is rather awkward. Then again I also don't think my version is super-great either (it was just the quickest to write – I mostly wanted to compare performance to see if it's worth it after our Twitter chat).

Since I really want to avoid introducing incompatible changes in the future I'd like to get this right – I need to play around and think a bit to see what works, so let me get back to you.

arp242 avatar Dec 14 '23 01:12 arp242

Also: there's quite a few other operations where it makes sense to operate on more than one key. Why do Get() and Set() have "Multi versions" but not Add() or Delete() or Touch() or GetWithExpire() or any of the others?

Adding Multi-variants for each one would be a bit much, but I'm wonder if there's nice way to make a more generic API that extends to more operations – for example maybe something like (and just thinking out loud here):

// Returns []struct{..} or two slices.
vals := mycache.Multi(key1, key2).Get()

mycache.Multi(key1, key2).Delete()

mycache.Multi(key1, key2).Set("val1", "val2")

mycache.Multi(key1, key2).SetWithExpire(expire, "val1", "val2")

mycache.Multi(key1, key2).Rename("newname1", "newname2")

Implementation is more work, but I don't mind doing the work on that. Getting the API right is what I'm most concerned with.

arp242 avatar Dec 14 '23 01:12 arp242

That might be a good idea. I tested 2 return types: ([]V, []bool), []struct{v V, exists bool} in benchmarks. I modified your benchmark. The return the get-method is iterated over and if a value doesn't exists time.Sleep(time.Nanosecond) is used to simulate loading the value and the value is set to "".

goos: windows
goarch: amd64
pkg: zgo.at/zcache/v2
cpu: AMD Ryzen 7 5700U with Radeon Graphics
BenchmarkGetMulti/get
BenchmarkGetMulti/get-16                       2        6196558400 ns/op
BenchmarkGetMulti/mget-16                      2        6155529550 ns/op
BenchmarkGetMulti/mgetStruct-16                4        2641001275 ns/op
PASS

As you can see, my idea with 2 slices completely ruins the performance, therefore the struct version should be the right return type.

Winnetoe24 avatar Dec 14 '23 08:12 Winnetoe24

Hm, I get quite different benchmark results?

BenchmarkGetMulti/get-8                    62580             17229 ns/op               0 B/op          0 allocs/op
BenchmarkGetMulti/mget-8                  112527             12081 ns/op           16384 B/op          1 allocs/op
BenchmarkGetMulti/mget2-8                 114786             10299 ns/op           10368 B/op          2 allocs/op

I put the benchmark in https://github.com/arp242/zcache/tree/mget

My benchmark is a lot simpler though.

arp242 avatar Dec 14 '23 23:12 arp242

Your Benchmark only tests the method and misses the access of the method retrieved. I find this important since you normally iterate through the information and load information the cache missed. My Tests simulates the load through time.sleep. Depending on the result time, the access of the data differs. My results might also be influenced by my cpu since it's a 15w Laptop CPU, which ran on battery.

Winnetoe24 avatar Dec 16 '23 15:12 Winnetoe24

I will further test tomorrow

Winnetoe24 avatar Dec 16 '23 15:12 Winnetoe24

I will further test tomorrow

It's fine, don't bother – it's not hugely important.

arp242 avatar Dec 17 '23 03:12 arp242

This seems to work reasonably well: https://github.com/arp242/zcache/pull/5 – what do you think?

Keyset.Set() is up to ~60% faster, and Keyset.Get() up to ~40%.

Need to finish with some extra tests and documentation, and maybe also some extra methods.

Also not entirely sure about the "Keyset" name; but I couldn't think of anything better.

arp242 avatar Jan 03 '24 14:01 arp242

i will look at it this weekend

Winnetoe24 avatar Jan 11 '24 15:01 Winnetoe24