You need to sign in before continuing.

Commit b4971936d4c21e459255ce4abc05abeb56e35d7a

Authored by Hayk Martirosyan
1 parent 696e1246

No-wait mode

User can now enable no-wait mode, which chooses whether we use the
EVRUN_NOWAIT flag in ev_run. The default is off, so that we don't
use 100% CPU. Note added in tutorial to enable when performance
is critical. Added to the speed test examples.

Bump to 0.2.1. Remove patch number from HISTORY entry - that's what the
git log is for. Make note on minor release.
CMakeLists.txt
... ... @@ -3,7 +3,7 @@ project(redox)
3 3  
4 4 set(REDOX_VERSION_MAJOR 0)
5 5 set(REDOX_VERSION_MINOR 2)
6   -set(REDOX_VERSION_PATCH 0)
  6 +set(REDOX_VERSION_PATCH 1)
7 7 set(REDOX_VERSION_STRING ${REDOX_VERSION_MAJOR}.${REDOX_VERSION_MINOR}.${REDOX_VERSION_PATCH})
8 8  
9 9 option(lib "Build Redox as a dynamic library." ON)
... ...
HISTORY.md
1 1 # Release History
2 2  
3   -## 0.2.0 (2015-01-31)
  3 +## 0.2 (2015-01-31)
4 4 * Move to vector of strings as input, to handle arbitrary data better and
5 5 improve speed.
6 6 * Add doxygen docs.
7 7 * Move all init code out of constructors into .connect methods.
8 8  
9   -## 0.1.0 (2015-01-27)
  9 +## 0.1 (2015-01-27)
10 10 First versioned release after many nights of segfaults and memory leaks.
11 11  
12   -## 0.0.0 (2014-12-20)
  12 +## 0.0 (2014-12-20)
13 13 Desire to use Redis in C++, but unhappy with existing clients.
... ...
README.md
... ... @@ -41,8 +41,7 @@ local Redis server.
41 41 * `speed_test_sync` over TCP: **21,072 commands/s**
42 42 * `speed_test_sync` over Unix socket: **24,911 commands/s**
43 43  
44   -Results are comparable to that of a mid-range laptop. On a high-end machine, performance
45   -can be much higher.
  44 +A mid-range laptop gives comparable results. Numbers can be much higher on a high-end machine.
46 45  
47 46 ## Tutorial
48 47 This section introduces the main features of redox. Look in `examples/` for more inspiration.
... ... @@ -231,6 +230,12 @@ a vector of strings as needed by its API. `rdx.strToVec("GET foo")`
231 230 will return an `std::vector<std::string>` containing `GET` and `foo`
232 231 as entries. `rdx.vecToStr({"GET", "foo"})` will return the string `GET foo`.
233 232  
  233 +#### No-Wait Mode
  234 +Redox provides a no-wait mode, which tells the event loop not to sleep
  235 +in between processing events. It means that the event thread will run
  236 +at 100% CPU, but it can greatly improve performance when critical. It is
  237 +disabled by default and can be enabled with `rdx.noWait(true);`.
  238 +
234 239 ## Reply types
235 240 These the available template parameters in redox and the Redis
236 241 [return types](http://redis.io/topics/protocol) they can hold.
... ...
examples/lpush_benchmark.cpp
... ... @@ -17,6 +17,7 @@ double time_s() {
17 17 int main(int argc, char* argv[]) {
18 18  
19 19 redox::Redox rdx;
  20 +
20 21 if(!rdx.connect()) return 1;
21 22  
22 23 rdx.del("test");
... ...
examples/speed_test_async.cpp
... ... @@ -19,6 +19,8 @@ double time_s() {
19 19 int main(int argc, char* argv[]) {
20 20  
21 21 Redox rdx;
  22 + rdx.noWait(true);
  23 +
22 24 if(!rdx.connectUnix("/var/run/redis/redis.sock")) return 1;
23 25  
24 26 if(rdx.set("simple_loop:count", "0")) {
... ...
examples/speed_test_async_multi.cpp
... ... @@ -20,6 +20,8 @@ double time_s() {
20 20 int main(int argc, char* argv[]) {
21 21  
22 22 Redox rdx = {cout, redox::log::Debug};
  23 + rdx.noWait(true);
  24 +
23 25 if(!rdx.connect("localhost", 6379)) return 1;
24 26  
25 27 if(rdx.set("simple_loop:count", "0")) {
... ...
examples/speed_test_pubsub.cpp
... ... @@ -14,7 +14,10 @@ double time_s() {
14 14 int main(int argc, char *argv[]) {
15 15  
16 16 Redox rdx_pub;
  17 + rdx_pub.noWait(true);
  18 +
17 19 Subscriber rdx_sub;
  20 + rdx_sub.noWait(true);
18 21  
19 22 if(!rdx_pub.connect()) return 1;
20 23 if(!rdx_sub.connect()) return 1;
... ...
examples/speed_test_sync.cpp
... ... @@ -18,6 +18,8 @@ double time_s() {
18 18 int main(int argc, char* argv[]) {
19 19  
20 20 Redox rdx;
  21 + rdx.noWait(true);
  22 +
21 23 if(!rdx.connect("localhost", 6379)) return 1;
22 24  
23 25 if(rdx.commandSync({"SET", "simple_loop:count", "0"})) {
... ...
include/redox/client.hpp
... ... @@ -82,6 +82,18 @@ public:
82 82 ~Redox();
83 83  
84 84 /**
  85 + * Enables or disables 'no-wait' mode. If enabled, no-wait mode means that the
  86 + * event loop does not pause in between processing events. It can greatly increase
  87 + * the throughput (commands per second),but means that the event thread will run at
  88 + * 100% CPU. Enable when performance is critical and you can spare a core. Default
  89 + * is off.
  90 + *
  91 + * Implementation note: When enabled, the event thread calls libev's ev_run in a
  92 + * loop with the EVRUN_NOWAIT flag.
  93 + */
  94 + void noWait(bool state);
  95 +
  96 + /**
85 97 * Connects to Redis over TCP and starts an event loop in a separate thread. Returns
86 98 * true once everything is ready, or false on failure.
87 99 */
... ... @@ -340,6 +352,9 @@ private:
340 352 // Dynamically allocated libev event loop
341 353 struct ev_loop* evloop_;
342 354  
  355 + // No-wait mode for high-performance
  356 + std::atomic_bool nowait_ = {false};
  357 +
343 358 // Asynchronous watchers
344 359 ev_async watcher_command_; // For processing commands
345 360 ev_async watcher_stop_; // For breaking the loop
... ...
include/redox/subscriber.hpp
... ... @@ -42,6 +42,11 @@ public:
42 42 ~Subscriber();
43 43  
44 44 /**
  45 + * Same as .noWait() on a Redox instance.
  46 + */
  47 + void noWait(bool state) { rdx_.noWait(state); }
  48 +
  49 + /**
45 50 * Same as .connect() on a Redox instance.
46 51 */
47 52 bool connect(
... ...
src/client.cpp
... ... @@ -200,6 +200,12 @@ bool Redox::initHiredis() {
200 200 return true;
201 201 }
202 202  
  203 +void Redox::noWait(bool state) {
  204 + if(state) logger_.info() << "No-wait mode enabled.";
  205 + else logger_.info() << "No-wait mode disabled.";
  206 + nowait_ = state;
  207 +}
  208 +
203 209 void breakEventLoop(struct ev_loop* loop, ev_async* async, int revents) {
204 210 ev_break(loop, EVBREAK_ALL);
205 211 }
... ... @@ -237,12 +243,14 @@ void Redox::runEventLoop() {
237 243 running_ = true;
238 244 running_waiter_.notify_one();
239 245  
240   - // Run the event loop
241   - // TODO this hogs resources, but ev_run(evloop_) without
242   - // the manual loop is slower. Maybe use a CV to run sparsely
243   - // unless there are commands to process?
  246 + // Run the event loop, using NOWAIT if enabled for maximum
  247 + // throughput by avoiding any sleeping
244 248 while (!to_exit_) {
245   - ev_run(evloop_, EVRUN_NOWAIT);
  249 + if(nowait_) {
  250 + ev_run(evloop_, EVRUN_NOWAIT);
  251 + } else {
  252 + ev_run(evloop_);
  253 + }
246 254 }
247 255  
248 256 logger_.info() << "Stop signal detected. Closing down event loop.";
... ...