mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
gpu: optimize opCache to avoid expensive map lookups
Benchmarking showed that the double map access calls were a bottleneck. Rework the cache to avoid half of them. The simplest, naive approach would have been to store a pointer to a struct with a keep field in the map, allowing cheap update and frame() operation. Benchmarking showed that the increased GC pressure of that approach decreased performance however. Signed-off-by: Viktor <viktor.ogeman@gmail.com>
This commit is contained in:
+53
-24
@@ -14,15 +14,22 @@ type resourceCache struct {
|
|||||||
newRes map[interface{}]resource
|
newRes map[interface{}]resource
|
||||||
}
|
}
|
||||||
|
|
||||||
// opCache is like a resourceCache but using concrete types.
|
// opCache is like a resourceCache but using concrete types and a
|
||||||
|
// freelist instead of two maps to avoid runtime.mapaccess2 calls
|
||||||
|
// since benchmarking showed them as a bottleneck.
|
||||||
type opCache struct {
|
type opCache struct {
|
||||||
res map[ops.Key]opCacheValue
|
// store the index + 1 in cache this key is stored in
|
||||||
newRes map[ops.Key]opCacheValue
|
index map[ops.Key]int
|
||||||
|
// list of indexes in cache that are free and can be used
|
||||||
|
freelist []int
|
||||||
|
cache []opCacheValue
|
||||||
}
|
}
|
||||||
|
|
||||||
type opCacheValue struct {
|
type opCacheValue struct {
|
||||||
data *pathData
|
data *pathData
|
||||||
bounds f32.Rectangle
|
bounds f32.Rectangle
|
||||||
|
key ops.Key
|
||||||
|
keep bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResourceCache() *resourceCache {
|
func newResourceCache() *resourceCache {
|
||||||
@@ -71,41 +78,63 @@ func (r *resourceCache) release() {
|
|||||||
|
|
||||||
func newOpCache() *opCache {
|
func newOpCache() *opCache {
|
||||||
return &opCache{
|
return &opCache{
|
||||||
res: make(map[ops.Key]opCacheValue),
|
index: make(map[ops.Key]int),
|
||||||
newRes: make(map[ops.Key]opCacheValue),
|
freelist: make([]int, 0),
|
||||||
|
cache: make([]opCacheValue, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *opCache) get(key ops.Key) (opCacheValue, bool) {
|
func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) {
|
||||||
v, exists := r.res[key]
|
v := r.index[key]
|
||||||
if exists {
|
if v == 0 {
|
||||||
r.newRes[key] = v
|
return
|
||||||
}
|
}
|
||||||
return v, exists
|
r.cache[v-1].keep = true
|
||||||
|
return r.cache[v-1], true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *opCache) put(key ops.Key, val opCacheValue) {
|
func (r *opCache) put(key ops.Key, val opCacheValue) {
|
||||||
r.res[key] = val
|
v := r.index[key]
|
||||||
r.newRes[key] = val
|
val.keep = true
|
||||||
|
val.key = key
|
||||||
|
if v == 0 {
|
||||||
|
// not in cache
|
||||||
|
i := len(r.cache)
|
||||||
|
if len(r.freelist) > 0 {
|
||||||
|
i = r.freelist[len(r.freelist)-1]
|
||||||
|
r.freelist = r.freelist[:len(r.freelist)-1]
|
||||||
|
r.cache[i] = val
|
||||||
|
} else {
|
||||||
|
r.cache = append(r.cache, val)
|
||||||
|
}
|
||||||
|
r.index[key] = i + 1
|
||||||
|
} else {
|
||||||
|
r.cache[v-1] = val
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *opCache) frame() {
|
func (r *opCache) frame() {
|
||||||
for k, v := range r.res {
|
r.freelist = r.freelist[:0]
|
||||||
if _, exists := r.newRes[k]; !exists {
|
for i, v := range r.cache {
|
||||||
delete(r.res, k)
|
r.cache[i].keep = false
|
||||||
v.data.release()
|
if v.keep {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
if v.data != nil {
|
||||||
for k, v := range r.newRes {
|
v.data.release()
|
||||||
delete(r.newRes, k)
|
r.cache[i].data = nil
|
||||||
r.res[k] = v
|
}
|
||||||
|
delete(r.index, v.key)
|
||||||
|
r.freelist = append(r.freelist, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *opCache) release() {
|
func (r *opCache) release() {
|
||||||
for _, v := range r.newRes {
|
for i := range r.cache {
|
||||||
v.data.release()
|
r.cache[i].keep = false
|
||||||
}
|
}
|
||||||
r.newRes = nil
|
r.frame()
|
||||||
r.res = nil
|
r.index = nil
|
||||||
|
r.freelist = nil
|
||||||
|
r.cache = nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user