From 6bf5d4dc2d002b196d6006185b4358ec0f988131 Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Sat, 2 Jul 2022 19:16:19 +0300 Subject: [PATCH] gpu: optimize resourceCache By keeping all the information in a single map, we avoid multiple lookups and can switch between frames more easily. Before ~35us, after ~20us for adding 50 new+old and switching the frame. Signed-off-by: Egon Elbre --- gpu/caches.go | 44 +++++++++++++++++++++++++------------------- gpu/caches_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 gpu/caches_test.go diff --git a/gpu/caches.go b/gpu/caches.go index 762f3e77..b44f7ead 100644 --- a/gpu/caches.go +++ b/gpu/caches.go @@ -9,8 +9,12 @@ import ( ) type resourceCache struct { - res map[interface{}]resource - newRes map[interface{}]resource + res map[interface{}]resourceCacheValue +} + +type resourceCacheValue struct { + used bool + resource resource } // opCache is like a resourceCache but using concrete types and a @@ -35,46 +39,48 @@ type opCacheValue struct { func newResourceCache() *resourceCache { return &resourceCache{ - res: make(map[interface{}]resource), - newRes: make(map[interface{}]resource), + res: make(map[interface{}]resourceCacheValue), } } func (r *resourceCache) get(key interface{}) (resource, bool) { v, exists := r.res[key] - if exists { - r.newRes[key] = v + if !exists { + return nil, false } - return v, exists + if !v.used { + v.used = true + r.res[key] = v + } + return v.resource, exists } func (r *resourceCache) put(key interface{}, val resource) { - if _, exists := r.newRes[key]; exists { + v, exists := r.res[key] + if exists && v.used { panic(fmt.Errorf("key exists, %p", key)) } - r.res[key] = val - r.newRes[key] = val + v.used = true + v.resource = val + r.res[key] = v } func (r *resourceCache) frame() { for k, v := range r.res { - if _, exists := r.newRes[k]; !exists { + if v.used { + v.used = false + r.res[k] = v + } else { delete(r.res, k) - v.release() + v.resource.release() } } - for k, v := range r.newRes { - delete(r.newRes, k) - r.res[k] = v - } } func (r *resourceCache) release() { - r.frame() for _, v := range r.res { - v.release() + v.resource.release() } - r.newRes = nil r.res = nil } diff --git a/gpu/caches_test.go b/gpu/caches_test.go new file mode 100644 index 00000000..411de5af --- /dev/null +++ b/gpu/caches_test.go @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gpu + +import "testing" + +func BenchmarkResourceCache(b *testing.B) { + offset := 0 + const N = 100 + + cache := newResourceCache() + for i := 0; i < b.N; i++ { + // half are the same and half updated + for k := 0; k < N; k++ { + cache.put(offset+k, nullResource{}) + } + cache.frame() + offset += N / 2 + } +} + +type nullResource struct{} + +func (nullResource) release() {}