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