From 21c97b496e462f39bdf4ddf8426ea2fb071e3395 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Sun, 11 Oct 2020 10:13:23 +0200 Subject: [PATCH] Adds a stride scheduler to split bandwidth between two sources of traffic. (#443) --- src/utils/BandwidthMerger.cxxtest | 92 +++++++++++++++++++++++++++++++ src/utils/BandwidthMerger.hxx | 89 ++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/utils/BandwidthMerger.cxxtest create mode 100644 src/utils/BandwidthMerger.hxx diff --git a/src/utils/BandwidthMerger.cxxtest b/src/utils/BandwidthMerger.cxxtest new file mode 100644 index 000000000..71720c578 --- /dev/null +++ b/src/utils/BandwidthMerger.cxxtest @@ -0,0 +1,92 @@ +/** \copyright + * Copyright (c) 2020, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file BandwidthMerger.cxxtest + * + * Unit tests for the stride scheduler. + * + * @author Balazs Racz + * @date 10 Oct 2020 + */ + +#include "trains/BandwidthMerger.hxx" + +#include "utils/test_main.hxx" + +class BandwidthMergerTest : public ::testing::Test +{ +protected: + /// Runs a simulation with a certain number of steps with a bandwidth + /// merger. + void collect_stats(uint8_t percent, unsigned num_steps) + { + BandwidthMerger m {percent}; + results_[0] = results_[1] = 0; + for (unsigned i = 0; i < num_steps; ++i) + { + if (m.step()) + { + ++results_[1]; + } + else + { + ++results_[0]; + } + } + } + + /// Results of the simulation. [0] is the number of steps with false + /// output, [1] is the number of steps with true output. + unsigned results_[2]; +}; + +TEST_F(BandwidthMergerTest, fifty) +{ + collect_stats(50, 34); + EXPECT_EQ(17u, results_[0]); + EXPECT_EQ(17u, results_[1]); +} + +TEST_F(BandwidthMergerTest, hundred) +{ + collect_stats(100, 34); + EXPECT_EQ(0u, results_[0]); + EXPECT_EQ(34u, results_[1]); +} + +TEST_F(BandwidthMergerTest, zero) +{ + collect_stats(0, 34); + EXPECT_EQ(34u, results_[0]); + EXPECT_EQ(0u, results_[1]); +} + +TEST_F(BandwidthMergerTest, ten) +{ + collect_stats(10, 34); + EXPECT_EQ(31u, results_[0]); + EXPECT_EQ(3u, results_[1]); +} diff --git a/src/utils/BandwidthMerger.hxx b/src/utils/BandwidthMerger.hxx new file mode 100644 index 000000000..096fd1402 --- /dev/null +++ b/src/utils/BandwidthMerger.hxx @@ -0,0 +1,89 @@ +/** \copyright + * Copyright (c) 2020, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file BandwidthMerger.hxx + * + * Simple stride scheduler for splitting bandwidth. + * + * @author Balazs Racz + * @date 10 Oct 2020 + */ + +#ifndef _TRAINS_BANDWIDTHMERGER_HXX_ +#define _TRAINS_BANDWIDTHMERGER_HXX_ + +#include + +#include "utils/macros.h" + +/// Simple stride scheduler. Emulates a two-way split of bandwidth between a +/// first source and a second source. The percentage assigned to the first +/// source is a parameter. Over time the fraction of outputs selected from the +/// first source converges to this percentage. +struct BandwidthMerger +{ + /// Constructor. + /// @param percent is the percentage (0..100) of the bandwidth that + /// should be assigned to the first source. + BandwidthMerger(uint8_t percent) + : percentFirst_(percent) + { + HASSERT(percentFirst_ <= 100); + } + /// Runs one step. + /// @return true if the first source is selected in this step. + bool step() + { + currentState_ += percentFirst_; + if (currentState_ >= 100) + { + currentState_ -= 100; + return true; + } + return false; + } + + /// If the step function returned false, but still the first source was + /// taken (e.g. because the second source was empty), call this function to + /// clear the state. This will avoid selecting the first source twice in a + /// row for example. + void reset() + { + // Reduces the state by 100 but clips it to zero. Since the state is + // always < 100, the clipping will always win. + currentState_ = 0; + } + + /// State of the current stride. This is always between 0..99. It + /// represents the fractional steps that the first source has accumulated + /// but not paid out in the form of selections yet. + uint8_t currentState_ {0}; + /// Percentage of the bandwidth that should be assigned to the first + /// source. Range 0..100. + uint8_t percentFirst_; +}; + +#endif // _TRAINS_BANDWIDTHMERGER_HXX_