Commit b4971936d4c21e459255ce4abc05abeb56e35d7a
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.
Showing
11 changed files
with
54 additions
and
11 deletions
CMakeLists.txt
| @@ -3,7 +3,7 @@ project(redox) | @@ -3,7 +3,7 @@ project(redox) | ||
| 3 | 3 | ||
| 4 | set(REDOX_VERSION_MAJOR 0) | 4 | set(REDOX_VERSION_MAJOR 0) |
| 5 | set(REDOX_VERSION_MINOR 2) | 5 | set(REDOX_VERSION_MINOR 2) |
| 6 | -set(REDOX_VERSION_PATCH 0) | 6 | +set(REDOX_VERSION_PATCH 1) |
| 7 | set(REDOX_VERSION_STRING ${REDOX_VERSION_MAJOR}.${REDOX_VERSION_MINOR}.${REDOX_VERSION_PATCH}) | 7 | set(REDOX_VERSION_STRING ${REDOX_VERSION_MAJOR}.${REDOX_VERSION_MINOR}.${REDOX_VERSION_PATCH}) |
| 8 | 8 | ||
| 9 | option(lib "Build Redox as a dynamic library." ON) | 9 | option(lib "Build Redox as a dynamic library." ON) |
HISTORY.md
| 1 | # Release History | 1 | # Release History |
| 2 | 2 | ||
| 3 | -## 0.2.0 (2015-01-31) | 3 | +## 0.2 (2015-01-31) |
| 4 | * Move to vector of strings as input, to handle arbitrary data better and | 4 | * Move to vector of strings as input, to handle arbitrary data better and |
| 5 | improve speed. | 5 | improve speed. |
| 6 | * Add doxygen docs. | 6 | * Add doxygen docs. |
| 7 | * Move all init code out of constructors into .connect methods. | 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 | First versioned release after many nights of segfaults and memory leaks. | 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 | Desire to use Redis in C++, but unhappy with existing clients. | 13 | Desire to use Redis in C++, but unhappy with existing clients. |
README.md
| @@ -41,8 +41,7 @@ local Redis server. | @@ -41,8 +41,7 @@ local Redis server. | ||
| 41 | * `speed_test_sync` over TCP: **21,072 commands/s** | 41 | * `speed_test_sync` over TCP: **21,072 commands/s** |
| 42 | * `speed_test_sync` over Unix socket: **24,911 commands/s** | 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 | ## Tutorial | 46 | ## Tutorial |
| 48 | This section introduces the main features of redox. Look in `examples/` for more inspiration. | 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,6 +230,12 @@ a vector of strings as needed by its API. `rdx.strToVec("GET foo")` | ||
| 231 | will return an `std::vector<std::string>` containing `GET` and `foo` | 230 | will return an `std::vector<std::string>` containing `GET` and `foo` |
| 232 | as entries. `rdx.vecToStr({"GET", "foo"})` will return the string `GET foo`. | 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 | ## Reply types | 239 | ## Reply types |
| 235 | These the available template parameters in redox and the Redis | 240 | These the available template parameters in redox and the Redis |
| 236 | [return types](http://redis.io/topics/protocol) they can hold. | 241 | [return types](http://redis.io/topics/protocol) they can hold. |
examples/lpush_benchmark.cpp
| @@ -17,6 +17,7 @@ double time_s() { | @@ -17,6 +17,7 @@ double time_s() { | ||
| 17 | int main(int argc, char* argv[]) { | 17 | int main(int argc, char* argv[]) { |
| 18 | 18 | ||
| 19 | redox::Redox rdx; | 19 | redox::Redox rdx; |
| 20 | + | ||
| 20 | if(!rdx.connect()) return 1; | 21 | if(!rdx.connect()) return 1; |
| 21 | 22 | ||
| 22 | rdx.del("test"); | 23 | rdx.del("test"); |
examples/speed_test_async.cpp
| @@ -19,6 +19,8 @@ double time_s() { | @@ -19,6 +19,8 @@ double time_s() { | ||
| 19 | int main(int argc, char* argv[]) { | 19 | int main(int argc, char* argv[]) { |
| 20 | 20 | ||
| 21 | Redox rdx; | 21 | Redox rdx; |
| 22 | + rdx.noWait(true); | ||
| 23 | + | ||
| 22 | if(!rdx.connectUnix("/var/run/redis/redis.sock")) return 1; | 24 | if(!rdx.connectUnix("/var/run/redis/redis.sock")) return 1; |
| 23 | 25 | ||
| 24 | if(rdx.set("simple_loop:count", "0")) { | 26 | if(rdx.set("simple_loop:count", "0")) { |
examples/speed_test_async_multi.cpp
| @@ -20,6 +20,8 @@ double time_s() { | @@ -20,6 +20,8 @@ double time_s() { | ||
| 20 | int main(int argc, char* argv[]) { | 20 | int main(int argc, char* argv[]) { |
| 21 | 21 | ||
| 22 | Redox rdx = {cout, redox::log::Debug}; | 22 | Redox rdx = {cout, redox::log::Debug}; |
| 23 | + rdx.noWait(true); | ||
| 24 | + | ||
| 23 | if(!rdx.connect("localhost", 6379)) return 1; | 25 | if(!rdx.connect("localhost", 6379)) return 1; |
| 24 | 26 | ||
| 25 | if(rdx.set("simple_loop:count", "0")) { | 27 | if(rdx.set("simple_loop:count", "0")) { |
examples/speed_test_pubsub.cpp
| @@ -14,7 +14,10 @@ double time_s() { | @@ -14,7 +14,10 @@ double time_s() { | ||
| 14 | int main(int argc, char *argv[]) { | 14 | int main(int argc, char *argv[]) { |
| 15 | 15 | ||
| 16 | Redox rdx_pub; | 16 | Redox rdx_pub; |
| 17 | + rdx_pub.noWait(true); | ||
| 18 | + | ||
| 17 | Subscriber rdx_sub; | 19 | Subscriber rdx_sub; |
| 20 | + rdx_sub.noWait(true); | ||
| 18 | 21 | ||
| 19 | if(!rdx_pub.connect()) return 1; | 22 | if(!rdx_pub.connect()) return 1; |
| 20 | if(!rdx_sub.connect()) return 1; | 23 | if(!rdx_sub.connect()) return 1; |
examples/speed_test_sync.cpp
| @@ -18,6 +18,8 @@ double time_s() { | @@ -18,6 +18,8 @@ double time_s() { | ||
| 18 | int main(int argc, char* argv[]) { | 18 | int main(int argc, char* argv[]) { |
| 19 | 19 | ||
| 20 | Redox rdx; | 20 | Redox rdx; |
| 21 | + rdx.noWait(true); | ||
| 22 | + | ||
| 21 | if(!rdx.connect("localhost", 6379)) return 1; | 23 | if(!rdx.connect("localhost", 6379)) return 1; |
| 22 | 24 | ||
| 23 | if(rdx.commandSync({"SET", "simple_loop:count", "0"})) { | 25 | if(rdx.commandSync({"SET", "simple_loop:count", "0"})) { |
include/redox/client.hpp
| @@ -82,6 +82,18 @@ public: | @@ -82,6 +82,18 @@ public: | ||
| 82 | ~Redox(); | 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 | * Connects to Redis over TCP and starts an event loop in a separate thread. Returns | 97 | * Connects to Redis over TCP and starts an event loop in a separate thread. Returns |
| 86 | * true once everything is ready, or false on failure. | 98 | * true once everything is ready, or false on failure. |
| 87 | */ | 99 | */ |
| @@ -340,6 +352,9 @@ private: | @@ -340,6 +352,9 @@ private: | ||
| 340 | // Dynamically allocated libev event loop | 352 | // Dynamically allocated libev event loop |
| 341 | struct ev_loop* evloop_; | 353 | struct ev_loop* evloop_; |
| 342 | 354 | ||
| 355 | + // No-wait mode for high-performance | ||
| 356 | + std::atomic_bool nowait_ = {false}; | ||
| 357 | + | ||
| 343 | // Asynchronous watchers | 358 | // Asynchronous watchers |
| 344 | ev_async watcher_command_; // For processing commands | 359 | ev_async watcher_command_; // For processing commands |
| 345 | ev_async watcher_stop_; // For breaking the loop | 360 | ev_async watcher_stop_; // For breaking the loop |
include/redox/subscriber.hpp
| @@ -42,6 +42,11 @@ public: | @@ -42,6 +42,11 @@ public: | ||
| 42 | ~Subscriber(); | 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 | * Same as .connect() on a Redox instance. | 50 | * Same as .connect() on a Redox instance. |
| 46 | */ | 51 | */ |
| 47 | bool connect( | 52 | bool connect( |
src/client.cpp
| @@ -200,6 +200,12 @@ bool Redox::initHiredis() { | @@ -200,6 +200,12 @@ bool Redox::initHiredis() { | ||
| 200 | return true; | 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 | void breakEventLoop(struct ev_loop* loop, ev_async* async, int revents) { | 209 | void breakEventLoop(struct ev_loop* loop, ev_async* async, int revents) { |
| 204 | ev_break(loop, EVBREAK_ALL); | 210 | ev_break(loop, EVBREAK_ALL); |
| 205 | } | 211 | } |
| @@ -237,12 +243,14 @@ void Redox::runEventLoop() { | @@ -237,12 +243,14 @@ void Redox::runEventLoop() { | ||
| 237 | running_ = true; | 243 | running_ = true; |
| 238 | running_waiter_.notify_one(); | 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 | while (!to_exit_) { | 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 | logger_.info() << "Stop signal detected. Closing down event loop."; | 256 | logger_.info() << "Stop signal detected. Closing down event loop."; |