1// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package store
16
17import (
18	"encoding/json"
19	"fmt"
20	"runtime"
21	"testing"
22)
23
24func BenchmarkStoreSet128Bytes(b *testing.B) {
25	benchStoreSet(b, 128, nil)
26}
27
28func BenchmarkStoreSet1024Bytes(b *testing.B) {
29	benchStoreSet(b, 1024, nil)
30}
31
32func BenchmarkStoreSet4096Bytes(b *testing.B) {
33	benchStoreSet(b, 4096, nil)
34}
35
36func BenchmarkStoreSetWithJson128Bytes(b *testing.B) {
37	benchStoreSet(b, 128, json.Marshal)
38}
39
40func BenchmarkStoreSetWithJson1024Bytes(b *testing.B) {
41	benchStoreSet(b, 1024, json.Marshal)
42}
43
44func BenchmarkStoreSetWithJson4096Bytes(b *testing.B) {
45	benchStoreSet(b, 4096, json.Marshal)
46}
47
48func BenchmarkStoreDelete(b *testing.B) {
49	b.StopTimer()
50
51	s := newStore()
52	kvs, _ := generateNRandomKV(b.N, 128)
53
54	memStats := new(runtime.MemStats)
55	runtime.GC()
56	runtime.ReadMemStats(memStats)
57
58	for i := 0; i < b.N; i++ {
59		_, err := s.Set(kvs[i][0], false, kvs[i][1], TTLOptionSet{ExpireTime: Permanent})
60		if err != nil {
61			panic(err)
62		}
63	}
64
65	setMemStats := new(runtime.MemStats)
66	runtime.GC()
67	runtime.ReadMemStats(setMemStats)
68
69	b.StartTimer()
70
71	for i := range kvs {
72		s.Delete(kvs[i][0], false, false)
73	}
74
75	b.StopTimer()
76
77	// clean up
78	e, err := s.Get("/", false, false)
79	if err != nil {
80		panic(err)
81	}
82
83	for _, n := range e.Node.Nodes {
84		_, err := s.Delete(n.Key, true, true)
85		if err != nil {
86			panic(err)
87		}
88	}
89	s.WatcherHub.EventHistory = nil
90
91	deleteMemStats := new(runtime.MemStats)
92	runtime.GC()
93	runtime.ReadMemStats(deleteMemStats)
94
95	fmt.Printf("\nBefore set Alloc: %v; After set Alloc: %v, After delete Alloc: %v\n",
96		memStats.Alloc/1000, setMemStats.Alloc/1000, deleteMemStats.Alloc/1000)
97}
98
99func BenchmarkWatch(b *testing.B) {
100	b.StopTimer()
101	s := newStore()
102	kvs, _ := generateNRandomKV(b.N, 128)
103	b.StartTimer()
104
105	memStats := new(runtime.MemStats)
106	runtime.GC()
107	runtime.ReadMemStats(memStats)
108
109	for i := 0; i < b.N; i++ {
110		w, _ := s.Watch(kvs[i][0], false, false, 0)
111
112		e := newEvent("set", kvs[i][0], uint64(i+1), uint64(i+1))
113		s.WatcherHub.notify(e)
114		<-w.EventChan()
115		s.CurrentIndex++
116	}
117
118	s.WatcherHub.EventHistory = nil
119	afterMemStats := new(runtime.MemStats)
120	runtime.GC()
121	runtime.ReadMemStats(afterMemStats)
122	fmt.Printf("\nBefore Alloc: %v; After Alloc: %v\n",
123		memStats.Alloc/1000, afterMemStats.Alloc/1000)
124}
125
126func BenchmarkWatchWithSet(b *testing.B) {
127	b.StopTimer()
128	s := newStore()
129	kvs, _ := generateNRandomKV(b.N, 128)
130	b.StartTimer()
131
132	for i := 0; i < b.N; i++ {
133		w, _ := s.Watch(kvs[i][0], false, false, 0)
134
135		s.Set(kvs[i][0], false, "test", TTLOptionSet{ExpireTime: Permanent})
136		<-w.EventChan()
137	}
138}
139
140func BenchmarkWatchWithSetBatch(b *testing.B) {
141	b.StopTimer()
142	s := newStore()
143	kvs, _ := generateNRandomKV(b.N, 128)
144	b.StartTimer()
145
146	watchers := make([]Watcher, b.N)
147
148	for i := 0; i < b.N; i++ {
149		watchers[i], _ = s.Watch(kvs[i][0], false, false, 0)
150	}
151
152	for i := 0; i < b.N; i++ {
153		s.Set(kvs[i][0], false, "test", TTLOptionSet{ExpireTime: Permanent})
154	}
155
156	for i := 0; i < b.N; i++ {
157		<-watchers[i].EventChan()
158	}
159
160}
161
162func BenchmarkWatchOneKey(b *testing.B) {
163	s := newStore()
164	watchers := make([]Watcher, b.N)
165
166	for i := 0; i < b.N; i++ {
167		watchers[i], _ = s.Watch("/foo", false, false, 0)
168	}
169
170	s.Set("/foo", false, "", TTLOptionSet{ExpireTime: Permanent})
171
172	for i := 0; i < b.N; i++ {
173		<-watchers[i].EventChan()
174	}
175}
176
177func benchStoreSet(b *testing.B, valueSize int, process func(interface{}) ([]byte, error)) {
178	s := newStore()
179	b.StopTimer()
180	kvs, size := generateNRandomKV(b.N, valueSize)
181	b.StartTimer()
182
183	for i := 0; i < b.N; i++ {
184		resp, err := s.Set(kvs[i][0], false, kvs[i][1], TTLOptionSet{ExpireTime: Permanent})
185		if err != nil {
186			panic(err)
187		}
188
189		if process != nil {
190			_, err = process(resp)
191			if err != nil {
192				panic(err)
193			}
194		}
195	}
196
197	kvs = nil
198	b.StopTimer()
199	memStats := new(runtime.MemStats)
200	runtime.GC()
201	runtime.ReadMemStats(memStats)
202	fmt.Printf("\nAlloc: %vKB; Data: %vKB; Kvs: %v; Alloc/Data:%v\n",
203		memStats.Alloc/1000, size/1000, b.N, memStats.Alloc/size)
204}
205
206func generateNRandomKV(n int, valueSize int) ([][]string, uint64) {
207	var size uint64
208	kvs := make([][]string, n)
209	bytes := make([]byte, valueSize)
210
211	for i := 0; i < n; i++ {
212		kvs[i] = make([]string, 2)
213		kvs[i][0] = fmt.Sprintf("/%010d/%010d/%010d", n, n, n)
214		kvs[i][1] = string(bytes)
215		size = size + uint64(len(kvs[i][0])) + uint64(len(kvs[i][1]))
216	}
217
218	return kvs, size
219}
220