Skip to content

Commit 2157586

Browse files
author
LeeGunhee
committed
performance enhancement
1 parent cf7bc37 commit 2157586

File tree

6 files changed

+458
-27
lines changed

6 files changed

+458
-27
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package cachemap2
2+
3+
import (
4+
"github.com/emirpasic/gods/lists/singlylinkedlist"
5+
"strconv"
6+
"sync"
7+
"time"
8+
)
9+
10+
var lock sync.RWMutex
11+
var once sync.Once
12+
13+
type CacheMap struct {
14+
table map[interface{}]interface{}
15+
ordering *singlylinkedlist.List
16+
orderPos map[interface{}]int
17+
maxSize int
18+
}
19+
20+
func (m *CacheMap) GetValues() []interface{} {
21+
l := make([]interface{}, 0, int(float32(len(m.table)) * 1.2))
22+
lock.RLock()
23+
defer lock.RUnlock()
24+
for _, v := range m.table {
25+
lock.RUnlock()
26+
if v != nil {
27+
l = append(l, v)
28+
}
29+
lock.RLock()
30+
}
31+
return l
32+
}
33+
34+
func New(maxSize int) *CacheMap {
35+
m := &CacheMap{
36+
table: make(map[interface{}]interface{}),
37+
ordering: singlylinkedlist.New(),
38+
orderPos: make(map[interface{}]int),
39+
maxSize: maxSize,
40+
}
41+
once.Do(func() {
42+
go func() {
43+
for {
44+
if m.ordering.Size() > m.maxSize/2 {
45+
time.Sleep(500 * time.Millisecond)
46+
//TODO
47+
} else {
48+
time.Sleep(100 * time.Millisecond)
49+
//TODO
50+
}
51+
}
52+
}()
53+
})
54+
return m
55+
}
56+
57+
func (m *CacheMap) Add(key interface{}, item interface{}) {
58+
lock.Lock()
59+
defer lock.Unlock()
60+
if _, contains := m.table[key]; !contains {
61+
m.removeExceeded()
62+
m.table[key] = item
63+
m.ordering.Append(key)
64+
m.orderPos[key] = m.ordering.Size() - 1
65+
}
66+
}
67+
68+
func (m *CacheMap) Remove(key interface{}) {
69+
lock.Lock()
70+
defer lock.Unlock()
71+
delete(m.table, key)
72+
//index, contains := m.orderPos[key]
73+
//if contains {
74+
// m.ordering.Remove(index)
75+
// delete(m.orderPos, key)
76+
//}
77+
}
78+
79+
func (m *CacheMap) removeExceeded() {
80+
for m.ordering.Size() >= m.maxSize {
81+
key, exist := m.ordering.Get(0)
82+
if exist {
83+
m.ordering.Remove(0)
84+
delete(m.orderPos, key)
85+
delete(m.table, key)
86+
}
87+
}
88+
}
89+
90+
func (m *CacheMap) Contains(key interface{}) bool {
91+
lock.RLock()
92+
defer lock.RUnlock()
93+
if _, contains := m.table[key]; !contains {
94+
return false
95+
}
96+
return true
97+
}
98+
99+
func (m *CacheMap) Get(key interface{}) interface{} {
100+
lock.RLock()
101+
defer lock.RUnlock()
102+
return m.table[key]
103+
}
104+
105+
func (m *CacheMap) Empty() bool {
106+
lock.Lock()
107+
defer lock.Unlock()
108+
return m.Size() == 0
109+
}
110+
111+
func (m *CacheMap) Size() int {
112+
return m.ordering.Size()
113+
}
114+
115+
func (m *CacheMap) Clear() {
116+
lock.Lock()
117+
defer lock.Unlock()
118+
m.table = make(map[interface{}]interface{})
119+
m.ordering.Clear()
120+
m.orderPos = make(map[interface{}]int)
121+
}
122+
123+
func (m *CacheMap) String() string {
124+
return "CacheMap[" + strconv.Itoa(m.ordering.Size()) + "]"
125+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package cachemap2
2+
3+
import (
4+
"strconv"
5+
"testing"
6+
)
7+
8+
func TestCacheSet(t *testing.T) {
9+
cacheMap := New(10)
10+
11+
//add "1" to "9"
12+
for i := 0; i < 10; i++ {
13+
cacheMap.Add(i, strconv.Itoa(i))
14+
}
15+
16+
if cacheMap.Size() != 10 {
17+
t.Error("size error.")
18+
}
19+
20+
//max size preserved
21+
cacheMap.Add(3, "3")
22+
if cacheMap.Size() != 10 {
23+
t.Error("size error.")
24+
}
25+
26+
//0 exist
27+
if (!cacheMap.Contains(0)) {
28+
t.Error("contains error.")
29+
}
30+
31+
//0 removed
32+
cacheMap.Add(1000, "1000")
33+
if cacheMap.Size() != 10 {
34+
t.Error("size error.")
35+
}
36+
37+
if cacheMap.Contains(0) {
38+
t.Error("contains error: 0 was removed.")
39+
}
40+
41+
//1 exist
42+
if !cacheMap.Contains(1) {
43+
t.Error("contains error: 1 should be exist.")
44+
}
45+
46+
//1 removed
47+
cacheMap.Add(1001, "1001")
48+
if cacheMap.Size() != 10 {
49+
t.Error("size error.")
50+
}
51+
52+
if cacheMap.Contains(1) {
53+
t.Error("contains error: 1 was removed.")
54+
}
55+
56+
if !cacheMap.Contains(1001) {
57+
t.Error("contains error: 1001 should be exist.")
58+
}
59+
60+
if cacheMap.Get(1001) != "1001" {
61+
t.Error("no matched value.")
62+
}
63+
}
64+
65+
//func TestValues(t *testing.T) {
66+
// cacheMap := New(9000)
67+
//
68+
// go func() {
69+
// for {
70+
// time.Sleep(10 * time.Nanosecond)
71+
// rand := rand.Intn(30000)
72+
// cacheMap.Add(rand, rand)
73+
// }
74+
// }()
75+
//
76+
// go func() {
77+
// for {
78+
// values := cacheMap.GetValues()
79+
// fmt.Println(strconv.Itoa(len(values)))
80+
// time.Sleep(10 * time.Millisecond)
81+
// }
82+
// }()
83+
//
84+
// time.Sleep(2000 * time.Millisecond)
85+
//}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package lra
2+
3+
import (
4+
"container/list"
5+
"sync"
6+
)
7+
8+
var lock sync.RWMutex
9+
var itemExists = struct{}{}
10+
11+
type Cache struct {
12+
MaxEntries int
13+
OnEvicted func(key Key, value interface{})
14+
15+
lst *list.List
16+
table map[interface{}]*list.Element
17+
}
18+
19+
type Key interface{}
20+
21+
type entry struct {
22+
key Key
23+
value interface{}
24+
}
25+
26+
func New(maxEntries int) *Cache {
27+
return &Cache{
28+
MaxEntries: maxEntries,
29+
lst: list.New(),
30+
table: make(map[interface{}]*list.Element),
31+
}
32+
}
33+
34+
func (c *Cache) AddKey(key Key) {
35+
c.Add(key, itemExists)
36+
}
37+
38+
func (c *Cache) Add(key Key, value interface{}) {
39+
lock.Lock()
40+
defer lock.Unlock()
41+
if ee, ok := c.table[key]; ok {
42+
ee.Value.(*entry).value = value
43+
return
44+
}
45+
ele := c.lst.PushFront(&entry{key, value})
46+
c.table[key] = ele
47+
if c.MaxEntries != 0 && c.lst.Len() > c.MaxEntries {
48+
c.removeOldest()
49+
}
50+
}
51+
52+
// Get looks up a key's value from the cache.
53+
func (c *Cache) Get(key Key) interface{} {
54+
lock.RLock()
55+
defer lock.RUnlock()
56+
if ele, contains := c.table[key]; contains {
57+
return ele.Value.(*entry).value
58+
}
59+
return nil
60+
}
61+
62+
func (c *Cache) Contains(key interface{}) bool {
63+
lock.RLock()
64+
defer lock.RUnlock()
65+
if _, contains := c.table[key]; !contains {
66+
return false
67+
}
68+
return true
69+
}
70+
71+
func (c *Cache) Empty() bool {
72+
lock.RLock()
73+
defer lock.RUnlock()
74+
return c.Size() == 0
75+
}
76+
77+
78+
func (c *Cache) Remove(key Key) {
79+
lock.Lock()
80+
defer lock.Unlock()
81+
if ele, hit := c.table[key]; hit {
82+
c.removeElement(ele)
83+
}
84+
}
85+
86+
func (c *Cache) removeOldest() {
87+
ele := c.lst.Back()
88+
if ele != nil {
89+
c.removeElement(ele)
90+
}
91+
}
92+
93+
func (c *Cache) removeElement(e *list.Element) {
94+
c.lst.Remove(e)
95+
kv := e.Value.(*entry)
96+
delete(c.table, kv.key)
97+
if c.OnEvicted != nil {
98+
c.OnEvicted(kv.key, kv.value)
99+
}
100+
}
101+
102+
func (c *Cache) Size() int {
103+
lock.RLock()
104+
defer lock.RUnlock()
105+
return c.lst.Len()
106+
}
107+
108+
func (c *Cache) Clear() {
109+
lock.Lock()
110+
defer lock.Unlock()
111+
if c.OnEvicted != nil {
112+
for _, e := range c.table {
113+
kv := e.Value.(*entry)
114+
c.OnEvicted(kv.key, kv.value)
115+
}
116+
}
117+
c.lst = nil
118+
c.table = nil
119+
}
120+
121+
func (c *Cache) GetValues() []interface{} {
122+
l := make([]interface{}, 0, int(float32(c.lst.Len()) * 1.2))
123+
lock.RLock()
124+
defer lock.RUnlock()
125+
for _, e := range c.table {
126+
lock.RUnlock()
127+
if e != nil {
128+
kv := e.Value.(*entry)
129+
if kv != nil && kv.value != nil {
130+
l = append(l, kv.value)
131+
}
132+
}
133+
lock.RLock()
134+
}
135+
return l
136+
}

0 commit comments

Comments
 (0)