Skip to content

Commit

Permalink
Merge pull request #653 from upper/refactor-memory-and-speed-optimiza…
Browse files Browse the repository at this point in the history
…tions

Memory and speed optimizations
  • Loading branch information
xiam committed Aug 30, 2022
2 parents 82a1771 + ba90d64 commit 8a3fe0c
Show file tree
Hide file tree
Showing 54 changed files with 1,177 additions and 2,123 deletions.
2 changes: 1 addition & 1 deletion adapter/cockroachdb/database.go
Expand Up @@ -129,7 +129,7 @@ func (*database) CompileStatement(sess sqladapter.Session, stmt *exql.Statement,
}

query, args := sqlbuilder.Preprocess(compiled, args)
query = sqladapter.ReplaceWithDollarSign(query)
query = string(sqladapter.ReplaceWithDollarSign([]byte(query)))
return query, args, nil
}

Expand Down
2 changes: 1 addition & 1 deletion adapter/postgresql/database.go
Expand Up @@ -99,7 +99,7 @@ func (*database) CompileStatement(sess sqladapter.Session, stmt *exql.Statement,
}

query, args := sqlbuilder.Preprocess(compiled, args)
query = sqladapter.ReplaceWithDollarSign(query)
query = string(sqladapter.ReplaceWithDollarSign([]byte(query)))
return query, args, nil
}

Expand Down
2 changes: 1 addition & 1 deletion adapter/ql/database.go
Expand Up @@ -81,7 +81,7 @@ func (*database) CompileStatement(sess sqladapter.Session, stmt *exql.Statement,
}

query, args := sqlbuilder.Preprocess(compiled, args)
query = sqladapter.ReplaceWithDollarSign(query)
query = string(sqladapter.ReplaceWithDollarSign([]byte(query)))
return query, args, nil
}

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Expand Up @@ -11,6 +11,8 @@ require (
github.com/jackc/pgx/v4 v4.15.0
github.com/lib/pq v1.10.4
github.com/mattn/go-sqlite3 v1.14.9
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/segmentio/fasthash v1.0.3
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Expand Up @@ -100,6 +100,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -112,6 +114,8 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
Expand Down
86 changes: 34 additions & 52 deletions internal/cache/cache.go
Expand Up @@ -24,25 +24,21 @@ package cache
import (
"container/list"
"errors"
"fmt"
"strconv"
"sync"

"github.com/upper/db/v4/internal/cache/hashstructure"
)

const defaultCapacity = 128

// Cache holds a map of volatile key -> values.
type Cache struct {
cache map[string]*list.Element
li *list.List
capacity int
keys *list.List
items map[uint64]*list.Element
mu sync.RWMutex
capacity int
}

type item struct {
key string
type cacheItem struct {
key uint64
value interface{}
}

Expand All @@ -52,11 +48,11 @@ func NewCacheWithCapacity(capacity int) (*Cache, error) {
if capacity < 1 {
return nil, errors.New("Capacity must be greater than zero.")
}
return &Cache{
cache: make(map[string]*list.Element),
li: list.New(),
c := &Cache{
capacity: capacity,
}, nil
}
c.init()
return c, nil
}

// NewCache initializes a new caching space with default settings.
Expand All @@ -68,6 +64,11 @@ func NewCache() *Cache {
return c
}

func (c *Cache) init() {
c.items = make(map[uint64]*list.Element)
c.keys = list.New()
}

// Read attempts to retrieve a cached value as a string, if the value does not
// exists returns an empty string and false.
func (c *Cache) Read(h Hashable) (string, bool) {
Expand All @@ -84,33 +85,35 @@ func (c *Cache) Read(h Hashable) (string, bool) {
func (c *Cache) ReadRaw(h Hashable) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
data, ok := c.cache[h.Hash()]

item, ok := c.items[h.Hash()]
if ok {
return data.Value.(*item).value, true
return item.Value.(*cacheItem).value, true
}

return nil, false
}

// Write stores a value in memory. If the value already exists its overwritten.
func (c *Cache) Write(h Hashable, value interface{}) {
key := h.Hash()

c.mu.Lock()
defer c.mu.Unlock()

if el, ok := c.cache[key]; ok {
el.Value.(*item).value = value
c.li.MoveToFront(el)
key := h.Hash()

if item, ok := c.items[key]; ok {
item.Value.(*cacheItem).value = value
c.keys.MoveToFront(item)
return
}

c.cache[key] = c.li.PushFront(&item{key, value})
c.items[key] = c.keys.PushFront(&cacheItem{key, value})

for c.li.Len() > c.capacity {
el := c.li.Remove(c.li.Back())
delete(c.cache, el.(*item).key)
if p, ok := el.(*item).value.(HasOnPurge); ok {
p.OnPurge()
for c.keys.Len() > c.capacity {
item := c.keys.Remove(c.keys.Back()).(*cacheItem)
delete(c.items, item.key)
if p, ok := item.value.(HasOnEvict); ok {
p.OnEvict()
}
}
}
Expand All @@ -120,33 +123,12 @@ func (c *Cache) Write(h Hashable, value interface{}) {
func (c *Cache) Clear() {
c.mu.Lock()
defer c.mu.Unlock()
for _, el := range c.cache {
if p, ok := el.Value.(*item).value.(HasOnPurge); ok {
p.OnPurge()
}
}
c.cache = make(map[string]*list.Element)
c.li.Init()
}

// Hash returns a hash of the given struct.
func Hash(v interface{}) string {
q, err := hashstructure.Hash(v, nil)
if err != nil {
panic(fmt.Sprintf("Could not hash struct: %v", err.Error()))
for _, item := range c.items {
if p, ok := item.Value.(*cacheItem).value.(HasOnEvict); ok {
p.OnEvict()
}
}
return strconv.FormatUint(q, 10)
}

type hash struct {
name string
}

func (h *hash) Hash() string {
return h.name
}

// String returns a Hashable that produces a hash equal to the given string.
func String(s string) Hashable {
return &hash{s}
c.init()
}
14 changes: 12 additions & 2 deletions internal/cache/cache_test.go
Expand Up @@ -23,6 +23,7 @@ package cache

import (
"fmt"
"hash/fnv"
"testing"
)

Expand All @@ -32,8 +33,10 @@ type cacheableT struct {
Name string
}

func (ct *cacheableT) Hash() string {
return Hash(ct)
func (ct *cacheableT) Hash() uint64 {
s := fnv.New64()
s.Sum([]byte(ct.Name))
return s.Sum64()
}

var (
Expand Down Expand Up @@ -77,6 +80,13 @@ func BenchmarkNewCache(b *testing.B) {
}
}

func BenchmarkNewCacheAndClear(b *testing.B) {
for i := 0; i < b.N; i++ {
c := NewCache()
c.Clear()
}
}

func BenchmarkReadNonExistentValue(b *testing.B) {
z := NewCache()
for i := 0; i < b.N; i++ {
Expand Down
109 changes: 109 additions & 0 deletions internal/cache/hash.go
@@ -0,0 +1,109 @@
package cache

import (
"fmt"

"github.com/segmentio/fasthash/fnv1a"
)

const (
hashTypeInt uint64 = 1 << iota
hashTypeSignedInt
hashTypeBool
hashTypeString
hashTypeHashable
hashTypeNil
)

type hasher struct {
t uint64
v interface{}
}

func (h *hasher) Hash() uint64 {
return NewHash(h.t, h.v)
}

func NewHashable(t uint64, v interface{}) Hashable {
return &hasher{t: t, v: v}
}

func InitHash(t uint64) uint64 {
return fnv1a.AddUint64(fnv1a.Init64, t)
}

func NewHash(t uint64, in ...interface{}) uint64 {
return AddToHash(InitHash(t), in...)
}

func AddToHash(h uint64, in ...interface{}) uint64 {
for i := range in {
if in[i] == nil {
continue
}
h = addToHash(h, in[i])
}
return h
}

func addToHash(h uint64, in interface{}) uint64 {
switch v := in.(type) {
case uint64:
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), v)
case uint32:
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
case uint16:
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
case uint8:
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
case uint:
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
case int64:
if v < 0 {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeSignedInt), uint64(-v))
} else {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
}
case int32:
if v < 0 {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeSignedInt), uint64(-v))
} else {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
}
case int16:
if v < 0 {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeSignedInt), uint64(-v))
} else {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
}
case int8:
if v < 0 {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeSignedInt), uint64(-v))
} else {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
}
case int:
if v < 0 {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeSignedInt), uint64(-v))
} else {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeInt), uint64(v))
}
case bool:
if v {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeBool), 1)
} else {
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeBool), 2)
}
case string:
return fnv1a.AddString64(fnv1a.AddUint64(h, hashTypeString), v)
case Hashable:
if in == nil {
panic(fmt.Sprintf("could not hash nil element %T", in))
}
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeHashable), v.Hash())
case nil:
return fnv1a.AddUint64(fnv1a.AddUint64(h, hashTypeNil), 0)
default:
panic(fmt.Sprintf("unsupported value type %T", in))
}
}
21 changes: 0 additions & 21 deletions internal/cache/hashstructure/LICENSE

This file was deleted.

0 comments on commit 8a3fe0c

Please sign in to comment.