From 1db41d903a827ae775e3d5cd8ecbeaf57694c99b Mon Sep 17 00:00:00 2001 From: Dawid Drozd Date: Sun, 6 Aug 2017 00:14:35 +0200 Subject: [PATCH] Add new implementation of EventBus Simply new version breaks back compability and simply is better ;) --- CMakeLists.txt | 10 +- include/eventbus/EventBus.h | 202 ++++++++++++++++++ include/eventbus/Notification2.h | 78 +++++++ performance/CMakeLists.txt | 12 +- performance/README.md | 64 +++--- .../CCNotificationCenterPerformance.cpp | 81 ++++--- performance/eventbus/EventBus2Performance.cpp | 165 ++++++++++++++ performance/eventbus/EventBusPerformance.cpp | 25 ++- test/CMakeLists.txt | 9 + test/eventbus/NotificationTest.cpp | 51 +++++ test/eventbus/NotifierTest.cpp | 92 ++++++-- 11 files changed, 711 insertions(+), 78 deletions(-) create mode 100644 include/eventbus/EventBus.h create mode 100644 include/eventbus/Notification2.h create mode 100644 performance/eventbus/EventBus2Performance.cpp create mode 100644 test/eventbus/NotificationTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 92aa95e..d21afc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,9 @@ PROJECT(EventBus ADD_LIBRARY(EventBus src/eventbus/EventCollector.cpp include/eventbus/EventCollector.h src/eventbus/Notification.cpp include/eventbus/Notification.h + include/eventbus/Notification2.h include/eventbus/Notifier.h + include/eventbus/EventBus.h ) ADD_LIBRARY(Dexode::EventBus ALIAS EventBus) @@ -20,8 +22,12 @@ TARGET_INCLUDE_DIRECTORIES(EventBus PUBLIC ) TARGET_COMPILE_OPTIONS(EventBus - PUBLIC $<$:-std=c++14> - PRIVATE -Wall -pedantic -Wno-unused-private-field -Wnon-virtual-dtor -Werror + PRIVATE -Wall -pedantic -Wno-unused-private-field -Wnon-virtual-dtor #-Werror + ) + +SET_TARGET_PROPERTIES(EventBus PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES ) TARGET_COMPILE_FEATURES(EventBus diff --git a/include/eventbus/EventBus.h b/include/eventbus/EventBus.h new file mode 100644 index 0000000..86f3088 --- /dev/null +++ b/include/eventbus/EventBus.h @@ -0,0 +1,202 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Notification2.h" + +#if __cplusplus < 201402L + #error This library needs at least a C++14 compliant compiler +#endif + +namespace Dexode +{ + +namespace +{ + +template +struct eventbus_traits +{ + typedef T type; +}; + +} + +class EventBus +{ +public: + EventBus() = default; + + ~EventBus() + { + _callbacks.clear(); + } + + EventBus(const EventBus&) = delete; + EventBus(EventBus&&) = delete; + + EventBus& operator=(EventBus&&) = delete; + EventBus& operator=(const EventBus&) = delete; + + /** + * Register listener for notification. Returns token used for unlisten + * + * @param notification + * @param callback - your callback to handle notification + * @return token used for unlisten + */ + template + int listen(const Notification2& notification + , typename eventbus_traits&>::type callback) + { + const int token = ++_tokener; + listen(token, notification, callback); + return token; + } + + /** + * Register listener for notification. Returns token used for unlisten + * + * @param notification - name of your notification + * @param callback - your callback to handle notification + * @return token used for unlisten + */ + template + int listen(const std::string& notificationName + , typename eventbus_traits&>::type callback) + { + return listen(Dexode::Notification2{notificationName}, callback); + } + + /** + * @param token - unique token for identification receiver. Simply pass token from @see EventBus::listen + * @param notification - pass notification like "getNotificationXYZ()" + * @param callback - your callback to handle notification + */ + template + void listen(const int token + , const Notification2& notification + , typename eventbus_traits&>::type callback) + { + using CallbackType = std::function; + using Vector = VectorImpl; + + assert(callback && "Please set it");//Check for valid object + + std::unique_ptr& vector = _callbacks[notification.getKey()]; + if (vector == nullptr) + { + vector.reset(new Vector{}); + } + + assert(dynamic_cast(vector.get())); + Vector* vectorImpl = static_cast(vector.get()); + vectorImpl->container.emplace_back(std::make_pair(callback, token)); + } + + /** + * @param token - token from EventBus::listen + */ + void unlistenAll(const int token) + { + for (auto& element : _callbacks) + { + element.second->remove(token); + } + } + + /** + * @param token - token from EventBus::listen + * @param notification - notification you wan't to unlisten. @see Notiier::listen + */ + template + void unlisten(const int token, const NotificationType& notification) + { + auto found = _callbacks.find(notification.getKey()); + if (found != _callbacks.end()) + { + found.second->remove(token); + } + } + + template + void notify(const NotificationType& notification, Args&& ... params) + { + using CallbackType = typename notification_traits::callback_type; + using Vector = VectorImpl; + + auto found = _callbacks.find(notification.getKey()); + if (found == _callbacks.end()) + { + return;// no such notifications + } + + std::unique_ptr& vector = found->second; + assert(dynamic_cast(vector.get())); + Vector* vectorImpl = static_cast(vector.get()); + + //Copy? TODO think about it Use 2 vectors? + for (auto& element : vectorImpl->container) + { + element.first(std::forward(params)...); + } + } + + // We need to call this in form like: notify("yes",value) + // We can't reduce it to notify("yes",value) + // It wouldn't be obvious which to call Notification or Notification + // So it can take to a lot of mistakes + template + void notify(const std::string& notificationName, Args&& ... params) + { + notify(Dexode::Notification2{notificationName}, std::forward(params)...); + } + +private: + struct VectorInterface + { + virtual ~VectorInterface() = default; + + virtual void remove(const int token) = 0; + virtual void removeAll() = 0; + }; + + template + struct VectorImpl : public VectorInterface + { + std::vector> container; + + virtual ~VectorImpl() + { + removeAll(); + } + + virtual void removeAll() override + { + container.clear(); + } + + virtual void remove(const int token) override + { + auto removeFrom = std::remove_if(container.begin(), container.end() + , [token](const std::pair& element) + { + return element.second == token; + }); + if (removeFrom == container.end()) + { + return; + } + container.erase(removeFrom, container.end()); + } + }; + + int _tokener = 0; + std::map> _callbacks; +}; + +} /* namespace Dexode */ diff --git a/include/eventbus/Notification2.h b/include/eventbus/Notification2.h new file mode 100644 index 0000000..4ff6b2e --- /dev/null +++ b/include/eventbus/Notification2.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +namespace Dexode +{ + +template +class Notification2 +{ +public: + using Callback = std::function; + + constexpr explicit Notification2(const std::string& name) + : _key{std::hash{}(name + typeid(Callback).name())} + , _name{name} + { + } + + constexpr Notification2(const Notification2& other) + : _key{other._key} + , _name{other._name} + { + } + + Notification2(Notification2&& other) + : _key{other._key} + , _name{other._name} + { + } + + Notification2& operator=(Notification2&&) = delete; + + Notification2& operator=(const Notification2&) = delete; + + const size_t getKey() const + { + return _key; + } + + const std::string& getName() const + { + return _name; + } + + bool operator==(const Notification2& rhs) const + { + return _key == rhs._key && + _name == rhs._name; + } + + bool operator!=(const Notification2& rhs) const + { + return !(rhs == *this); + } + + friend std::ostream& operator<<(std::ostream& stream, const Notification2& notification) + { + stream << "Notification{name: " << notification._name << " key: " << notification._key; + return stream; + } + +private: + const std::size_t _key; + const std::string _name; +}; + +template +struct notification_traits; + +template +struct notification_traits> +{ + using callback_type = typename std::function; +}; + +} diff --git a/performance/CMakeLists.txt b/performance/CMakeLists.txt index 2c10f27..061529f 100644 --- a/performance/CMakeLists.txt +++ b/performance/CMakeLists.txt @@ -4,13 +4,23 @@ ADD_SUBDIRECTORY(benchmark/) # If you want to compare with CCNotificationCenter read about it in README and uncomment line below -INCLUDE(cocos2d-x-compare/Cocos2dxCompare.cmake) +#INCLUDE(cocos2d-x-compare/Cocos2dxCompare.cmake) ADD_EXECUTABLE(EventBusPerformance eventbus/EventBusPerformance.cpp + eventbus/EventBus2Performance.cpp ${CCNOTIFICATION_CENTER_SRC} ) +TARGET_COMPILE_OPTIONS(EventBusPerformance + PRIVATE -Wall -pedantic -Wno-unused-private-field -Wnon-virtual-dtor #-Werror + ) + +SET_TARGET_PROPERTIES(EventBusPerformance PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES + ) + TARGET_INCLUDE_DIRECTORIES(EventBusPerformance PRIVATE ./ ${CCNOTIFICATION_CENTER_INCLUDE} diff --git a/performance/README.md b/performance/README.md index 5af5531..6ea7310 100644 --- a/performance/README.md +++ b/performance/README.md @@ -40,30 +40,44 @@ git apply ../CCNotificationCenterPerformance.patch ``` Run on (8 X 3600 MHz CPU s) -2017-08-05 19:30:38 ------------------------------------------------------------------------------------------------------------------ -Benchmark Time CPU Iterations UserCounters... ------------------------------------------------------------------------------------------------------------------ -checkSimpleNotification 6 ns 6 ns 109393759 sum=417.304M -check10Listeners 22 ns 22 ns 32170743 sum=1.19845G -check100Listeners 213 ns 213 ns 3299613 sum=1.2292G -check1kListeners 2094 ns 2093 ns 333683 sum=1.24307G ------------------------------------------------------------------------------------------------------------------ -call1kLambdas_compare 2181 ns 2181 ns 308722 sum=1.15008G ------------------------------------------------------------------------------------------------------------------ -check10NotificationsFor1kListeners 267 ns 267 ns 2607581 sum=-1.81421G -check100NotificationsFor1kListeners 107 ns 107 ns 6373043 sum=1.8782G -check1kNotificationsFor1kListeners 129 ns 129 ns 5497132 sum=1.11759G -check100NotificationsFor10kListeners 314 ns 314 ns 2226597 sum=543.525M ------------------------------------------------------------------------------------------------------------------ -CCNotificationCenter_checkSimpleNotification 168 ns 168 ns 4259511 sum=16.2487M -CCNotificationCenter_check10Listeners 275 ns 275 ns 2484477 sum=94.7753M -CCNotificationCenter_check100Listeners 1399 ns 1398 ns 514899 sum=196.418M -CCNotificationCenter_check1kListeners 13124 ns 13123 ns 53271 sum=203.213M -CCNotificationCenter_check10NotificationsFor1kListeners 11379 ns 11378 ns 62138 sum=53.3993M -CCNotificationCenter_check100NotificationsFor1kListeners 9583 ns 9582 ns 64665 sum=61.3059M -CCNotificationCenter_check1kNotificationsFor1kListeners 9451 ns 9451 ns 64162 sum=61.5188M -CCNotificationCenter_check100NotificationsFor10kListeners 107082 ns 107074 ns 6603 sum=62.1745M +2017-08-06 00:09:43 +----------------------------------------------------------------------------------------------------------------------- +Benchmark Time CPU Iterations UserCounters... +----------------------------------------------------------------------------------------------------------------------- +check100Listeners 234 ns 234 ns 3006299 sum=1.11993G +check100Listeners_EventBus2 216 ns 216 ns 3238994 sum=1.20662G +check100Listeners_CCNotificationCenter 1353 ns 1353 ns 535779 sum=204.383M +----------------------------------------------------------------------------------------------------------------------- +check100NotificationsFor10kListeners 315 ns 315 ns 2208261 sum=370.782M +check100NotificationsFor10kListeners_EventBus2 307 ns 307 ns 2288071 sum=1122.83M +check100NotificationsFor10kListeners_CCNotificationCenter 109629 ns 109620 ns 6493 sum=61.1035M +----------------------------------------------------------------------------------------------------------------------- +check100NotificationsFor1kListeners 108 ns 108 ns 6351160 sum=1.85786G +check100NotificationsFor1kListeners_EventBus2 102 ns 102 ns 6985920 sum=-1.55558G +check100NotificationsFor1kListeners_CCNotificationCenter 9684 ns 9683 ns 67812 sum=64.3065M +----------------------------------------------------------------------------------------------------------------------- +check10Listeners 22 ns 22 ns 32136715 sum=1.19719G +check10Listeners_EventBus2 21 ns 21 ns 32519065 sum=1.21143G +check10Listeners_CCNotificationCenter 264 ns 264 ns 2572170 sum=98.1205M +----------------------------------------------------------------------------------------------------------------------- +check10NotificationsFor1kListeners 273 ns 273 ns 2609987 sum=-1.81219G +check10NotificationsFor1kListeners_EventBus2 267 ns 267 ns 2652159 sum=-1.77676G +check10NotificationsFor1kListeners_CCNotificationCenter 11172 ns 11171 ns 62865 sum=54.023M +----------------------------------------------------------------------------------------------------------------------- +check1kListeners 2158 ns 2158 ns 317734 sum=1.18365G +check1kListeners_EventBus2 2168 ns 2168 ns 326126 sum=1.21491G +check1kListeners_CCNotificationCenter 12919 ns 12918 ns 54781 sum=208.973M +----------------------------------------------------------------------------------------------------------------------- +check1kNotificationsFor1kListeners 129 ns 129 ns 5459854 sum=1108.69M +check1kNotificationsFor1kListeners_EventBus2 103 ns 103 ns 6867175 sum=-1.60612G +check1kNotificationsFor1kListeners_CCNotificationCenter 9711 ns 9710 ns 67611 sum=64.9171M +----------------------------------------------------------------------------------------------------------------------- +checkNotifyFor10kListenersWhenNoOneListens 2 ns 2 ns 399367972 sum=0 +checkNotifyFor10kListenersWhenNoOneListens_EventBus2 2 ns 2 ns 400616370 sum=0 +checkNotifyFor10kListenersWhenNoOneListens_CCNotificationCenter 125173 ns 125162 ns 5657 sum=0``` +----------------------------------------------------------------------------------------------------------------------- +checkSimpleNotification 7 ns 7 ns 100833112 sum=384.648M +checkSimpleNotification_EventBus2 5 ns 5 ns 144959897 sum=552.978M +checkSimpleNotification_CCNotificationCenter 172 ns 172 ns 4179021 sum=15.9417M ``` - So comparing to CCNotificationCenter, EventBus is something like ~10x FASTER specially when we have more unique notifications. diff --git a/performance/cocos2d-x-compare/CCNotificationCenterPerformance.cpp b/performance/cocos2d-x-compare/CCNotificationCenterPerformance.cpp index 7d447dd..14fab76 100644 --- a/performance/cocos2d-x-compare/CCNotificationCenterPerformance.cpp +++ b/performance/cocos2d-x-compare/CCNotificationCenterPerformance.cpp @@ -22,7 +22,7 @@ struct SampleObserver : public cocos2d::CCObject } }; -void CCNotificationCenter_checkNListeners(benchmark::State& state, const int listenersCount) +void checkNListeners(benchmark::State& state, const int listenersCount) { using namespace cocos2d; cocos2d::CCNotificationCenter bus; @@ -46,29 +46,29 @@ void CCNotificationCenter_checkNListeners(benchmark::State& state, const int lis CCPoolManager::sharedPoolManager()->purgePoolManager(); } -void CCNotificationCenter_checkSimpleNotification(benchmark::State& state) +void checkSimpleNotification_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNListeners(state, 1); + checkNListeners(state, 1); } -void CCNotificationCenter_check10Listeners(benchmark::State& state) +void check10Listeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNListeners(state, 10); + checkNListeners(state, 10); } -void CCNotificationCenter_check100Listeners(benchmark::State& state) +void check100Listeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNListeners(state, 100); + checkNListeners(state, 100); } -void CCNotificationCenter_check1kListeners(benchmark::State& state) +void check1kListeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNListeners(state, 1000); + checkNListeners(state, 1000); } -void CCNotificationCenter_checkNNotificationsForNListeners(benchmark::State& state - , const int notificationsCount - , const int listenersCount) +void checkNNotificationsForNListeners(benchmark::State& state + , const int notificationsCount + , const int listenersCount) { std::mt19937 generator(311281); std::uniform_int_distribution uniformDistribution(0, notificationsCount - 1); @@ -112,32 +112,57 @@ void CCNotificationCenter_checkNNotificationsForNListeners(benchmark::State& sta CCPoolManager::sharedPoolManager()->purgePoolManager(); } -void CCNotificationCenter_check10NotificationsFor1kListeners(benchmark::State& state) +void check10NotificationsFor1kListeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNNotificationsForNListeners(state, 10, 1000); + checkNNotificationsForNListeners(state, 10, 1000); } -void CCNotificationCenter_check100NotificationsFor1kListeners(benchmark::State& state) +void check100NotificationsFor1kListeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNNotificationsForNListeners(state, 100, 1000); + checkNNotificationsForNListeners(state, 100, 1000); } -void CCNotificationCenter_check1kNotificationsFor1kListeners(benchmark::State& state) +void check1kNotificationsFor1kListeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNNotificationsForNListeners(state, 1000, 1000); + checkNNotificationsForNListeners(state, 1000, 1000); } -void CCNotificationCenter_check100NotificationsFor10kListeners(benchmark::State& state) +void check100NotificationsFor10kListeners_CCNotificationCenter(benchmark::State& state) { - CCNotificationCenter_checkNNotificationsForNListeners(state, 100, 10000); + checkNNotificationsForNListeners(state, 100, 10000); } -BENCHMARK(CCNotificationCenter_checkSimpleNotification); -BENCHMARK(CCNotificationCenter_check10Listeners); -BENCHMARK(CCNotificationCenter_check100Listeners); -BENCHMARK(CCNotificationCenter_check1kListeners); -BENCHMARK(CCNotificationCenter_check10NotificationsFor1kListeners); -BENCHMARK(CCNotificationCenter_check100NotificationsFor1kListeners); -BENCHMARK(CCNotificationCenter_check1kNotificationsFor1kListeners); -BENCHMARK(CCNotificationCenter_check100NotificationsFor10kListeners); +void checkNotifyFor10kListenersWhenNoOneListens_CCNotificationCenter(benchmark::State& state) +{ + using namespace cocos2d; + cocos2d::CCNotificationCenter bus; + int sum = 0; + for (int i = 0; i < 10000; ++i) + { + auto observer = new SampleObserver{}; + observer->autorelease(); + observer->callback = [&](int value) + { + benchmark::DoNotOptimize(sum += value * 2); + }; + bus.addObserver(observer, callfuncO_selector(SampleObserver::onCall), "sample", nullptr); + } + auto number = CCInteger::create(2); + while (state.KeepRunning())//Performance area! + { + bus.postNotification("unknown", number); + } + state.counters["sum"] = sum; + CCPoolManager::sharedPoolManager()->purgePoolManager(); +} + +BENCHMARK(checkSimpleNotification_CCNotificationCenter); +BENCHMARK(check10Listeners_CCNotificationCenter); +BENCHMARK(check100Listeners_CCNotificationCenter); +BENCHMARK(check1kListeners_CCNotificationCenter); +BENCHMARK(check10NotificationsFor1kListeners_CCNotificationCenter); +BENCHMARK(check100NotificationsFor1kListeners_CCNotificationCenter); +BENCHMARK(check1kNotificationsFor1kListeners_CCNotificationCenter); +BENCHMARK(check100NotificationsFor10kListeners_CCNotificationCenter); +BENCHMARK(checkNotifyFor10kListenersWhenNoOneListens_CCNotificationCenter); } diff --git a/performance/eventbus/EventBus2Performance.cpp b/performance/eventbus/EventBus2Performance.cpp new file mode 100644 index 0000000..772db4e --- /dev/null +++ b/performance/eventbus/EventBus2Performance.cpp @@ -0,0 +1,165 @@ +// +// Created by Dawid Drozd aka Gelldur on 05.08.17. +// +#include + +#include +#include + +#include + +namespace +{ + +void checkNListeners(benchmark::State& state, const int listenersCount) +{ + Dexode::EventBus bus; + int sum = 0; + + Dexode::Notification2 simpleNotification("simple"); + for (int i = 0; i < listenersCount; ++i) + { + bus.listen(simpleNotification, [&](int value) + { + benchmark::DoNotOptimize(sum += value * 2); + }); + } + + while (state.KeepRunning())//Performance area! + { + bus.notify(simpleNotification, 2); + } + state.counters["sum"] = sum; +} + +void checkSimpleNotification_EventBus2(benchmark::State& state) +{ + checkNListeners(state, 1); +} + +void check10Listeners_EventBus2(benchmark::State& state) +{ + checkNListeners(state, 10); +} + +void check100Listeners_EventBus2(benchmark::State& state) +{ + checkNListeners(state, 100); +} + +void check1kListeners_EventBus2(benchmark::State& state) +{ + checkNListeners(state, 1000); +} + +void call1kLambdas_compare_EventBus2(benchmark::State& state) +{ + int sum = 0; + std::vector> callbacks; + callbacks.reserve(1000); + for (int i = 0; i < 1000; ++i) + { + callbacks.emplace_back([&](int value) + { + benchmark::DoNotOptimize(sum += value * 2); + }); + } + + while (state.KeepRunning())//Performance area! + { + for (int i = 0; i < 1000; ++i) + // for (auto& callback :callbacks) + { + callbacks[i](2); + } + } + state.counters["sum"] = sum; +} + +void checkNNotificationsForNListeners(benchmark::State& state, const int notificationsCount, const int listenersCount) +{ + std::mt19937 generator(311281); + std::uniform_int_distribution uniformDistribution(0, notificationsCount - 1); + + //We generate here N different notifications + std::vector> notifications; + notifications.reserve(notificationsCount); + for (int i = 0; i < notificationsCount; ++i) + { + notifications.emplace_back(std::string{"notify_"} + std::to_string(i)); + } + + Dexode::EventBus bus; + int sum = 0; + for (int i = 0; i < listenersCount; ++i)//We register M listeners for N notifications using uniform distribution + { + const auto& notification = notifications.at(uniformDistribution(generator)); + bus.listen(notification, [&](int value) + { + benchmark::DoNotOptimize(sum += value * 2);//we use it to prevent some? optimizations + }); + } + + while (state.KeepRunning())//Performance area! + { + //Pick random notification + const auto& notification = notifications.at(uniformDistribution(generator)); + bus.notify(notification, uniformDistribution(generator)); + } + state.counters["sum"] = sum; +} + +void check10NotificationsFor1kListeners_EventBus2(benchmark::State& state) +{ + checkNNotificationsForNListeners(state, 10, 1000); +} + +void check100NotificationsFor1kListeners_EventBus2(benchmark::State& state) +{ + checkNNotificationsForNListeners(state, 100, 1000); +} + +void check1kNotificationsFor1kListeners_EventBus2(benchmark::State& state) +{ + checkNNotificationsForNListeners(state, 1000, 1000); +} + +void check100NotificationsFor10kListeners_EventBus2(benchmark::State& state) +{ + checkNNotificationsForNListeners(state, 100, 10000); +} + +void checkNotifyFor10kListenersWhenNoOneListens_EventBus2(benchmark::State& state) +{ + Dexode::EventBus bus; + Dexode::Notification2 simpleNotification("simple"); + Dexode::Notification2 unknownNotification("unknown"); + int sum = 0; + for (int i = 0; i < 10000; ++i) + { + bus.listen(simpleNotification, [&](int value) + { + benchmark::DoNotOptimize(sum += value * 2); + }); + } + + while (state.KeepRunning())//Performance area! + { + bus.notify(unknownNotification, 2); + } + state.counters["sum"] = sum; +} + +} + +BENCHMARK(call1kLambdas_compare_EventBus2); + +BENCHMARK(checkSimpleNotification_EventBus2); +BENCHMARK(check10Listeners_EventBus2); +BENCHMARK(check100Listeners_EventBus2); +BENCHMARK(check1kListeners_EventBus2); +BENCHMARK(check10NotificationsFor1kListeners_EventBus2); +BENCHMARK(check100NotificationsFor1kListeners_EventBus2); +BENCHMARK(check1kNotificationsFor1kListeners_EventBus2); +BENCHMARK(check100NotificationsFor10kListeners_EventBus2); +BENCHMARK(checkNotifyFor10kListenersWhenNoOneListens_EventBus2); diff --git a/performance/eventbus/EventBusPerformance.cpp b/performance/eventbus/EventBusPerformance.cpp index bf8b0fb..a8559a7 100644 --- a/performance/eventbus/EventBusPerformance.cpp +++ b/performance/eventbus/EventBusPerformance.cpp @@ -127,16 +127,39 @@ void check100NotificationsFor10kListeners(benchmark::State& state) checkNNotificationsForNListeners(state, 100, 10000); } +MAKE_NOTIFICATION(UnknownNotification, int); + +void checkNotifyFor10kListenersWhenNoOneListens(benchmark::State& state) +{ + Dexode::Notifier bus; + int sum = 0; + for (int i = 0; i < 10000; ++i) + { + bus.listen(getNotificationSimpleNotification(), [&](int value) + { + benchmark::DoNotOptimize(sum += value * 2); + }); + } + + while (state.KeepRunning())//Performance area! + { + bus.notify(getNotificationUnknownNotification(), 2); + } + state.counters["sum"] = sum; } +} + +BENCHMARK(call1kLambdas_compare); + BENCHMARK(checkSimpleNotification); BENCHMARK(check10Listeners); BENCHMARK(check100Listeners); BENCHMARK(check1kListeners); -BENCHMARK(call1kLambdas_compare); BENCHMARK(check10NotificationsFor1kListeners); BENCHMARK(check100NotificationsFor1kListeners); BENCHMARK(check1kNotificationsFor1kListeners); BENCHMARK(check100NotificationsFor10kListeners); +BENCHMARK(checkNotifyFor10kListenersWhenNoOneListens); BENCHMARK_MAIN(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 612a72e..b329a4a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,6 +4,15 @@ ADD_EXECUTABLE(EventBusTest eventbus/EventCollectorTest.cpp eventbus/NotifierTest.cpp + eventbus/NotificationTest.cpp) + +SET_TARGET_PROPERTIES(EventBusTest PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES + ) + +TARGET_COMPILE_OPTIONS(EventBusTest + PRIVATE -Wall -pedantic -Wno-unused-private-field -Wnon-virtual-dtor #-Werror ) TARGET_INCLUDE_DIRECTORIES(EventBusTest PRIVATE Catch/single_include/) diff --git a/test/eventbus/NotificationTest.cpp b/test/eventbus/NotificationTest.cpp new file mode 100644 index 0000000..5f638f7 --- /dev/null +++ b/test/eventbus/NotificationTest.cpp @@ -0,0 +1,51 @@ +// +// Created by Dawid Drozd aka Gelldur on 05.08.17. +// + +#include +#include + +TEST_CASE("eventbus/Notification same", "Notifications should be same") +{ + { + Dexode::Notification2 one("one"); + Dexode::Notification2 two("one"); + REQUIRE(one == two); + } + { + Dexode::Notification2 one("one"); + Dexode::Notification2 two("one"); + REQUIRE(one == two);//int == signed + } +} + +TEST_CASE("eventbus/Notification not same", "Notifications should different") +{ + { + Dexode::Notification2 one("one"); + Dexode::Notification2 two("two"); + REQUIRE(one != two); + } + { + Dexode::Notification2 one("one"); + Dexode::Notification2 two("two"); + //REQUIRE(one != two); //Different types! + } + { + Dexode::Notification2 one("one"); + Dexode::Notification2 two("one"); + //REQUIRE(one != two); //Different types! + } + { + Dexode::Notification2 one("one"); + Dexode::Notification2 two("one"); + //REQUIRE(one != two); //Different types! + } +} + +TEST_CASE("eventbus/Notification should copy constructable", "Notifications should be copy constructable") +{ + Dexode::Notification2 one("one"); + Dexode::Notification2 two = one; + REQUIRE(one == two); +} diff --git a/test/eventbus/NotifierTest.cpp b/test/eventbus/NotifierTest.cpp index 7a46128..5a4231b 100644 --- a/test/eventbus/NotifierTest.cpp +++ b/test/eventbus/NotifierTest.cpp @@ -5,52 +5,102 @@ #define CATCH_CONFIG_MAIN #include -#include - -namespace -{ - -MAKE_NOTIFICATION(SimpleNotification, int); - -MAKE_NOTIFICATION(RefNotification, int &); - -} +#include TEST_CASE("eventbus/Simple test", "Simple test") { - Dexode::Notifier bus; - const auto token = bus.listen(getNotificationSimpleNotification(), [](int value) + Dexode::EventBus bus; + Dexode::Notification2 simpleNotification{"simple"}; + + const auto token = bus.listen(simpleNotification, [](int value) { REQUIRE(value == 3); }); - bus.notify(getNotificationSimpleNotification(), 3); + bus.notify(simpleNotification, 3); bus.unlistenAll(token); - bus.notify(getNotificationSimpleNotification(), 2); + bus.notify(simpleNotification, 2); - bus.listen(getNotificationSimpleNotification(), [](int value) + bus.listen(simpleNotification, [](int value) { REQUIRE(value == 1); }); - bus.notify(getNotificationSimpleNotification(), 1); + bus.notify(simpleNotification, 1); } TEST_CASE("eventbus/Multiple listen on same token", "Listening on the same token") { - Dexode::Notifier bus; - const auto token = bus.listen(getNotificationRefNotification(), [](int& value) + Dexode::EventBus bus; + Dexode::Notification2 simpleNotification{"simple"}; + Dexode::Notification2 simpleRefNotification{"simple"}; + const auto token = bus.listen(simpleRefNotification, [](int& value) { REQUIRE(value == 3); --value; }); - bus.listen(token, getNotificationRefNotification(), [](int& value) + bus.listen(token, simpleRefNotification, [](int& value) { REQUIRE(value == 2); }); int value = 3; - bus.notify(getNotificationRefNotification(), value); + bus.notify(simpleRefNotification, value); bus.unlistenAll(token); - bus.notify(getNotificationSimpleNotification(), 2); + bus.notify(simpleNotification, value); + REQUIRE(value == 2); + bus.notify(simpleRefNotification, value); + REQUIRE(value == 2); +} + +TEST_CASE("eventbus/EventBus listen", "Listen without notification object. Using only string") +{ + int isCalled = 0; + Dexode::EventBus bus; + const auto token = bus.listen("simple", [&](int value) + { + ++isCalled; + REQUIRE(value == 3); + }); + + Dexode::Notification2 simpleNotification{"simple"}; + REQUIRE(isCalled == 0); + bus.notify(simpleNotification, 3); + REQUIRE(isCalled == 1); + bus.unlistenAll(token); + bus.notify(simpleNotification, 2); + + bus.listen(simpleNotification, [&](int value) + { + ++isCalled; + REQUIRE(value == 1); + }); + REQUIRE(isCalled == 1); + bus.notify(simpleNotification, 1); + REQUIRE(isCalled == 2); +} + +TEST_CASE("eventbus/EventBus listen & notify", "Listen & notify without notification object. Using only string") +{ + int isCalled = 0; + Dexode::EventBus bus; + const auto token = bus.listen("simple", [&](int value) + { + ++isCalled; + REQUIRE(value == 3); + }); + REQUIRE(isCalled == 0); + bus.notify(std::string{"simple"}, 3); + REQUIRE(isCalled == 1); + bus.unlistenAll(token); + bus.notify("simple", 2); + REQUIRE(isCalled == 1); + + bus.listen("simple", [&](int value) + { + ++isCalled; + REQUIRE(value == 1); + }); + bus.notify("simple", 1); + REQUIRE(isCalled == 2); }