1 /**************************************************************************
2 Copyright (c) 2017 sewenew
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 *************************************************************************/
16
17 #ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
18 #define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
19
20 #include <chrono>
21 #include <random>
22 #include <future>
23 #include <algorithm>
24 #include "utils.h"
25
26 namespace sw {
27
28 namespace redis {
29
30 namespace test {
31
32 template <typename RedisInstance>
BenchmarkTest(const BenchmarkOptions & opts,RedisInstance & instance)33 BenchmarkTest<RedisInstance>::BenchmarkTest(const BenchmarkOptions &opts,
34 RedisInstance &instance) : _opts(opts), _redis(instance) {
35 REDIS_ASSERT(_opts.pool_size > 0
36 && _opts.thread_num > 0
37 && _opts.total_request_num > 0
38 && _opts.key_len > 0
39 && _opts.val_len > 0,
40 "Invalid benchmark test options.");
41
42 _keys = _gen_keys();
43 _value = _gen_value();
44 }
45
46 template <typename RedisInstance>
run()47 void BenchmarkTest<RedisInstance>::run() {
48 _cleanup();
49
50 _run("SET key value", [this](std::size_t idx) { this->_redis.set(this->_key(idx), _value); });
51
52 _run("GET key", [this](std::size_t idx) {
53 auto res = this->_redis.get(this->_key(idx));
54 (void)res;
55 });
56
57 _cleanup();
58
59 _run("LPUSH key value", [this](std::size_t idx) {
60 this->_redis.lpush(this->_key(idx), _value);
61 });
62
63 _run("LRANGE key 0 10", [this](std::size_t idx) {
64 std::vector<std::string> res;
65 res.reserve(10);
66 this->_redis.lrange(this->_key(idx), 0, 10, std::back_inserter(res));
67 });
68
69 _run("LPOP key", [this](std::size_t idx) {
70 auto res = this->_redis.lpop(this->_key(idx));
71 (void)res;
72 });
73
74 _cleanup();
75
76 _run("INCR key", [this](std::size_t idx) {
77 auto num = this->_redis.incr(this->_key(idx));
78 (void)num;
79 });
80
81 _cleanup();
82
83 _run("SADD key member", [this](std::size_t idx) {
84 auto num = this->_redis.sadd(this->_key(idx), _value);
85 (void)num;
86 });
87
88 _run("SPOP key", [this](std::size_t idx) {
89 auto res = this->_redis.spop(this->_key(idx));
90 (void)res;
91 });
92
93 _cleanup();
94 }
95
96 template <typename RedisInstance>
97 template <typename Func>
_run(const std::string & title,Func && func)98 void BenchmarkTest<RedisInstance>::_run(const std::string &title, Func &&func) {
99 auto thread_num = _opts.thread_num;
100 auto requests_per_thread = _opts.total_request_num / thread_num;
101 auto total_request_num = requests_per_thread * thread_num;
102 std::vector<std::future<std::size_t>> res;
103 res.reserve(thread_num);
104 res.push_back(std::async(std::launch::async,
105 [this](Func &&func, std::size_t request_num) {
106 return this->_run(std::forward<Func>(func), request_num);
107 },
108 std::forward<Func>(func),
109 requests_per_thread));
110
111 auto total_in_msec = 0;
112 for (auto &fut : res) {
113 total_in_msec += fut.get();
114 }
115
116 auto total_in_sec = total_in_msec * 1.0 / 1000;
117
118 auto avg = total_in_msec * 1.0 / total_request_num;
119
120 auto ops = static_cast<std::size_t>(1000 / avg);
121
122 std::cout << "-----" << title << "-----" << std::endl;
123 std::cout << total_request_num << " requests cost " << total_in_sec << " seconds" << std::endl;
124 std::cout << ops << " requests per second" << std::endl;
125 }
126
127 template <typename RedisInstance>
128 template <typename Func>
_run(Func && func,std::size_t request_num)129 std::size_t BenchmarkTest<RedisInstance>::_run(Func &&func, std::size_t request_num) {
130 auto start = std::chrono::steady_clock::now();
131
132 for (auto idx = 0U; idx != request_num; ++idx) {
133 func(idx);
134 }
135
136 auto stop = std::chrono::steady_clock::now();
137
138 return std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
139 }
140
141 template <typename RedisInstance>
_gen_keys() const142 std::vector<std::string> BenchmarkTest<RedisInstance>::_gen_keys() const {
143 const auto KEY_NUM = 100;
144 std::vector<std::string> res;
145 res.reserve(KEY_NUM);
146 std::default_random_engine engine(std::random_device{}());
147 std::uniform_int_distribution<int> uniform_dist(0, 255);
148 for (auto i = 0; i != KEY_NUM; ++i) {
149 std::string str;
150 str.reserve(_opts.key_len);
151 for (std::size_t j = 0; j != _opts.key_len; ++j) {
152 str.push_back(static_cast<char>(uniform_dist(engine)));
153 }
154 res.push_back(str);
155 }
156
157 return res;
158 }
159
160 template <typename RedisInstance>
_gen_value() const161 std::string BenchmarkTest<RedisInstance>::_gen_value() const {
162 return std::string(_opts.val_len, 'x');
163 }
164
165 template <typename RedisInstance>
_cleanup()166 void BenchmarkTest<RedisInstance>::_cleanup() {
167 for (const auto &key : _keys) {
168 _redis.del(key);
169 }
170 }
171
172 }
173
174 }
175
176 }
177
178 #endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
179