Skip to content

Commit

Permalink
Use spinlock in "partly-multithreaded" operators instead of heavy-mut…
Browse files Browse the repository at this point in the history
…ex (#274)
  • Loading branch information
victimsnino authored Sep 11, 2022
1 parent ea672e7 commit cba61a5
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 21 deletions.
34 changes: 34 additions & 0 deletions src/benchmarks/rpp_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <rpp/operators.hpp>
#include <rpp/subjects.hpp>
#include <rpp/schedulers/trampoline_scheduler.hpp>
#include <rpp/utils/spinlock.hpp>

#include <array>
#include <vector>
Expand Down Expand Up @@ -1044,3 +1045,36 @@ TEST_CASE("trampoline scheduler")
});
};
}

TEST_CASE("single-threaded locks")
{
int target{};
BENCHMARK("no-lock increment", i)
{
target += i;
return target;
};
target = 0;
std::mutex mutex{};

BENCHMARK("mutex lock increment", i)
{
{
std::lock_guard lock{mutex};
target += i;
}
return target;
};

target = 0;
rpp::utils::spinlock spinlock{};

BENCHMARK("spin-lock increment", i)
{
{
std::lock_guard lock{spinlock};
target += i;
}
return target;
};
}
11 changes: 6 additions & 5 deletions src/rpp/rpp/operators/combine_latest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include <rpp/utils/functors.hpp>
#include <rpp/operators/merge.hpp>
#include <rpp/defs.hpp>

#include <rpp/utils/spinlock.hpp>

#include <rpp/operators/details/subscriber_with_state.hpp> // create_subscriber_with_state

Expand Down Expand Up @@ -71,11 +71,12 @@ using combine_latest_on_error = merge_on_error;
using combine_latest_on_completed = merge_on_completed;

template<typename TCombiner, constraint::decayed_type... Types>
struct combine_latest_state_with_serialized_mutex : combine_latest_state<TCombiner,Types...>
struct combine_latest_state_with_serialized_spinlock : combine_latest_state<TCombiner,Types...>
{
using combine_latest_state<TCombiner,Types...>::combine_latest_state;

std::mutex mutex{};
// we can use spinlock there because 99.9% of time only one ever thread would send values from on_next serialized (due to values_mutex), but we have small probability to get error from another observable immediately
utils::spinlock spinlock{};
};

/**
Expand Down Expand Up @@ -134,9 +135,9 @@ struct combine_latest_impl
template<constraint::subscriber_of_type<DownstreamType> TSub>
auto operator()(TSub&& in_subscriber) const
{
auto state = std::make_shared<combine_latest_state_with_serialized_mutex<TCombiner, Type, utils::extract_observable_type_t<TOtherObservable>...>>(m_combiner, in_subscriber.get_subscription());
auto state = std::make_shared<combine_latest_state_with_serialized_spinlock<TCombiner, Type, utils::extract_observable_type_t<TOtherObservable>...>>(m_combiner, in_subscriber.get_subscription());
// change subscriber to serialized to avoid manual using of mutex
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<std::mutex>{state, &state->mutex});
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<utils::spinlock>{state, &state->spinlock});

state->count_of_on_completed_needed.store(sizeof...(TOtherObservable) + 1, std::memory_order::relaxed);

Expand Down
11 changes: 7 additions & 4 deletions src/rpp/rpp/operators/concat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include <rpp/sources/just.hpp>
#include <rpp/utils/functors.hpp>
#include <rpp/utils/spinlock.hpp>


#include <mutex>
#include <memory>
Expand Down Expand Up @@ -114,11 +116,12 @@ struct concat_on_completed


template<constraint::decayed_type ValueType>
struct concat_state_with_serialized_mutex : concat_state<ValueType>
struct concat_state_with_serialized_spinlock : concat_state<ValueType>
{
using concat_state<ValueType>::concat_state;

std::mutex mutex{};
// we can use spinlock there because 99.9% of time only one ever thread would send values from on_next (only one active observable), but we have small probability to get error from main observable immediately
utils::spinlock spinlock{};
};

template<constraint::decayed_type Type>
Expand All @@ -129,10 +132,10 @@ struct concat_impl
template<constraint::subscriber_of_type<ValueType> TSub>
auto operator()(TSub&& in_subscriber) const
{
auto state = std::make_shared<concat_state_with_serialized_mutex<ValueType>>(in_subscriber.get_subscription());
auto state = std::make_shared<concat_state_with_serialized_spinlock<ValueType>>(in_subscriber.get_subscription());

// change subscriber to serialized to avoid manual using of mutex
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<std::mutex>{state, &state->mutex});
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<utils::spinlock>{state, &state->spinlock});

return create_subscriber_with_state<Type>(state->source_subscription,
concat_on_next_outer<ValueType>{},
Expand Down
11 changes: 7 additions & 4 deletions src/rpp/rpp/operators/switch_on_next.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <rpp/operators/merge.hpp>
#include <rpp/subscribers/constraints.hpp>
#include <rpp/utils/functors.hpp>
#include <rpp/utils/spinlock.hpp>


#include <atomic>
#include <memory>
Expand Down Expand Up @@ -75,11 +77,12 @@ struct switch_on_next_on_next

using switch_on_next_on_completed_outer = merge_on_completed;

struct switch_on_next_state_with_serialized_mutex : switch_on_next_state
struct switch_on_next_state_with_serialized_spinlock : switch_on_next_state
{
using switch_on_next_state::switch_on_next_state;

std::mutex mutex{};
// we can use spinlock there because 99.9% of time only one ever thread would send values from on_next (only one active observable), but we have small probability to get error from main observable immediately
utils::spinlock spinlock{};
};

template<constraint::decayed_type Type>
Expand All @@ -90,10 +93,10 @@ struct switch_on_next_impl
template<constraint::subscriber_of_type<ValueType> TSub>
auto operator()(TSub&& in_subscriber) const
{
auto state = std::make_shared<switch_on_next_state_with_serialized_mutex>(in_subscriber.get_subscription());
auto state = std::make_shared<switch_on_next_state_with_serialized_spinlock>(in_subscriber.get_subscription());

// change subscriber to serialized to avoid manual using of mutex
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<std::mutex>{state, &state->mutex});
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<utils::spinlock>{state, &state->spinlock});

state->count_of_on_completed_needed.fetch_add(1, std::memory_order::relaxed);

Expand Down
10 changes: 6 additions & 4 deletions src/rpp/rpp/operators/take_until.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <rpp/operators/fwd/take_until.hpp>
#include <rpp/subscribers/constraints.hpp>
#include <rpp/utils/functors.hpp>
#include <rpp/utils/spinlock.hpp>

#include <rpp/operators/details/subscriber_with_state.hpp> // create_subscriber_with_state

Expand Down Expand Up @@ -48,11 +49,12 @@ struct take_until_throttler_on_next
using take_until_throttler_on_error = take_until_on_error;
using take_until_throttler_on_completed = take_until_on_completed;

struct take_until_state_with_serialized_mutex : take_until_state
struct take_until_state_with_serialized_spinlock : take_until_state
{
using take_until_state::take_until_state;

std::mutex mutex{};
// we can use spinlock there because 99.9% of time only one ever thread would send values from on_next (main observable), but we have small probability to get error from "until observable" immediately
utils::spinlock spinlock{};
};
/**
* \brief "combine_latest" operator (an OperatorFn used by "lift").
Expand All @@ -67,9 +69,9 @@ struct take_until_impl
template<constraint::subscriber_of_type<Type> TSub>
auto operator()(TSub&& in_subscriber) const
{
auto state = std::make_shared<take_until_state_with_serialized_mutex>(in_subscriber.get_subscription());
auto state = std::make_shared<take_until_state_with_serialized_spinlock>(in_subscriber.get_subscription());
// change subscriber to serialized to avoid manual using of mutex
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<std::mutex>{state, &state->mutex});
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<utils::spinlock>{state, &state->spinlock});

// Subscribe to trigger observable
m_until_observable.subscribe(create_subscriber_with_state<TriggerType>(state->children_subscriptions.make_child(),
Expand Down
10 changes: 6 additions & 4 deletions src/rpp/rpp/operators/with_latest_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <rpp/subscribers/constraints.hpp>
#include <rpp/utils/utilities.hpp>
#include <rpp/utils/functors.hpp>
#include <rpp/utils/spinlock.hpp>

#include <mutex>
#include <array>
Expand Down Expand Up @@ -105,11 +106,12 @@ struct with_latest_from_on_next_outer
};

template<typename TSelector, constraint::decayed_type... ValueTypes>
struct with_latest_from_state_with_serialized_mutex : public with_latest_from_state<TSelector, ValueTypes...>
struct with_latest_from_state_with_serialized_spinlock : public with_latest_from_state<TSelector, ValueTypes...>
{
using with_latest_from_state<TSelector, ValueTypes...>::with_latest_from_state;

std::mutex mutex{};
// we can use spinlock there because 99.9% of time only one ever thread would send values from on_next (main observable), but we have small probability to get error from inner observables immediately
utils::spinlock spinlock{};
};

template<constraint::decayed_type Type, typename TSelector, constraint::observable ...TObservables>
Expand All @@ -124,9 +126,9 @@ struct with_latest_from_impl
template<constraint::subscriber_of_type<ResultType> TSub>
auto operator()(TSub&& in_subscriber) const
{
auto state = std::make_shared<with_latest_from_state_with_serialized_mutex<TSelector, utils::extract_observable_type_t<TObservables>...>>(selector, in_subscriber.get_subscription());
auto state = std::make_shared<with_latest_from_state_with_serialized_spinlock<TSelector, utils::extract_observable_type_t<TObservables>...>>(selector, in_subscriber.get_subscription());
// change subscriber to serialized to avoid manual using of mutex
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<std::mutex>{state, &state->mutex});
auto subscriber = make_serialized_subscriber(std::forward<TSub>(in_subscriber), std::shared_ptr<utils::spinlock>{state, &state->spinlock});

with_latest_from_subscribe_observables(std::index_sequence_for<TObservables...>{},
state,
Expand Down
36 changes: 36 additions & 0 deletions src/rpp/rpp/utils/spinlock.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// ReactivePlusPlus library
//
// Copyright Aleksey Loginov 2022 - present.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/victimsnino/ReactivePlusPlus

#pragma once
#include <atomic>

namespace rpp::utils
{
class spinlock
{
public:
spinlock() = default;

void lock()
{
while(m_lock_flag.exchange(true, std::memory_order_acquire))
{
while(m_lock_flag.load(std::memory_order_relaxed)){};
}
}

void unlock()
{
m_lock_flag.store(false, std::memory_order_release);
}

private:
std::atomic_bool m_lock_flag{false};
};
} // namespace rpp::utils

1 comment on commit cba61a5

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BENCHMARK RESULTS (AUTOGENERATED)

ci-ubuntu-clang

Observable construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable construction 0.35ns 0.334603 1.05 0.38ns
Dynamic observable construction 33.94ns 29.154 1.16 24.77ns
Specific observable construction + as_dynamic 24.33ns 29.1372 0.83 22.01ns

Observable lift

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable lift specific observer 117.01ns 112.721 1.04 292.93ns
Specific observable lift dynamic observer 164.09ns 134.534 1.22 362.40ns
Dynamic observable lift specific observer 188.65ns 186.552 1.01 363.13ns
Dynamic observable lift dynamic observer 169.73ns 198.748 0.85 392.06ns

Observable subscribe

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable subscribe specific observer 92.64ns 82.7434 1.12 270.75ns
Specific observable subscribe dynamic observer 91.53ns 94.4467 0.97 263.05ns
Dynamic observable subscribe specific observer 132.31ns 154.162 0.86 281.19ns
Dynamic observable subscribe dynamic observer 132.42ns 143.908 0.92 308.81ns

Observable subscribe #2

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable subscribe lambda 73.55ns 76.3895 0.96 292.70ns
Dynamic observable subscribe lambda 157.89ns 144.087 1.10 366.93ns
Specific observable subscribe lambda without subscription 84.83ns 76.7121 1.11 352.03ns
Dynamic observable subscribe lambda without subscription 156.03ns 141.965 1.10 331.81ns
Specific observable subscribe specific subscriber 40.22ns 43.1114 0.93 235.16ns
Dynamic observable subscribe specific subscriber 126.15ns 109.539 1.15 315.76ns
Specific observable subscribe dynamic observer 45.15ns 43.6412 1.03 291.38ns
Dynamic observable subscribe dynamic observer 87.74ns 96.3358 0.91 309.59ns

Observer construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observer construction 0.36ns 0.334762 1.06 0.34ns
Dynamic observer construction 26.78ns 29.1294 0.92 17.96ns
Specific observer construction + as_dynamic 24.27ns 29.2523 0.83 24.97ns

OnNext

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observer OnNext 0.65ns 0.668973 0.98 0.62ns
Dynamic observer OnNext 1.66ns 1.67284 0.99 2.47ns

Subscriber construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Make subsriber 34.98ns 33.9069 1.03 75.84ns
Make copy of subscriber 17.46ns 16.748 1.04 4.71ns
Transform subsriber to dynamic 51.05ns 43.5782 1.17 27.31ns

Subscription

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
composite_subscription create 39.24ns 33.8866 1.16 62.50ns
composite_subscription add 55.02ns 50.0007 1.10 111.59ns
composite_subscription unsubscribe 49.87ns 44.1952 1.13 22.98ns

buffer

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
buffer 313.33ns 269.023 1.16 1807.13ns
sending of values from observable via buffer to subscriber 6.18ns 6.46221 0.96 30.74ns

chains creation test

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
long non-state chain creation + subscribe 274.11ns 270.896 1.01 465.40ns
long stateful chain creation + subscribe 468.63ns 401.263 1.17 947.62ns

combine_latest

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
combine_latest construction from observable via dot + subscribe 930.94ns 913.256 1.02 692.61ns
sending of values from observable via combine_latest to subscriber 32.24ns 36.5322 0.88 2.79ns

concat

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
concat 1597.95ns 1900.22 0.84 3673.43ns
concat_with 2266.52ns 2242.61 1.01 3355.58ns

distinct_until_changed

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
distinct_until_changed construction from observable via dot + subscribe 104.21ns 127.803 0.82 307.13ns
sending of values from observable via distinct_until_changed to subscriber 3.16ns 2.34511 1.35 1.31ns

first

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
first construction from observable via dot + subscribe 141.47ns 148.925 0.95 671.13ns
sending of values from observable via first to subscriber 0.68ns 0.66995 1.01 0.65ns

foundamental sources

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
empty 82.64ns 84.9886 0.97 689.00ns
error 146.26ns 137.583 1.06 758.03ns
never 54.50ns 47.009 1.16 246.19ns

from

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
from vector with int 102.45ns 101.428 1.01 716.68ns

immediate scheduler

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no any re-schedule 1.23ns 0.670804 1.84 132.30ns
re-schedule 10 times 13.82ns 7.1721 1.93 153.41ns

just

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
just send int 95.48ns 88.4067 1.08 753.31ns
just send variadic 115.27ns 118.978 0.97 894.06ns

last

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
last construction from observable via dot + subscribe 192.00ns 186.803 1.03 390.06ns
sending of values from observable via last to subscriber 2.52ns 2.53964 0.99 1.41ns

map

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
map construction from observable via dot + subscribe 96.15ns 89.1049 1.08 233.66ns
sending of values from observable via map to subscriber 1.09ns 1.00333 1.08 2.21ns

merge

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
merge 1947.70ns 1836.68 1.06 3569.73ns
merge_with 1904.44ns 2147.89 0.89 3817.53ns

observe_on

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
observe_on construction from observable via dot + subscribe 680.88ns 580.461 1.17 3068.66ns
sending of values from observable via observe_on to subscriber 100.82ns 89.1905 1.13 215.84ns

publish_subject callbacks

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
on_next 28.73ns 24.1208 1.19 12.66ns
on_error 0.58ns 0.674628 0.85 18.47ns
on_completed 0.56ns 0.673973 0.83 0.81ns

publish_subject routines

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
construct 157.64ns 198.239 0.80 209.27ns
get_observable 26.77ns 29.0693 0.92 43.13ns
get_subscriber 63.29ns 60.5513 1.05 14.71ns

repeat

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
repeat construction from observable via dot + subscribe 3772.53ns 3911.56 0.96 2964.57ns

scan

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
scan construction from observable via dot + subscribe 118.70ns 126.376 0.94 254.23ns
sending of values from observable via scan to subscriber 2.10ns 2.0084 1.05 2.61ns

single-threaded locks

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no-lock increment 2.25ns None . .
mutex lock increment 20.70ns None . .
spin-lock increment 7.54ns None . .

skip

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
skip construction from observable via dot + subscribe 141.16ns 120.418 1.17 475.39ns
sending of values from observable via skip to subscriber 2.17ns 2.01221 1.08 2.38ns

switch_on_next

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
switch_on_next construction from observable via dot + subscribe 2295.27ns 2246.27 1.02 3159.14ns
sending of values from observable via switch_on_next to subscriber 519.61ns 575.495 0.90 761.12ns

take

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take construction from observable via dot + subscribe 185.89ns 187.428 0.99 530.24ns
sending of values from observable via take to subscriber 2.84ns 2.34594 1.21 3.21ns

take_last

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take_last construction from observable via dot + subscribe 238.42ns 232.576 1.03 494.08ns
sending of values from observable via take_last to subscriber 4.08ns 3.3684 1.21 3.56ns

take_until

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take_until construction from observable via dot + subscribe 1008.80ns 1064.36 0.95 1265.78ns
sending of values from observable via take_until to subscriber 10.61ns 18.0997 0.59 1.49ns

trampoline scheduler

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no any re-schedule 14.15ns 11.7316 1.21 158.91ns
re-schedule 10 times 27.90ns 30.5396 0.91 222.28ns
recursively schedule 10 times 1166.79ns 1404.93 0.83 6409.92ns

window

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
window 1939.95ns 2019.34 0.96 3588.36ns
sending of values from observable via window to subscriber 614.72ns 548.548 1.12 388.53ns

with_latest_from

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
with_latest_from construction from observable via dot + subscribe 1118.97ns 1053.38 1.06 1301.69ns
sending of values from observable via with_latest_from to subscriber 31.17ns 35.1555 0.89 3.03ns

ci-ubuntu-gcc

Observable construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable construction 0.34ns 0.388296 0.86 0.33ns
Dynamic observable construction 31.56ns 29.2855 1.08 23.23ns
Specific observable construction + as_dynamic 31.57ns 28.546 1.11 23.43ns

Observable lift

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable lift specific observer 118.85ns 132.925 0.89 332.02ns
Specific observable lift dynamic observer 144.97ns 168.105 0.86 352.48ns
Dynamic observable lift specific observer 198.99ns 287.358 0.69 387.80ns
Dynamic observable lift dynamic observer 208.15ns 253.836 0.82 370.35ns

Observable subscribe

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable subscribe specific observer 82.76ns 90.0362 0.92 327.00ns
Specific observable subscribe dynamic observer 95.49ns 112.666 0.85 332.88ns
Dynamic observable subscribe specific observer 150.01ns 157.572 0.95 366.97ns
Dynamic observable subscribe dynamic observer 146.10ns 164.447 0.89 346.73ns

Observable subscribe #2

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable subscribe lambda 83.41ns 91.4396 0.91 325.66ns
Dynamic observable subscribe lambda 152.06ns 276.078 0.55 379.85ns
Specific observable subscribe lambda without subscription 83.61ns 84.1895 0.99 322.62ns
Dynamic observable subscribe lambda without subscription 152.40ns 175.449 0.87 793.51ns
Specific observable subscribe specific subscriber 50.24ns 52.3777 0.96 277.26ns
Dynamic observable subscribe specific subscriber 118.05ns 133.737 0.88 315.84ns
Specific observable subscribe dynamic observer 50.07ns 66.9864 0.75 279.53ns
Dynamic observable subscribe dynamic observer 104.67ns 100.741 1.04 288.40ns

Observer construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observer construction 0.34ns 0.338249 0.99 0.34ns
Dynamic observer construction 31.49ns 31.4899 1.00 20.91ns
Specific observer construction + as_dynamic 28.41ns 32.3827 0.88 20.11ns

OnNext

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observer OnNext 0.34ns 0.429861 0.78 0.33ns
Dynamic observer OnNext 2.01ns 2.38629 0.84 2.01ns

Subscriber construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Make subsriber 34.58ns 36.774 0.94 62.34ns
Make copy of subscriber 16.71ns 22.109 0.76 4.49ns
Transform subsriber to dynamic 44.81ns 42.3211 1.06 25.37ns

Subscription

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
composite_subscription create 34.48ns 39.2359 0.88 55.14ns
composite_subscription add 46.84ns 60.8483 0.77 98.24ns
composite_subscription unsubscribe 41.89ns 45.7916 0.91 22.01ns

buffer

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
buffer 253.96ns 354.532 0.72 1895.58ns
sending of values from observable via buffer to subscriber 6.25ns 10.4672 0.60 30.49ns

chains creation test

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
long non-state chain creation + subscribe 301.10ns 349.278 0.86 684.37ns
long stateful chain creation + subscribe 437.12ns 468.822 0.93 1066.97ns

combine_latest

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
combine_latest construction from observable via dot + subscribe 945.10ns 975.969 0.97 1128.56ns
sending of values from observable via combine_latest to subscriber 27.44ns 44.8896 0.61 2.34ns

concat

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
concat 1927.33ns 2222.16 0.87 3627.92ns
concat_with 2284.33ns 2715.86 0.84 4109.43ns

distinct_until_changed

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
distinct_until_changed construction from observable via dot + subscribe 145.45ns 161.313 0.90 356.48ns
sending of values from observable via distinct_until_changed to subscriber 3.35ns 4.18108 0.80 1.35ns

first

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
first construction from observable via dot + subscribe 161.59ns 172.915 0.93 696.94ns
sending of values from observable via first to subscriber 0.67ns 0.694021 0.97 1.01ns

foundamental sources

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
empty 87.83ns 98.5537 0.89 735.39ns
error 130.59ns 156.887 0.83 840.23ns
never 49.28ns 59.207 0.83 281.38ns

from

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
from vector with int 107.63ns 151.296 0.71 771.34ns

immediate scheduler

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no any re-schedule 1.68ns 1.09528 1.53 130.07ns
re-schedule 10 times 22.81ns 22.0975 1.03 163.75ns

just

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
just send int 90.70ns 95.5158 0.95 760.53ns
just send variadic 127.09ns 134.16 0.95 813.55ns

last

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
last construction from observable via dot + subscribe 196.03ns 217.66 0.90 427.32ns
sending of values from observable via last to subscriber 2.68ns 3.28116 0.82 1.34ns

map

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
map construction from observable via dot + subscribe 100.83ns 126.71 0.80 340.69ns
sending of values from observable via map to subscriber 0.84ns 0.99762 0.84 2.34ns

merge

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
merge 1884.52ns 2017.95 0.93 3820.30ns
merge_with 2242.02ns 2651.02 0.85 4030.30ns

observe_on

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
observe_on construction from observable via dot + subscribe 609.76ns 655.735 0.93 2802.85ns
sending of values from observable via observe_on to subscriber 87.55ns 89.961 0.97 238.74ns

publish_subject callbacks

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
on_next 22.75ns 25.8022 0.88 9.95ns
on_error 0.67ns 0.747282 0.90 16.83ns
on_completed 0.68ns 0.751245 0.90 1.01ns

publish_subject routines

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
construct 208.85ns 219.328 0.95 172.26ns
get_observable 29.44ns 27.7262 1.06 48.43ns
get_subscriber 57.32ns 63.4691 0.90 23.12ns

repeat

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
repeat construction from observable via dot + subscribe 4171.41ns 4449.03 0.94 3245.94ns

scan

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
scan construction from observable via dot + subscribe 137.13ns 178.284 0.77 383.28ns
sending of values from observable via scan to subscriber 3.66ns 4.25276 0.86 1.68ns

single-threaded locks

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no-lock increment 2.01ns None . .
mutex lock increment 18.08ns None . .
spin-lock increment 9.04ns None . .

skip

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
skip construction from observable via dot + subscribe 136.53ns 156.148 0.87 539.62ns
sending of values from observable via skip to subscriber 3.01ns 3.2818 0.92 2.35ns

switch_on_next

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
switch_on_next construction from observable via dot + subscribe 2361.00ns 2903.02 0.81 4299.90ns
sending of values from observable via switch_on_next to subscriber 598.61ns 639.108 0.94 1104.57ns

take

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take construction from observable via dot + subscribe 192.09ns 208.082 0.92 581.10ns
sending of values from observable via take to subscriber 3.68ns 4.70523 0.78 4.07ns

take_last

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take_last construction from observable via dot + subscribe 230.94ns 252.174 0.92 629.11ns
sending of values from observable via take_last to subscriber 3.07ns 4.00412 0.77 5.93ns

take_until

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take_until construction from observable via dot + subscribe 1088.70ns 1261.97 0.86 1611.28ns
sending of values from observable via take_until to subscriber 10.45ns 26.4153 0.40 1.78ns

trampoline scheduler

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no any re-schedule 20.00ns 24.3213 0.82 183.69ns
re-schedule 10 times 49.40ns 55.5911 0.89 213.26ns
recursively schedule 10 times 1434.88ns 1472.06 0.97 6382.07ns

window

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
window 2163.17ns 2412.69 0.90 3381.84ns
sending of values from observable via window to subscriber 582.41ns 637.635 0.91 415.00ns

with_latest_from

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
with_latest_from construction from observable via dot + subscribe 1119.35ns 1257.63 0.89 1410.96ns
sending of values from observable via with_latest_from to subscriber 25.43ns 43.1247 0.59 3.91ns

ci-windows

Observable construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable construction 1.50ns 1.50394 1.00 0.67ns
Dynamic observable construction 81.64ns 80.1981 1.02 122.73ns
Specific observable construction + as_dynamic 81.47ns 79.9683 1.02 122.83ns

Observable lift

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable lift specific observer 152.88ns 173.641 0.88 1202.41ns
Specific observable lift dynamic observer 202.84ns 200.856 1.01 1262.52ns
Dynamic observable lift specific observer 302.41ns 301.424 1.00 1400.21ns
Dynamic observable lift dynamic observer 261.23ns 261.216 1.00 1315.60ns

Observable subscribe

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable subscribe specific observer 138.22ns 141.773 0.97 1160.74ns
Specific observable subscribe dynamic observer 153.44ns 152.261 1.01 1208.73ns
Dynamic observable subscribe specific observer 257.28ns 254.929 1.01 1353.95ns
Dynamic observable subscribe dynamic observer 201.61ns 201.627 1.00 1241.00ns

Observable subscribe #2

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observable subscribe lambda 138.75ns 139.536 0.99 1034.14ns
Dynamic observable subscribe lambda 258.32ns 255.444 1.01 1346.55ns
Specific observable subscribe lambda without subscription 138.01ns 138.762 0.99 1171.05ns
Dynamic observable subscribe lambda without subscription 258.14ns 255.61 1.01 1349.53ns
Specific observable subscribe specific subscriber 49.29ns 56.8093 0.87 841.64ns
Dynamic observable subscribe specific subscriber 166.26ns 165.045 1.01 1018.70ns
Specific observable subscribe dynamic observer 52.51ns 52.2651 1.00 767.88ns
Dynamic observable subscribe dynamic observer 111.96ns 101.205 1.11 913.07ns

Observer construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observer construction 1.50ns 1.50546 1.00 1.50ns
Dynamic observer construction 83.66ns 81.8548 1.02 111.77ns
Specific observer construction + as_dynamic 88.04ns 82.1042 1.07 111.76ns

OnNext

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Specific observer OnNext 0.67ns 0.67004 1.00 0.67ns
Dynamic observer OnNext 1.79ns 2.035 0.88 2.02ns

Subscriber construction

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
Make subsriber 85.30ns 84.9799 1.00 350.59ns
Make copy of subscriber 16.72ns 16.7069 1.00 31.63ns
Transform subsriber to dynamic 97.64ns 96.1856 1.02 151.13ns

Subscription

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
composite_subscription create 85.13ns 84.8328 1.00 341.76ns
composite_subscription add 71.81ns 71.1626 1.01 158.50ns
composite_subscription unsubscribe 62.63ns 63.7481 0.98 122.25ns

buffer

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
buffer 388.18ns 379.18 1.02 4461.33ns
sending of values from observable via buffer to subscriber 6.45ns 7.43022 0.87 132.56ns

chains creation test

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
long non-state chain creation + subscribe 284.40ns 286.244 0.99 1713.29ns
long stateful chain creation + subscribe 682.20ns 694.314 0.98 3155.62ns

combine_latest

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
combine_latest construction from observable via dot + subscribe 1675.67ns 1680.36 1.00 3053.75ns
sending of values from observable via combine_latest to subscriber 44.56ns 61.449 0.73 3.80ns

concat

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
concat 2775.30ns 2818.89 0.98 10585.70ns
concat_with 3474.62ns 3529.88 0.98 11497.30ns

distinct_until_changed

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
distinct_until_changed construction from observable via dot + subscribe 202.32ns 202.102 1.00 1018.28ns
sending of values from observable via distinct_until_changed to subscriber 3.68ns 4.28377 0.86 3.62ns

first

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
first construction from observable via dot + subscribe 161.38ns 147.06 1.10 2613.10ns
sending of values from observable via first to subscriber 2.34ns 2.53777 0.92 1.75ns

foundamental sources

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
empty 83.30ns 82.0044 1.02 2382.36ns
error 139.57ns 138.628 1.01 2459.30ns
never 50.91ns 51.0468 1.00 877.92ns

from

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
from vector with int 144.56ns 179.395 0.81 2462.64ns

immediate scheduler

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no any re-schedule 1.55ns 1.84403 0.84 405.38ns
re-schedule 10 times 97.77ns 97.7297 1.00 438.73ns

just

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
just send int 89.60ns 88.6683 1.01 2398.18ns
just send variadic 130.53ns 129.987 1.00 2477.10ns

last

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
last construction from observable via dot + subscribe 244.82ns 243.144 1.01 1431.88ns
sending of values from observable via last to subscriber 2.83ns 3.19671 0.89 2.96ns

map

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
map construction from observable via dot + subscribe 106.78ns 106.5 1.00 984.35ns
sending of values from observable via map to subscriber 3.66ns 4.01112 0.91 6.33ns

merge

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
merge 2679.70ns 2690.1 1.00 10928.30ns
merge_with 3459.62ns 3413.5 1.01 10253.00ns

observe_on

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
observe_on construction from observable via dot + subscribe 829.57ns 828.067 1.00 5952.00ns
sending of values from observable via observe_on to subscriber 85.87ns 85.2881 1.01 846.49ns

publish_subject callbacks

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
on_next 20.23ns 20.1139 1.01 32.73ns
on_error 3.04ns 3.05411 1.00 18.48ns
on_completed 3.04ns 2.45031 1.24 0.68ns

publish_subject routines

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
construct 362.89ns 376.787 0.96 590.15ns
get_observable 28.72ns 26.0838 1.10 164.19ns
get_subscriber 50.16ns 50.1726 1.00 91.74ns

repeat

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
repeat construction from observable via dot + subscribe 7005.20ns 6107.0 1.15 11247.30ns

scan

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
scan construction from observable via dot + subscribe 204.59ns 206.975 0.99 1241.95ns
sending of values from observable via scan to subscriber 5.71ns 6.01993 0.95 8.88ns

single-threaded locks

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no-lock increment 1.88ns None . .
mutex lock increment 22.72ns None . .
spin-lock increment 9.60ns None . .

skip

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
skip construction from observable via dot + subscribe 195.91ns 194.698 1.01 1532.19ns
sending of values from observable via skip to subscriber 4.76ns 3.72654 1.28 3.35ns

switch_on_next

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
switch_on_next construction from observable via dot + subscribe 3544.88ns 3626.0 0.98 12022.30ns
sending of values from observable via switch_on_next to subscriber 856.07ns 870.667 0.98 3126.33ns

take

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take construction from observable via dot + subscribe 248.89ns 248.578 1.00 2145.00ns
sending of values from observable via take to subscriber 5.67ns 6.11726 0.93 6.15ns

take_last

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take_last construction from observable via dot + subscribe 353.91ns 343.591 1.03 2435.45ns
sending of values from observable via take_last to subscriber 4.39ns 4.4497 0.99 21.02ns

take_until

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
take_until construction from observable via dot + subscribe 1614.62ns 1637.94 0.99 5185.60ns
sending of values from observable via take_until to subscriber 11.13ns 29.6388 0.38 4.78ns

trampoline scheduler

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
no any re-schedule 18.80ns 21.5218 0.87 613.67ns
re-schedule 10 times 120.89ns 121.019 1.00 641.60ns
recursively schedule 10 times 3153.80ns 2628.8 1.20 18941.50ns

window

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
window 2976.78ns 2989.67 1.00 9732.33ns
sending of values from observable via window to subscriber 737.09ns 842.806 0.87 1798.00ns

with_latest_from

Table
Test Name Current, ns Prev, ns Ratio RxCpp current, ns
with_latest_from construction from observable via dot + subscribe 2023.17ns 2022.67 1.00 3710.17ns
sending of values from observable via with_latest_from to subscriber 37.76ns 56.9022 0.66 6.68ns

Please sign in to comment.