mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
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 <egonelbre@gmail.com>
This commit is contained in:
+25
-19
@@ -9,8 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type resourceCache struct {
|
type resourceCache struct {
|
||||||
res map[interface{}]resource
|
res map[interface{}]resourceCacheValue
|
||||||
newRes map[interface{}]resource
|
}
|
||||||
|
|
||||||
|
type resourceCacheValue struct {
|
||||||
|
used bool
|
||||||
|
resource resource
|
||||||
}
|
}
|
||||||
|
|
||||||
// opCache is like a resourceCache but using concrete types and a
|
// opCache is like a resourceCache but using concrete types and a
|
||||||
@@ -35,46 +39,48 @@ type opCacheValue struct {
|
|||||||
|
|
||||||
func newResourceCache() *resourceCache {
|
func newResourceCache() *resourceCache {
|
||||||
return &resourceCache{
|
return &resourceCache{
|
||||||
res: make(map[interface{}]resource),
|
res: make(map[interface{}]resourceCacheValue),
|
||||||
newRes: make(map[interface{}]resource),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceCache) get(key interface{}) (resource, bool) {
|
func (r *resourceCache) get(key interface{}) (resource, bool) {
|
||||||
v, exists := r.res[key]
|
v, exists := r.res[key]
|
||||||
if exists {
|
if !exists {
|
||||||
r.newRes[key] = v
|
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) {
|
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))
|
panic(fmt.Errorf("key exists, %p", key))
|
||||||
}
|
}
|
||||||
r.res[key] = val
|
v.used = true
|
||||||
r.newRes[key] = val
|
v.resource = val
|
||||||
|
r.res[key] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceCache) frame() {
|
func (r *resourceCache) frame() {
|
||||||
for k, v := range r.res {
|
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)
|
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() {
|
func (r *resourceCache) release() {
|
||||||
r.frame()
|
|
||||||
for _, v := range r.res {
|
for _, v := range r.res {
|
||||||
v.release()
|
v.resource.release()
|
||||||
}
|
}
|
||||||
r.newRes = nil
|
|
||||||
r.res = nil
|
r.res = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {}
|
||||||
Reference in New Issue
Block a user