MEG-2953: Implemented a faster version of the Size() function.

This commit is contained in:
AlexisWilke 2020-09-17 17:51:42 -07:00
parent 46f4078530
commit fd5be3e9b1
2 changed files with 122 additions and 0 deletions

View File

@ -8,6 +8,7 @@ import (
"runtime"
"sync"
"time"
"unsafe"
)
type Item struct {
@ -1034,6 +1035,57 @@ func (c *cache) LoadFile(fname string) error {
return fp.Close()
}
// Return current size of all values
// WARNING: this function assumes all values can be converted to a buffer
// of bytes to gather their length
func (c *cache) Size() (size int) {
c.mu.RLock()
defer c.mu.RUnlock()
size = 0
now := time.Now().UnixNano()
for _, v := range c.items {
// "Inlining" of Expired
if v.Expiration > 0 && now > v.Expiration {
continue
}
switch value := v.Object.(type) {
case bool:
size += int(unsafe.Sizeof(value))
case int:
size += int(unsafe.Sizeof(value))
case int8:
size += int(unsafe.Sizeof(value))
case int16:
size += int(unsafe.Sizeof(value))
case int32: // rune
size += int(unsafe.Sizeof(value))
case int64:
size += int(unsafe.Sizeof(value))
case uint:
size += int(unsafe.Sizeof(value))
case uint8: // byte
size += int(unsafe.Sizeof(value))
case uint16:
size += int(unsafe.Sizeof(value))
case uint32:
size += int(unsafe.Sizeof(value))
case uint64:
size += int(unsafe.Sizeof(value))
case uintptr:
size += int(unsafe.Sizeof(value))
case float32:
size += int(unsafe.Sizeof(value))
case float64:
size += int(unsafe.Sizeof(value))
case string:
size += int(unsafe.Sizeof(value))
size += len(value)
}
}
return
}
// Copies all unexpired items in the cache into a new map and returns it.
func (c *cache) Items() map[string]Item {
c.mu.RLock()

View File

@ -2,12 +2,15 @@ package cache
import (
"bytes"
"fmt"
"io/ioutil"
"math/rand"
"runtime"
"strconv"
"sync"
"testing"
"time"
"unsafe"
)
type TestStruct struct {
@ -66,6 +69,17 @@ func TestCache(t *testing.T) {
} else if c2 := x.(float64); c2+1.2 != 4.7 {
t.Error("c2 (which should be 3.5) plus 1.2 does not equal 4.7; value:", c2)
}
// in Go we have to have a variable with a type to be able to get
// the size of the type
var a_size = 1
var b_size = "b"
var c_size = 3.5
size := tc.Size()
expected_size := int(unsafe.Sizeof(a_size)) + int(unsafe.Sizeof(b_size)) + len(b_size) + int(unsafe.Sizeof(c_size))
if size != expected_size {
t.Error("size (which should be", expected_size, "); value:", size)
}
}
func TestCacheTimes(t *testing.T) {
@ -1769,3 +1783,59 @@ func TestGetWithExpiration(t *testing.T) {
t.Error("expiration for e is in the past")
}
}
// countBytes from the KVStores
func countBytes(items map[string]Item) int64 {
sum := int64(0)
for _, v := range items {
if buff, ok := v.Object.([]byte); ok {
sum += int64(len(buff))
}
}
return sum
}
func TestSizeSpeedTest(t *testing.T) {
if testing.Short() {
t.Skip()
fmt.Println("warning: Skipping slow TestSizeSpeed() -- do not use `-short` to not skip this speed tests.")
return
}
tc := New(DefaultExpiration, 0)
fmt.Print("Generate large cache ")
for i := 0; i < 100000; i++ {
if i % 1000 == 999 {
fmt.Print(".")
}
key := ""
key_size := rand.Uint32() % 15 + 5
for j := uint32(0); j < key_size; j++ {
key += string(rune(rand.Uint32() % 26 + 65))
}
value := ""
value_size := rand.Uint32() % 55 + 5
for j := uint32(0); j < value_size; j++ {
value += string(rune(rand.Uint32() % 26 + 65))
}
tc.Set(key, value, DefaultExpiration)
}
fmt.Println()
fmt.Print("Time 1,000 Size() calls: ")
start := time.Now()
for i := 0; i < 1000; i++ {
tc.Size()
}
end := time.Now()
fmt.Println("Size() x 1,000 took:", end.Sub(start))
fmt.Print("Time 1,000 countBytes(Items()) calls: ")
start = time.Now()
for i := 0; i < 1000; i++ {
countBytes(tc.Items())
}
end = time.Now()
fmt.Println("Size() x 1,000 took:", end.Sub(start))
}