From b4971936d4c21e459255ce4abc05abeb56e35d7a Mon Sep 17 00:00:00 2001 From: Hayk Martirosyan Date: Wed, 25 Feb 2015 13:06:43 -0800 Subject: [PATCH] No-wait mode --- CMakeLists.txt | 2 +- HISTORY.md | 6 +++--- README.md | 9 +++++++-- examples/lpush_benchmark.cpp | 1 + examples/speed_test_async.cpp | 2 ++ examples/speed_test_async_multi.cpp | 2 ++ examples/speed_test_pubsub.cpp | 3 +++ examples/speed_test_sync.cpp | 2 ++ include/redox/client.hpp | 15 +++++++++++++++ include/redox/subscriber.hpp | 5 +++++ src/client.cpp | 18 +++++++++++++----- 11 files changed, 54 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e21ad02..b009488 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(redox) set(REDOX_VERSION_MAJOR 0) set(REDOX_VERSION_MINOR 2) -set(REDOX_VERSION_PATCH 0) +set(REDOX_VERSION_PATCH 1) set(REDOX_VERSION_STRING ${REDOX_VERSION_MAJOR}.${REDOX_VERSION_MINOR}.${REDOX_VERSION_PATCH}) option(lib "Build Redox as a dynamic library." ON) diff --git a/HISTORY.md b/HISTORY.md index 18c244c..e85239c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,13 +1,13 @@ # Release History -## 0.2.0 (2015-01-31) +## 0.2 (2015-01-31) * Move to vector of strings as input, to handle arbitrary data better and improve speed. * Add doxygen docs. * Move all init code out of constructors into .connect methods. -## 0.1.0 (2015-01-27) +## 0.1 (2015-01-27) First versioned release after many nights of segfaults and memory leaks. -## 0.0.0 (2014-12-20) +## 0.0 (2014-12-20) Desire to use Redis in C++, but unhappy with existing clients. diff --git a/README.md b/README.md index 781b6e6..01f5969 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,7 @@ local Redis server. * `speed_test_sync` over TCP: **21,072 commands/s** * `speed_test_sync` over Unix socket: **24,911 commands/s** -Results are comparable to that of a mid-range laptop. On a high-end machine, performance -can be much higher. +A mid-range laptop gives comparable results. Numbers can be much higher on a high-end machine. ## Tutorial 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")` will return an `std::vector` containing `GET` and `foo` as entries. `rdx.vecToStr({"GET", "foo"})` will return the string `GET foo`. +#### No-Wait Mode +Redox provides a no-wait mode, which tells the event loop not to sleep +in between processing events. It means that the event thread will run +at 100% CPU, but it can greatly improve performance when critical. It is +disabled by default and can be enabled with `rdx.noWait(true);`. + ## Reply types These the available template parameters in redox and the Redis [return types](http://redis.io/topics/protocol) they can hold. diff --git a/examples/lpush_benchmark.cpp b/examples/lpush_benchmark.cpp index ec99b2c..9431286 100644 --- a/examples/lpush_benchmark.cpp +++ b/examples/lpush_benchmark.cpp @@ -17,6 +17,7 @@ double time_s() { int main(int argc, char* argv[]) { redox::Redox rdx; + if(!rdx.connect()) return 1; rdx.del("test"); diff --git a/examples/speed_test_async.cpp b/examples/speed_test_async.cpp index f375384..f4ca65f 100644 --- a/examples/speed_test_async.cpp +++ b/examples/speed_test_async.cpp @@ -19,6 +19,8 @@ double time_s() { int main(int argc, char* argv[]) { Redox rdx; + rdx.noWait(true); + if(!rdx.connectUnix("/var/run/redis/redis.sock")) return 1; if(rdx.set("simple_loop:count", "0")) { diff --git a/examples/speed_test_async_multi.cpp b/examples/speed_test_async_multi.cpp index 8c85699..5645135 100644 --- a/examples/speed_test_async_multi.cpp +++ b/examples/speed_test_async_multi.cpp @@ -20,6 +20,8 @@ double time_s() { int main(int argc, char* argv[]) { Redox rdx = {cout, redox::log::Debug}; + rdx.noWait(true); + if(!rdx.connect("localhost", 6379)) return 1; if(rdx.set("simple_loop:count", "0")) { diff --git a/examples/speed_test_pubsub.cpp b/examples/speed_test_pubsub.cpp index 97d527f..bcca68b 100644 --- a/examples/speed_test_pubsub.cpp +++ b/examples/speed_test_pubsub.cpp @@ -14,7 +14,10 @@ double time_s() { int main(int argc, char *argv[]) { Redox rdx_pub; + rdx_pub.noWait(true); + Subscriber rdx_sub; + rdx_sub.noWait(true); if(!rdx_pub.connect()) return 1; if(!rdx_sub.connect()) return 1; diff --git a/examples/speed_test_sync.cpp b/examples/speed_test_sync.cpp index 31350b6..e74774b 100644 --- a/examples/speed_test_sync.cpp +++ b/examples/speed_test_sync.cpp @@ -18,6 +18,8 @@ double time_s() { int main(int argc, char* argv[]) { Redox rdx; + rdx.noWait(true); + if(!rdx.connect("localhost", 6379)) return 1; if(rdx.commandSync({"SET", "simple_loop:count", "0"})) { diff --git a/include/redox/client.hpp b/include/redox/client.hpp index 1196c05..713d980 100644 --- a/include/redox/client.hpp +++ b/include/redox/client.hpp @@ -82,6 +82,18 @@ public: ~Redox(); /** + * Enables or disables 'no-wait' mode. If enabled, no-wait mode means that the + * event loop does not pause in between processing events. It can greatly increase + * the throughput (commands per second),but means that the event thread will run at + * 100% CPU. Enable when performance is critical and you can spare a core. Default + * is off. + * + * Implementation note: When enabled, the event thread calls libev's ev_run in a + * loop with the EVRUN_NOWAIT flag. + */ + void noWait(bool state); + + /** * Connects to Redis over TCP and starts an event loop in a separate thread. Returns * true once everything is ready, or false on failure. */ @@ -340,6 +352,9 @@ private: // Dynamically allocated libev event loop struct ev_loop* evloop_; + // No-wait mode for high-performance + std::atomic_bool nowait_ = {false}; + // Asynchronous watchers ev_async watcher_command_; // For processing commands ev_async watcher_stop_; // For breaking the loop diff --git a/include/redox/subscriber.hpp b/include/redox/subscriber.hpp index 96c3729..80351c6 100644 --- a/include/redox/subscriber.hpp +++ b/include/redox/subscriber.hpp @@ -42,6 +42,11 @@ public: ~Subscriber(); /** + * Same as .noWait() on a Redox instance. + */ + void noWait(bool state) { rdx_.noWait(state); } + + /** * Same as .connect() on a Redox instance. */ bool connect( diff --git a/src/client.cpp b/src/client.cpp index 90d196c..fb6a03c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -200,6 +200,12 @@ bool Redox::initHiredis() { return true; } +void Redox::noWait(bool state) { + if(state) logger_.info() << "No-wait mode enabled."; + else logger_.info() << "No-wait mode disabled."; + nowait_ = state; +} + void breakEventLoop(struct ev_loop* loop, ev_async* async, int revents) { ev_break(loop, EVBREAK_ALL); } @@ -237,12 +243,14 @@ void Redox::runEventLoop() { running_ = true; running_waiter_.notify_one(); - // Run the event loop - // TODO this hogs resources, but ev_run(evloop_) without - // the manual loop is slower. Maybe use a CV to run sparsely - // unless there are commands to process? + // Run the event loop, using NOWAIT if enabled for maximum + // throughput by avoiding any sleeping while (!to_exit_) { - ev_run(evloop_, EVRUN_NOWAIT); + if(nowait_) { + ev_run(evloop_, EVRUN_NOWAIT); + } else { + ev_run(evloop_); + } } logger_.info() << "Stop signal detected. Closing down event loop."; -- libgit2 0.21.4