catchunks: Implement CUBIC window adaptation
Also increase the RTT estimator multiplier k to 8. Change-Id: I68c5096ac0da854f071bab5f7519b1c144f20ca1 refs: #4861
This commit is contained in:
1 parent
bf2c51716a
commit
9e5122bafc
11 files changed
+1029
-108
No files matched your search
+7
-7
@@ -25,7 +25,7 @@
|
||||
* @author Klaus Schneider
|
||||
*/
|
||||
|
||||
#include "tools/chunks/catchunks/pipeline-interests-adaptive.hpp"
|
||||
#include "tools/chunks/catchunks/pipeline-interests-aimd.hpp"
|
||||
#include "tools/chunks/catchunks/options.hpp"
|
||||
|
||||
#include "pipeline-interests-fixture.hpp"
|
||||
@@ -36,10 +36,10 @@ namespace tests {
|
||||
|
||||
using namespace ndn::tests;
|
||||
|
||||
class PipelineInterestAdaptiveFixture : public PipelineInterestsFixture
|
||||
class PipelineInterestAimdFixture : public PipelineInterestsFixture
|
||||
{
|
||||
public:
|
||||
PipelineInterestAdaptiveFixture()
|
||||
PipelineInterestAimdFixture()
|
||||
: opt(makePipelineOptions())
|
||||
, rttEstimator(makeRttEstimatorOptions())
|
||||
{
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
void
|
||||
createPipeline()
|
||||
{
|
||||
auto pline = make_unique<PipelineInterestsAdaptive>(face, rttEstimator, opt);
|
||||
auto pline = make_unique<PipelineInterestsAimd>(face, rttEstimator, opt);
|
||||
pipeline = pline.get();
|
||||
setPipeline(std::move(pline));
|
||||
}
|
||||
@@ -90,10 +90,10 @@ protected:
|
||||
static constexpr double MARGIN = 0.01;
|
||||
};
|
||||
|
||||
constexpr double PipelineInterestAdaptiveFixture::MARGIN;
|
||||
constexpr double PipelineInterestAimdFixture::MARGIN;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Chunks)
|
||||
BOOST_FIXTURE_TEST_SUITE(TestPipelineInterestsAdaptive, PipelineInterestAdaptiveFixture)
|
||||
BOOST_FIXTURE_TEST_SUITE(TestPipelineInterestsAimd, PipelineInterestAimdFixture)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SlowStart)
|
||||
{
|
||||
@@ -608,7 +608,7 @@ BOOST_AUTO_TEST_CASE(StopsWhenFileSizeLessThanChunkSize)
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterestsAdaptive
|
||||
BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterestsAimd
|
||||
BOOST_AUTO_TEST_SUITE_END() // Chunks
|
||||
|
||||
} // namespace tests
|
||||
@@ -0,0 +1,585 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, Regents of the University of California,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University.
|
||||
*
|
||||
* This file is part of ndn-tools (Named Data Networking Essential Tools).
|
||||
* See AUTHORS.md for complete list of ndn-tools authors and contributors.
|
||||
*
|
||||
* ndn-tools is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
|
||||
*
|
||||
* @author Weiwei Liu
|
||||
* @author Chavoosh Ghasemi
|
||||
* @author Klaus Schneider
|
||||
*/
|
||||
|
||||
#include "tools/chunks/catchunks/pipeline-interests-cubic.hpp"
|
||||
#include "tools/chunks/catchunks/options.hpp"
|
||||
|
||||
#include "pipeline-interests-fixture.hpp"
|
||||
|
||||
namespace ndn {
|
||||
namespace chunks {
|
||||
namespace tests {
|
||||
|
||||
using namespace ndn::tests;
|
||||
|
||||
class PipelineInterestCubicFixture : public PipelineInterestsFixture
|
||||
{
|
||||
public:
|
||||
PipelineInterestCubicFixture()
|
||||
: opt(makePipelineOptions())
|
||||
, rttEstimator(makeRttEstimatorOptions())
|
||||
{
|
||||
createPipeline();
|
||||
}
|
||||
|
||||
void
|
||||
createPipeline()
|
||||
{
|
||||
auto pline = make_unique<PipelineInterestsCubic>(face, rttEstimator, opt);
|
||||
pipeline = pline.get();
|
||||
setPipeline(std::move(pline));
|
||||
}
|
||||
|
||||
private:
|
||||
static PipelineInterestsCubic::Options
|
||||
makePipelineOptions()
|
||||
{
|
||||
PipelineInterestsCubic::Options pipelineOptions;
|
||||
pipelineOptions.isQuiet = true;
|
||||
pipelineOptions.isVerbose = false;
|
||||
pipelineOptions.disableCwa = false;
|
||||
pipelineOptions.ignoreCongMarks = false;
|
||||
pipelineOptions.resetCwndToInit = false;
|
||||
pipelineOptions.initCwnd = 1.0;
|
||||
pipelineOptions.aiStep = 1.0;
|
||||
pipelineOptions.mdCoef = 0.5;
|
||||
pipelineOptions.initSsthresh = std::numeric_limits<int>::max();
|
||||
pipelineOptions.cubicBeta = 0.7;
|
||||
pipelineOptions.enableFastConv = false;
|
||||
return pipelineOptions;
|
||||
}
|
||||
|
||||
static RttEstimator::Options
|
||||
makeRttEstimatorOptions()
|
||||
{
|
||||
RttEstimator::Options rttOptions;
|
||||
rttOptions.alpha = 0.125;
|
||||
rttOptions.beta = 0.25;
|
||||
rttOptions.k = 8;
|
||||
rttOptions.minRto = Milliseconds(200);
|
||||
rttOptions.maxRto = Milliseconds(4000);
|
||||
return rttOptions;
|
||||
}
|
||||
|
||||
protected:
|
||||
PipelineInterestsCubic::Options opt;
|
||||
RttEstimator rttEstimator;
|
||||
PipelineInterestsCubic* pipeline;
|
||||
static constexpr double MARGIN = 0.01;
|
||||
};
|
||||
|
||||
constexpr double PipelineInterestCubicFixture::MARGIN;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Chunks)
|
||||
BOOST_FIXTURE_TEST_SUITE(TestPipelineInterestsCubic, PipelineInterestCubicFixture)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SlowStart)
|
||||
{
|
||||
nDataSegments = 4;
|
||||
BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 1, MARGIN);
|
||||
|
||||
double preCwnd = pipeline->m_cwnd;
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
|
||||
|
||||
for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
|
||||
face.receive(*makeDataWithSegment(i));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd - preCwnd, 1, MARGIN);
|
||||
preCwnd = pipeline->m_cwnd;
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments - 1);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Timeout)
|
||||
{
|
||||
nDataSegments = 8;
|
||||
BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 1, MARGIN);
|
||||
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
|
||||
|
||||
// receive segment 0, 1, and 2
|
||||
for (uint64_t i = 0; i < 3; ++i) {
|
||||
face.receive(*makeDataWithSegment(i));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 3);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4, MARGIN);
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 7); // request for segment 7 has been sent
|
||||
|
||||
advanceClocks(io, time::milliseconds(100));
|
||||
|
||||
// receive segment 4
|
||||
face.receive(*makeDataWithSegment(4));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 5
|
||||
face.receive(*makeDataWithSegment(5));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 6.0, MARGIN);
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), nDataSegments); // all the segment requests have been sent
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nTimeouts, 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nLossDecr, 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nMarkDecr, 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nRetransmitted, 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nSkippedRetx, 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nCongMarks, 0);
|
||||
|
||||
// timeout segment 3 & 6
|
||||
advanceClocks(io, time::milliseconds(150));
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nTimeouts, 2);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nRetransmitted, 2);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nLossDecr, 1);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nSkippedRetx, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.2, MARGIN); // window size drop to 0.7x of previous size
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxQueue.size(), 0);
|
||||
|
||||
// receive segment 6, retransmit 3
|
||||
face.receive(*makeDataWithSegment(6));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.2, MARGIN); // congestion avoidance
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxQueue.size(), 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[3], 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nTimeouts, 2);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nRetransmitted, 2);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nTimeouts,
|
||||
pipeline->m_nRetransmitted + pipeline->m_nSkippedRetx);
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CongestionMarksWithCwa)
|
||||
{
|
||||
nDataSegments = 7;
|
||||
BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 1, MARGIN);
|
||||
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
|
||||
|
||||
// receive segments 0 to 4
|
||||
for (uint64_t i = 0; i < 5; ++i) {
|
||||
face.receive(*makeDataWithSegment(i));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 6.0, MARGIN);
|
||||
|
||||
// receive segment 5 with congestion mark
|
||||
face.receive(*makeDataWithSegmentAndCongMark(5));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.2, MARGIN); // window size drops to 1/2 of previous size
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), nDataSegments); // all interests have been sent
|
||||
|
||||
// receive the last segment with congestion mark
|
||||
face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.2, MARGIN); // conservative window adaption (window size should not decrease)
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxQueue.size(), 0);
|
||||
|
||||
// make sure no interest is retransmitted for marked data packets
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[5], 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[nDataSegments - 1], 0);
|
||||
|
||||
// check number of received marked data packets
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nCongMarks, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CongestionMarksWithoutCwa)
|
||||
{
|
||||
opt.disableCwa = true;
|
||||
createPipeline();
|
||||
|
||||
nDataSegments = 7;
|
||||
BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 1, MARGIN);
|
||||
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
|
||||
|
||||
// receive segments 0 to 4
|
||||
for (uint64_t i = 0; i < 5; ++i) {
|
||||
face.receive(*makeDataWithSegment(i));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 6.0, MARGIN);
|
||||
|
||||
// receive segment 5 with congestion mark
|
||||
face.receive(*makeDataWithSegmentAndCongMark(5));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.2, MARGIN); // window size drops to 1/2 of previous size
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), nDataSegments); // all interests have been sent
|
||||
|
||||
// receive the last segment with congestion mark
|
||||
face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 2.94,
|
||||
MARGIN); // window size should decrease, as cwa is disabled
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxQueue.size(), 0);
|
||||
|
||||
// make sure no interest is retransmitted for marked data packets
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[5], 0);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[nDataSegments - 1], 0);
|
||||
|
||||
// check number of received marked data packets
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nCongMarks, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(IgnoreCongestionMarks)
|
||||
{
|
||||
opt.ignoreCongMarks = true;
|
||||
createPipeline();
|
||||
|
||||
nDataSegments = 7;
|
||||
BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 1, MARGIN);
|
||||
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
|
||||
|
||||
// receive segments 0 to 5
|
||||
for (uint64_t i = 0; i < 6; ++i) {
|
||||
face.receive(*makeDataWithSegment(i));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 7.0, MARGIN);
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), nDataSegments); // all interests have been sent
|
||||
|
||||
// receive the last segment with congestion mark
|
||||
face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
|
||||
BOOST_CHECK_CLOSE(pipeline->m_cwnd, 8.0, MARGIN); // window size increases
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxQueue.size(), 0);
|
||||
|
||||
// make sure no interest is retransmitted for marked data packet
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[nDataSegments - 1], 0);
|
||||
|
||||
// check number of received marked data packets
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nCongMarks, 1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Nack)
|
||||
{
|
||||
nDataSegments = 5;
|
||||
pipeline->m_cwnd = 10.0;
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
face.receive(*makeDataWithSegment(0));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
face.receive(*makeDataWithSegment(1));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
|
||||
BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 10);
|
||||
|
||||
// receive a nack with NackReason::DUPLICATE for segment 1
|
||||
auto nack1 = makeNack(face.sentInterests[1], lp::NackReason::DUPLICATE);
|
||||
face.receive(nack1);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// nack1 is ignored
|
||||
BOOST_CHECK_EQUAL(hasFailed, false);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxQueue.size(), 0);
|
||||
|
||||
// receive a nack with NackReason::CONGESTION for segment 2
|
||||
auto nack2 = makeNack(face.sentInterests[2], lp::NackReason::CONGESTION);
|
||||
face.receive(nack2);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// segment 2 is retransmitted
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[2], 1);
|
||||
|
||||
// receive a nack with NackReason::NONE for segment 3
|
||||
auto nack3 = makeNack(face.sentInterests[3], lp::NackReason::NONE);
|
||||
face.receive(nack3);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// Other types of Nack will trigger a failure
|
||||
BOOST_CHECK_EQUAL(hasFailed, true);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(FinalBlockIdNotSetAtBeginning)
|
||||
{
|
||||
nDataSegments = 4;
|
||||
pipeline->m_cwnd = 4;
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 0 without FinalBlockId
|
||||
face.receive(*makeDataWithSegment(0, false));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// interests for segment 0 - 5 have been sent
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 1);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, false);
|
||||
// pending interests: segment 1, 2, 3, 4, 5
|
||||
BOOST_CHECK_EQUAL(face.getNPendingInterests(), 5);
|
||||
|
||||
// receive segment 1 with FinalBlockId
|
||||
face.receive(*makeDataWithSegment(1));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, true);
|
||||
|
||||
// pending interests for segment 1, 4, 5 haven been removed
|
||||
BOOST_CHECK_EQUAL(face.getNPendingInterests(), 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(FailureBeforeFinalBlockIdReceived)
|
||||
{
|
||||
// failed to retrieve segNo while the FinalBlockId has not yet been
|
||||
// set, and later received a FinalBlockId >= segNo, i.e. segNo is
|
||||
// part of the content.
|
||||
|
||||
nDataSegments = 4;
|
||||
pipeline->m_cwnd = 4;
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 0 without FinalBlockId
|
||||
face.receive(*makeDataWithSegment(0, false));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 1 without FinalBlockId
|
||||
face.receive(*makeDataWithSegment(1, false));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// interests for segment 0 - 7 have been sent
|
||||
BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 8);
|
||||
|
||||
// receive nack with NackReason::NONE for segment 3
|
||||
auto nack = makeNack(face.sentInterests[3], lp::NackReason::NONE);
|
||||
face.receive(nack);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// error not triggered
|
||||
// pending interests for segment > 3 haven been removed
|
||||
BOOST_CHECK_EQUAL(hasFailed, false);
|
||||
BOOST_CHECK_EQUAL(face.getNPendingInterests(), 1);
|
||||
|
||||
// receive segment 2 with FinalBlockId
|
||||
face.receive(*makeDataWithSegment(2));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// error triggered since segment 3 is part of the content
|
||||
BOOST_CHECK_EQUAL(hasFailed, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SpuriousFailureBeforeFinalBlockIdReceived)
|
||||
{
|
||||
// failed to retrieve segNo while the FinalBlockId has not yet been
|
||||
// set, and later received a FinalBlockId < segNo, i.e. segNo is
|
||||
// not part of the content, and it was actually a spurious failure
|
||||
|
||||
nDataSegments = 4;
|
||||
pipeline->m_cwnd = 4;
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 0 without FinalBlockId
|
||||
face.receive(*makeDataWithSegment(0, false));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 1 without FinalBlockId
|
||||
face.receive(*makeDataWithSegment(1, false));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// interests for segment 0 - 7 have been sent
|
||||
BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 8);
|
||||
|
||||
// receive nack with NackReason::NONE for segment 4
|
||||
auto nack = makeNack(face.sentInterests[4], lp::NackReason::NONE);
|
||||
face.receive(nack);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// error not triggered
|
||||
// pending interests for segment > 3 have been removed
|
||||
BOOST_CHECK_EQUAL(hasFailed, false);
|
||||
BOOST_CHECK_EQUAL(face.getNPendingInterests(), 2);
|
||||
|
||||
// receive segment 2 with FinalBlockId
|
||||
face.receive(*makeDataWithSegment(2));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// timeout segment 3
|
||||
advanceClocks(io, time::seconds(1));
|
||||
|
||||
// segment 3 is retransmitted
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount[3], 1);
|
||||
|
||||
// receive segment 3
|
||||
face.receive(*makeDataWithSegment(3));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(hasFailed, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SegmentInfoMaintenance)
|
||||
{
|
||||
// test that m_segmentInfo is properly maintained when
|
||||
// a segment is received after two consecutive timeouts
|
||||
|
||||
nDataSegments = 3;
|
||||
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 0
|
||||
face.receive(*makeDataWithSegment(0));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// receive segment 1
|
||||
face.receive(*makeDataWithSegment(1));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
|
||||
|
||||
// check if segment 2's state is FirstTimeSent
|
||||
auto it = pipeline->m_segmentInfo.find(2);
|
||||
BOOST_REQUIRE(it != pipeline->m_segmentInfo.end());
|
||||
BOOST_CHECK(it->second.state == SegmentState::FirstTimeSent);
|
||||
|
||||
// timeout segment 2 twice
|
||||
advanceClocks(io, time::milliseconds(400), 3);
|
||||
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 5);
|
||||
|
||||
// check if segment 2's state is Retransmitted
|
||||
it = pipeline->m_segmentInfo.find(2);
|
||||
BOOST_REQUIRE(it != pipeline->m_segmentInfo.end());
|
||||
BOOST_CHECK(it->second.state == SegmentState::Retransmitted);
|
||||
|
||||
// check if segment 2 was retransmitted twice
|
||||
BOOST_CHECK_EQUAL(pipeline->m_retxCount.at(2), 2);
|
||||
|
||||
// receive segment 2 the first time
|
||||
face.receive(*makeDataWithSegment(2));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// check if segment 2 was erased from m_segmentInfo
|
||||
it = pipeline->m_segmentInfo.find(2);
|
||||
BOOST_CHECK(it == pipeline->m_segmentInfo.end());
|
||||
|
||||
auto prevRtt = rttEstimator.getAvgRtt();
|
||||
auto prevRto = rttEstimator.getEstimatedRto();
|
||||
|
||||
// receive segment 2 the second time
|
||||
face.receive(*makeDataWithSegment(2));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
// nothing changed
|
||||
it = pipeline->m_segmentInfo.find(2);
|
||||
BOOST_CHECK(it == pipeline->m_segmentInfo.end());
|
||||
BOOST_CHECK_EQUAL(face.sentInterests.size(), 5);
|
||||
BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), prevRtt);
|
||||
BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), prevRto);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(PrintSummaryWithNoRttMeasurements)
|
||||
{
|
||||
// test the console ouptut when no RTT measurement is available,
|
||||
// to make sure a proper message will be printed out
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
// change the underlying buffer and save the old buffer
|
||||
auto oldBuf = std::cerr.rdbuf(ss.rdbuf());
|
||||
|
||||
pipeline->printSummary();
|
||||
std::string line;
|
||||
|
||||
bool found = false;
|
||||
while (std::getline(ss, line)) {
|
||||
if (line == "RTT stats unavailable") {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BOOST_CHECK(found);
|
||||
std::cerr.rdbuf(oldBuf); // reset
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(StopsWhenFileSizeLessThanChunkSize)
|
||||
{
|
||||
// test to see if the program doesn't hang,
|
||||
// when transfer is complete, for files less than the chunk size
|
||||
// (i.e. when only one segment is sent/received)
|
||||
|
||||
createPipeline();
|
||||
nDataSegments = 1;
|
||||
|
||||
run(name);
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
face.receive(*makeDataWithSegment(0));
|
||||
advanceClocks(io, time::nanoseconds(1));
|
||||
|
||||
BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, true);
|
||||
BOOST_CHECK_EQUAL(pipeline->m_segmentInfo.size(), 0);
|
||||
BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterestsCubic
|
||||
BOOST_AUTO_TEST_SUITE_END() // Chunks
|
||||
|
||||
} // namespace tests
|
||||
} // namespace chunks
|
||||
} // namespace ndn
|
||||
+11
-9
@@ -13,11 +13,11 @@
|
||||
|
||||
## Version discovery methods in ndncatchunks
|
||||
|
||||
* `fixed` : sends an interest attempting to find a data packet with the
|
||||
* `fixed` : sends an Interest attempting to find a data packet with the
|
||||
specified prefix and version number. A version component must be present at the
|
||||
end of the user-specified NDN name.
|
||||
|
||||
* `realtime` : sends discovery interests to fetch metadata of the solicited content from which
|
||||
* `realtime` : sends discovery Interests to fetch metadata of the solicited content from which
|
||||
the data version will be resolved.
|
||||
The version number of the solicited content is included in the name of returned
|
||||
metadata data packet.
|
||||
@@ -32,14 +32,16 @@ The default discovery method is `realtime`.
|
||||
* `fixed`: maintains a fixed-size window of Interests in flight; the window size is configurable
|
||||
via a command line option and defaults to 1.
|
||||
|
||||
* `aimd` : sends Interests using an additive-increase/multiplicative-decrease (AIMD) algorithm to
|
||||
control the window size. By default, a Conservative Loss Adaptation algorithm is adopted
|
||||
combining with the AIMD algorithm, that is, at most one window decrease will be
|
||||
performed per round-trip-time. For details please refer to:
|
||||
[A Practical Congestion Control Scheme for Named Data
|
||||
Networking](https://www.researchgate.net/publication/306259672_A_Practical_Congestion_Control_Scheme_for_Named_Data_Networking)
|
||||
* `aimd` : adjusts the window size via additive-increase/multiplicative-decrease (AIMD).
|
||||
By default, it uses a Conservative Window Adaptation, that is, the congestion window
|
||||
will be decreased at most once per round-trip-time.
|
||||
|
||||
The default Interest pipeline type is `aimd`.
|
||||
* `cubic`: adjusts the window size similar to the TCP CUBIC algorithm.
|
||||
For details about both aimd and cubic please refer to:
|
||||
[A Practical Congestion Control Scheme for Named Data
|
||||
Networking](https://conferences2.sigcomm.org/acm-icn/2016/proceedings/p21-schneider.pdf)
|
||||
|
||||
The default Interest pipeline type is `cubic`.
|
||||
|
||||
## Usage examples
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
#include "discover-version-fixed.hpp"
|
||||
#include "discover-version-realtime.hpp"
|
||||
#include "options.hpp"
|
||||
#include "pipeline-interests-adaptive.hpp"
|
||||
#include "pipeline-interests-aimd.hpp"
|
||||
#include "pipeline-interests-cubic.hpp"
|
||||
#include "pipeline-interests-fixed.hpp"
|
||||
#include "rtt-estimator.hpp"
|
||||
#include "statistics-collector.hpp"
|
||||
@@ -51,17 +52,18 @@ main(int argc, char* argv[])
|
||||
std::string programName(argv[0]);
|
||||
Options options;
|
||||
std::string discoverType("realtime");
|
||||
std::string pipelineType("aimd");
|
||||
std::string pipelineType("cubic");
|
||||
size_t maxPipelineSize(1);
|
||||
int64_t discoveryTimeoutMs(DEFAULT_INTEREST_LIFETIME.count());
|
||||
std::string uri;
|
||||
|
||||
// congestion control parameters, CWA refers to conservative window adaptation,
|
||||
// i.e. only reduce window size at most once per RTT
|
||||
bool disableCwa(false), resetCwndToInit(false), ignoreCongMarks(false);
|
||||
double aiStep(1.0), mdCoef(0.5), alpha(0.125), beta(0.25),
|
||||
minRto(200.0), maxRto(4000.0);
|
||||
int initCwnd(1), initSsthresh(std::numeric_limits<int>::max()), k(4);
|
||||
bool disableCwa(false), resetCwndToInit(false),
|
||||
ignoreCongMarks(false), enableFastConv(false);
|
||||
double aiStep(1.0), rtoAlpha(0.125), rtoBeta(0.25), minRto(200.0), maxRto(4000.0),
|
||||
aimdBeta(0.5), cubicBeta(0.7);
|
||||
int initCwnd(1), initSsthresh(std::numeric_limits<int>::max()), k(8);
|
||||
std::string cwndPath, rttPath;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
@@ -71,7 +73,7 @@ main(int argc, char* argv[])
|
||||
("discover-version,d", po::value<std::string>(&discoverType)->default_value(discoverType),
|
||||
"version discovery algorithm to use; valid values are: 'fixed', 'realtime'")
|
||||
("pipeline-type,p", po::value<std::string>(&pipelineType)->default_value(pipelineType),
|
||||
"type of Interest pipeline to use; valid values are: 'fixed', 'aimd'")
|
||||
"type of Interest pipeline to use; valid values are: 'fixed', 'aimd', 'cubic'")
|
||||
("fresh,f", po::bool_switch(&options.mustBeFresh), "only return fresh content")
|
||||
("lifetime,l", po::value<int64_t>()->default_value(options.interestLifetime.count()),
|
||||
"lifetime of expressed Interests, in milliseconds")
|
||||
@@ -93,30 +95,27 @@ main(int argc, char* argv[])
|
||||
"size of the Interest pipeline")
|
||||
;
|
||||
|
||||
po::options_description adaptivePipeDesc("Adaptive pipeline options (AIMD)");
|
||||
po::options_description adaptivePipeDesc("Adaptive pipeline options (AIMD & CUBIC)");
|
||||
adaptivePipeDesc.add_options()
|
||||
("log-cwnd", po::value<std::string>(&cwndPath), "log file for cwnd statistics")
|
||||
("log-rtt", po::value<std::string>(&rttPath), "log file for rtt statistics")
|
||||
("disable-cwa", po::bool_switch(&disableCwa),
|
||||
"disable Conservative Window Adaptation, "
|
||||
"i.e. reduce window on each congestion event (timeout or congestion mark) "
|
||||
"instead of at most once per RTT")
|
||||
("ignore-marks", po::bool_switch(&ignoreCongMarks),
|
||||
"ignore congestion marks, "
|
||||
"the default is to decrease the window after receiving a congestion mark")
|
||||
"do not decrease the window after receiving a congestion mark")
|
||||
("disable-cwa", po::bool_switch(&disableCwa),
|
||||
"disable Conservative Window Adaptation, i.e., reduce the window on "
|
||||
"each timeout or congestion mark instead of at most once per RTT")
|
||||
("reset-cwnd-to-init", po::bool_switch(&resetCwndToInit),
|
||||
"reset cwnd to initial value after loss/mark, default is "
|
||||
"resetting to ssthresh")
|
||||
("init-cwnd", po::value<int>(&initCwnd)->default_value(initCwnd), "initial cwnd")
|
||||
("init-ssthresh", po::value<int>(&initSsthresh),
|
||||
"initial slow start threshold (defaults to infinity)")
|
||||
("aistep", po::value<double>(&aiStep)->default_value(aiStep),
|
||||
"after a timeout or congestion mark, reset the window "
|
||||
"to the initial value instead of resetting to ssthresh")
|
||||
("init-cwnd", po::value<int>(&initCwnd)->default_value(initCwnd),
|
||||
"initial congestion window in segments")
|
||||
("init-ssthresh", po::value<int>(&initSsthresh),
|
||||
"initial slow start threshold in segments (defaults to infinity)")
|
||||
("aimd-step", po::value<double>(&aiStep)->default_value(aiStep),
|
||||
"additive-increase step")
|
||||
("mdcoef", po::value<double>(&mdCoef)->default_value(mdCoef),
|
||||
"multiplicative-decrease coefficient")
|
||||
("rto-alpha", po::value<double>(&alpha)->default_value(alpha),
|
||||
("aimd-beta", po::value<double>(&aimdBeta)->default_value(aimdBeta),
|
||||
"multiplicative decrease factor (AIMD)")
|
||||
("rto-alpha", po::value<double>(&rtoAlpha)->default_value(rtoAlpha),
|
||||
"alpha value for rto calculation")
|
||||
("rto-beta", po::value<double>(&beta)->default_value(beta),
|
||||
("rto-beta", po::value<double>(&rtoBeta)->default_value(rtoBeta),
|
||||
"beta value for rto calculation")
|
||||
("rto-k", po::value<int>(&k)->default_value(k),
|
||||
"k value for rto calculation")
|
||||
@@ -124,10 +123,23 @@ main(int argc, char* argv[])
|
||||
"minimum rto value in milliseconds")
|
||||
("max-rto", po::value<double>(&maxRto)->default_value(maxRto),
|
||||
"maximum rto value in milliseconds")
|
||||
("log-cwnd", po::value<std::string>(&cwndPath), "log file for congestion window stats")
|
||||
("log-rtt", po::value<std::string>(&rttPath), "log file for round-trip time stats")
|
||||
;
|
||||
|
||||
po::options_description cubicPipeDesc("CUBIC pipeline options");
|
||||
cubicPipeDesc.add_options()
|
||||
("fast-conv", po::bool_switch(&enableFastConv), "enable cubic fast convergence")
|
||||
("cubic-beta", po::value<double>(&cubicBeta),
|
||||
"window decrease factor for CUBIC (defaults to 0.7)")
|
||||
;
|
||||
|
||||
po::options_description visibleDesc;
|
||||
visibleDesc.add(basicDesc).add(realDiscoveryDesc).add(fixedPipeDesc).add(adaptivePipeDesc);
|
||||
visibleDesc.add(basicDesc)
|
||||
.add(realDiscoveryDesc)
|
||||
.add(fixedPipeDesc)
|
||||
.add(adaptivePipeDesc)
|
||||
.add(cubicPipeDesc);
|
||||
|
||||
po::options_description hiddenDesc;
|
||||
hiddenDesc.add_options()
|
||||
@@ -231,11 +243,11 @@ main(int argc, char* argv[])
|
||||
optionsPipeline.maxPipelineSize = maxPipelineSize;
|
||||
pipeline = make_unique<PipelineInterestsFixed>(face, optionsPipeline);
|
||||
}
|
||||
else if (pipelineType == "aimd") {
|
||||
else if (pipelineType == "aimd" || pipelineType == "cubic") {
|
||||
RttEstimator::Options optionsRttEst;
|
||||
optionsRttEst.isVerbose = options.isVerbose;
|
||||
optionsRttEst.alpha = alpha;
|
||||
optionsRttEst.beta = beta;
|
||||
optionsRttEst.alpha = rtoAlpha;
|
||||
optionsRttEst.beta = rtoBeta;
|
||||
optionsRttEst.k = k;
|
||||
optionsRttEst.minRto = Milliseconds(minRto);
|
||||
optionsRttEst.maxRto = Milliseconds(maxRto);
|
||||
@@ -248,10 +260,19 @@ main(int argc, char* argv[])
|
||||
optionsPipeline.initCwnd = static_cast<double>(initCwnd);
|
||||
optionsPipeline.initSsthresh = static_cast<double>(initSsthresh);
|
||||
optionsPipeline.aiStep = aiStep;
|
||||
optionsPipeline.mdCoef = mdCoef;
|
||||
optionsPipeline.mdCoef = aimdBeta;
|
||||
optionsPipeline.ignoreCongMarks = ignoreCongMarks;
|
||||
|
||||
auto adaptivePipeline = make_unique<PipelineInterestsAdaptive>(face, *rttEstimator, optionsPipeline);
|
||||
unique_ptr<PipelineInterestsAdaptive> adaptivePipeline;
|
||||
if (pipelineType == "aimd") {
|
||||
adaptivePipeline = make_unique<PipelineInterestsAimd>(face, *rttEstimator, optionsPipeline);
|
||||
}
|
||||
else {
|
||||
PipelineInterestsCubic::Options optionsCubic(optionsPipeline);
|
||||
optionsCubic.enableFastConv = enableFastConv;
|
||||
optionsCubic.cubicBeta = cubicBeta;
|
||||
adaptivePipeline = make_unique<PipelineInterestsCubic>(face, *rttEstimator, optionsCubic);
|
||||
}
|
||||
|
||||
if (!cwndPath.empty() || !rttPath.empty()) {
|
||||
if (!cwndPath.empty()) {
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
namespace ndn {
|
||||
namespace chunks {
|
||||
|
||||
@@ -42,6 +41,8 @@ PipelineInterestsAdaptive::PipelineInterestsAdaptive(Face& face, RttEstimator& r
|
||||
const Options& options)
|
||||
: PipelineInterests(face)
|
||||
, m_options(options)
|
||||
, m_cwnd(m_options.initCwnd)
|
||||
, m_ssthresh(m_options.initSsthresh)
|
||||
, m_rttEstimator(rttEstimator)
|
||||
, m_scheduler(m_face.getIoService())
|
||||
, m_highData(0)
|
||||
@@ -55,14 +56,9 @@ PipelineInterestsAdaptive::PipelineInterestsAdaptive(Face& face, RttEstimator& r
|
||||
, m_nRetransmitted(0)
|
||||
, m_nCongMarks(0)
|
||||
, m_nSent(0)
|
||||
, m_cwnd(m_options.initCwnd)
|
||||
, m_ssthresh(m_options.initSsthresh)
|
||||
, m_hasFailure(false)
|
||||
, m_failedSegNo(0)
|
||||
{
|
||||
if (m_options.isVerbose) {
|
||||
std::cerr << m_options;
|
||||
}
|
||||
}
|
||||
|
||||
PipelineInterestsAdaptive::~PipelineInterestsAdaptive()
|
||||
@@ -399,29 +395,6 @@ PipelineInterestsAdaptive::handleFail(uint64_t segNo, const std::string& reason)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsAdaptive::increaseWindow()
|
||||
{
|
||||
if (m_cwnd < m_ssthresh) {
|
||||
m_cwnd += m_options.aiStep; // additive increase
|
||||
}
|
||||
else {
|
||||
m_cwnd += m_options.aiStep / std::floor(m_cwnd); // congestion avoidance
|
||||
}
|
||||
|
||||
afterCwndChange(time::steady_clock::now() - getStartTime(), m_cwnd);
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsAdaptive::decreaseWindow()
|
||||
{
|
||||
// please refer to RFC 5681, Section 3.1 for the rationale behind it
|
||||
m_ssthresh = std::max(MIN_SSTHRESH, m_cwnd * m_options.mdCoef); // multiplicative decrease
|
||||
m_cwnd = m_options.resetCwndToInit ? m_options.initCwnd : m_ssthresh;
|
||||
|
||||
afterCwndChange(time::steady_clock::now() - getStartTime(), m_cwnd);
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsAdaptive::cancelInFlightSegmentsGreaterThan(uint64_t segNo)
|
||||
{
|
||||
@@ -488,9 +461,9 @@ operator<<(std::ostream& os, const PipelineInterestsAdaptiveOptions& options)
|
||||
<< "\tRTO check interval = " << options.rtoCheckInterval << "\n"
|
||||
<< "\tMax retries on timeout or Nack = " << (options.maxRetriesOnTimeoutOrNack == DataFetcher::MAX_RETRIES_INFINITE ?
|
||||
"infinite" : to_string(options.maxRetriesOnTimeoutOrNack)) << "\n"
|
||||
<< "\tReaction to congestion marks " << (options.ignoreCongMarks ? "disabled" : "enabled") << "\n"
|
||||
<< "\tConservative window adaptation " << (options.disableCwa ? "disabled" : "enabled") << "\n"
|
||||
<< "\tResetting cwnd to " << (options.resetCwndToInit ? "initCwnd" : "ssthresh") << " upon loss event\n";
|
||||
<< "\tReact to congestion marks = " << (options.ignoreCongMarks ? "no" : "yes") << "\n"
|
||||
<< "\tConservative window adaptation = " << (options.disableCwa ? "no" : "yes") << "\n"
|
||||
<< "\tResetting window to " << (options.resetCwndToInit ? "initCwnd" : "ssthresh") << " upon loss event\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ public:
|
||||
bool ignoreCongMarks = false; ///< disable window decrease after congestion marks
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const PipelineInterestsAdaptiveOptions& options);
|
||||
|
||||
/**
|
||||
* @brief indicates the state of the segment
|
||||
*/
|
||||
@@ -96,11 +99,11 @@ struct SegmentInfo
|
||||
class PipelineInterestsAdaptive : public PipelineInterests
|
||||
{
|
||||
public:
|
||||
typedef PipelineInterestsAdaptiveOptions Options;
|
||||
using Options = PipelineInterestsAdaptiveOptions;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief create a PipelineInterestsAdaptive service
|
||||
* @brief Constructor.
|
||||
*
|
||||
* Configures the pipelining service without specifying the retrieval namespace. After this
|
||||
* configuration the method run must be called to start the Pipeline.
|
||||
@@ -108,19 +111,35 @@ public:
|
||||
PipelineInterestsAdaptive(Face& face, RttEstimator& rttEstimator,
|
||||
const Options& options = Options());
|
||||
|
||||
~PipelineInterestsAdaptive() final;
|
||||
~PipelineInterestsAdaptive() override;
|
||||
|
||||
/**
|
||||
* @brief Signals when cwnd changes
|
||||
* @brief Signals when the congestion window changes.
|
||||
*
|
||||
* The callback function should be: void(Milliseconds age, double cwnd) where age is the
|
||||
* duration since pipeline starts, and cwnd is the new congestion window size (in segments).
|
||||
*/
|
||||
signal::Signal<PipelineInterestsAdaptive, Milliseconds, double> afterCwndChange;
|
||||
|
||||
protected:
|
||||
DECLARE_SIGNAL_EMIT(afterCwndChange)
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief fetch all the segments between 0 and lastSegment of the specified prefix
|
||||
* @brief Increase congestion window.
|
||||
*/
|
||||
virtual void
|
||||
increaseWindow() = 0;
|
||||
|
||||
/**
|
||||
* @brief Decrease congestion window.
|
||||
*/
|
||||
virtual void
|
||||
decreaseWindow() = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Fetch all the segments between 0 and lastSegment of the specified prefix.
|
||||
*
|
||||
* Starts the pipeline with an adaptive window algorithm to control the window size.
|
||||
* The pipeline will fetch every segment until the last segment is successfully received
|
||||
@@ -130,13 +149,13 @@ private:
|
||||
doRun() final;
|
||||
|
||||
/**
|
||||
* @brief stop all fetch operations
|
||||
* @brief Stop all fetch operations.
|
||||
*/
|
||||
void
|
||||
doCancel() final;
|
||||
|
||||
/**
|
||||
* @brief check RTO for all sent-but-not-acked segments.
|
||||
* @brief Check RTO for all sent-but-not-acked segments.
|
||||
*/
|
||||
void
|
||||
checkRto();
|
||||
@@ -169,18 +188,6 @@ private:
|
||||
void
|
||||
handleFail(uint64_t segNo, const std::string& reason);
|
||||
|
||||
/**
|
||||
* @brief increase congestion window size
|
||||
*/
|
||||
void
|
||||
increaseWindow();
|
||||
|
||||
/**
|
||||
* @brief decrease congestion window size
|
||||
*/
|
||||
void
|
||||
decreaseWindow();
|
||||
|
||||
void
|
||||
cancelInFlightSegmentsGreaterThan(uint64_t segNo);
|
||||
|
||||
@@ -188,9 +195,14 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
void
|
||||
printSummary() const final;
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
PUBLIC_WITH_TESTS_ELSE_PROTECTED:
|
||||
static constexpr double MIN_SSTHRESH = 2.0;
|
||||
const Options m_options;
|
||||
|
||||
double m_cwnd; ///< current congestion window size (in segments)
|
||||
double m_ssthresh; ///< current slow start threshold
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
RttEstimator& m_rttEstimator;
|
||||
Scheduler m_scheduler;
|
||||
scheduler::ScopedEventId m_checkRtoEvent;
|
||||
@@ -210,9 +222,6 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
int64_t m_nCongMarks; ///< # of data packets with congestion mark
|
||||
int64_t m_nSent; ///< # of interest packets sent out (including retransmissions)
|
||||
|
||||
double m_cwnd; ///< current congestion window size (in segments)
|
||||
double m_ssthresh; ///< current slow start threshold
|
||||
|
||||
std::unordered_map<uint64_t, SegmentInfo> m_segmentInfo; ///< keeps all the internal information
|
||||
///< on sent but not acked segments
|
||||
std::unordered_map<uint64_t, int> m_retxCount; ///< maps segment number to its retransmission count;
|
||||
@@ -225,10 +234,6 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
std::string m_failureReason;
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const PipelineInterestsAdaptiveOptions& options);
|
||||
|
||||
|
||||
} // namespace chunks
|
||||
} // namespace ndn
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, Regents of the University of California,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University.
|
||||
*
|
||||
* This file is part of ndn-tools (Named Data Networking Essential Tools).
|
||||
* See AUTHORS.md for complete list of ndn-tools authors and contributors.
|
||||
*
|
||||
* ndn-tools is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
|
||||
*
|
||||
* @author Shuo Yang
|
||||
* @author Weiwei Liu
|
||||
* @author Chavoosh Ghasemi
|
||||
* @author Klaus Schneider
|
||||
*/
|
||||
|
||||
#include "pipeline-interests-aimd.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace ndn {
|
||||
namespace chunks {
|
||||
|
||||
PipelineInterestsAimd::PipelineInterestsAimd(Face& face, RttEstimator& rttEstimator,
|
||||
const Options& options)
|
||||
: PipelineInterestsAdaptive(face, rttEstimator, options)
|
||||
{
|
||||
if (options.isVerbose) {
|
||||
std::cerr << options;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsAimd::increaseWindow()
|
||||
{
|
||||
if (m_cwnd < m_ssthresh) {
|
||||
m_cwnd += m_options.aiStep; // additive increase
|
||||
}
|
||||
else {
|
||||
m_cwnd += m_options.aiStep / std::floor(m_cwnd); // congestion avoidance
|
||||
}
|
||||
|
||||
emitSignal(afterCwndChange, time::steady_clock::now() - getStartTime(), m_cwnd);
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsAimd::decreaseWindow()
|
||||
{
|
||||
// please refer to RFC 5681, Section 3.1 for the rationale behind it
|
||||
m_ssthresh = std::max(MIN_SSTHRESH, m_cwnd * m_options.mdCoef); // multiplicative decrease
|
||||
m_cwnd = m_options.resetCwndToInit ? m_options.initCwnd : m_ssthresh;
|
||||
|
||||
emitSignal(afterCwndChange, time::steady_clock::now() - getStartTime(), m_cwnd);
|
||||
}
|
||||
|
||||
} // namespace chunks
|
||||
} // namespace ndn
|
||||
@@ -0,0 +1,57 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, Regents of the University of California,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University.
|
||||
*
|
||||
* This file is part of ndn-tools (Named Data Networking Essential Tools).
|
||||
* See AUTHORS.md for complete list of ndn-tools authors and contributors.
|
||||
*
|
||||
* ndn-tools is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
|
||||
*
|
||||
* @author Shuo Yang
|
||||
* @author Weiwei Liu
|
||||
* @author Chavoosh Ghasemi
|
||||
* @author Klaus Schneider
|
||||
*/
|
||||
|
||||
#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_AIMD_HPP
|
||||
#define NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_AIMD_HPP
|
||||
|
||||
#include "pipeline-interests-adaptive.hpp"
|
||||
|
||||
namespace ndn {
|
||||
namespace chunks {
|
||||
|
||||
/**
|
||||
* @brief Implements AIMD window increase and decrease.
|
||||
*/
|
||||
class PipelineInterestsAimd : public PipelineInterestsAdaptive
|
||||
{
|
||||
public:
|
||||
PipelineInterestsAimd(Face& face, RttEstimator& rttEstimator,
|
||||
const Options& options = Options());
|
||||
|
||||
private:
|
||||
void
|
||||
increaseWindow() final;
|
||||
|
||||
void
|
||||
decreaseWindow() final;
|
||||
};
|
||||
|
||||
} // namespace chunks
|
||||
} // namespace ndn
|
||||
|
||||
#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_AIMD_HPP
|
||||
@@ -0,0 +1,124 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, Regents of the University of California,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University.
|
||||
*
|
||||
* This file is part of ndn-tools (Named Data Networking Essential Tools).
|
||||
* See AUTHORS.md for complete list of ndn-tools authors and contributors.
|
||||
*
|
||||
* ndn-tools is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
|
||||
*
|
||||
* @author Klaus Schneider
|
||||
*/
|
||||
|
||||
#include "pipeline-interests-cubic.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace ndn {
|
||||
namespace chunks {
|
||||
|
||||
constexpr double CUBIC_C = 0.4;
|
||||
|
||||
PipelineInterestsCubic::PipelineInterestsCubic(Face& face, RttEstimator& rttEstimator,
|
||||
const Options& options)
|
||||
: PipelineInterestsAdaptive(face, rttEstimator, options)
|
||||
, m_cubicOptions(options)
|
||||
, m_lastDecrease(time::steady_clock::now())
|
||||
{
|
||||
if (options.isVerbose) {
|
||||
std::cerr << options;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsCubic::increaseWindow()
|
||||
{
|
||||
// Slow start phase
|
||||
if (m_cwnd < m_ssthresh) {
|
||||
m_cwnd += 1.0;
|
||||
}
|
||||
// Congestion avoidance phase
|
||||
else {
|
||||
// If wmax is still 0, set it to the current cwnd. Usually unnecessary,
|
||||
// if m_ssthresh is large enough.
|
||||
if (m_wmax < m_options.initCwnd) {
|
||||
m_wmax = m_cwnd;
|
||||
}
|
||||
|
||||
// 1. Time since last congestion event in seconds
|
||||
const double t = (time::steady_clock::now() - m_lastDecrease).count() / 1e9;
|
||||
|
||||
// 2. Time it takes to increase the window to m_wmax = the cwnd right before the last
|
||||
// window decrease.
|
||||
// K = cubic_root(wmax*(1-beta_cubic)/C) (Eq. 2)
|
||||
const double k = std::cbrt(m_wmax * (1 - m_cubicOptions.cubicBeta) / CUBIC_C);
|
||||
|
||||
// 3. Target: W_cubic(t) = C*(t-K)^3 + wmax (Eq. 1)
|
||||
const double wCubic = CUBIC_C * std::pow(t - k, 3) + m_wmax;
|
||||
|
||||
// 4. Estimate of Reno Increase (Currently Disabled)
|
||||
// const double rtt = m_rtt->GetCurrentEstimate().GetSeconds();
|
||||
// const double w_est = wmax*m_beta + (3*(1-m_beta)/(1+m_beta)) * (t/rtt);
|
||||
const double wEst = 0.0;
|
||||
|
||||
// Actual adaptation
|
||||
double cubicIncrement = std::max(wCubic, wEst) - m_cwnd;
|
||||
// Cubic increment must be positive
|
||||
// Note: This change is not part of the RFC, but I added it to improve performance.
|
||||
cubicIncrement = std::max(0.0, cubicIncrement);
|
||||
|
||||
m_cwnd += cubicIncrement / m_cwnd;
|
||||
}
|
||||
|
||||
emitSignal(afterCwndChange, time::steady_clock::now() - getStartTime(), m_cwnd);
|
||||
}
|
||||
|
||||
void
|
||||
PipelineInterestsCubic::decreaseWindow()
|
||||
{
|
||||
// A flow remembers the last value of wmax,
|
||||
// before it updates wmax for the current congestion event.
|
||||
|
||||
// Current wmax < last_wmax
|
||||
if (m_cubicOptions.enableFastConv && m_cwnd < m_lastWmax) {
|
||||
m_lastWmax = m_cwnd;
|
||||
m_wmax = m_cwnd * (1.0 + m_cubicOptions.cubicBeta) / 2.0;
|
||||
}
|
||||
else {
|
||||
// Save old cwnd as wmax
|
||||
m_lastWmax = m_cwnd;
|
||||
m_wmax = m_cwnd;
|
||||
}
|
||||
|
||||
m_ssthresh = std::max(m_options.initCwnd, m_cwnd * m_cubicOptions.cubicBeta);
|
||||
m_cwnd = m_ssthresh;
|
||||
m_lastDecrease = time::steady_clock::now();
|
||||
|
||||
emitSignal(afterCwndChange, time::steady_clock::now() - getStartTime(), m_cwnd);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const PipelineInterestsCubicOptions& options)
|
||||
{
|
||||
os << static_cast<const PipelineInterestsAdaptiveOptions&>(options)
|
||||
<< "Cubic pipeline parameters:\n"
|
||||
<< "\tFast convergence = " << (options.enableFastConv ? "yes" : "no") << "\n"
|
||||
<< "\tCubic beta = " << options.cubicBeta << "\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace chunks
|
||||
} // namespace ndn
|
||||
@@ -0,0 +1,85 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, Regents of the University of California,
|
||||
* Colorado State University,
|
||||
* University Pierre & Marie Curie, Sorbonne University.
|
||||
*
|
||||
* This file is part of ndn-tools (Named Data Networking Essential Tools).
|
||||
* See AUTHORS.md for complete list of ndn-tools authors and contributors.
|
||||
*
|
||||
* ndn-tools is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
|
||||
*
|
||||
* @author Klaus Schneider
|
||||
*/
|
||||
|
||||
#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_CUBIC_HPP
|
||||
#define NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_CUBIC_HPP
|
||||
|
||||
#include "pipeline-interests-adaptive.hpp"
|
||||
|
||||
namespace ndn {
|
||||
namespace chunks {
|
||||
|
||||
class PipelineInterestsCubicOptions : public PipelineInterestsAdaptiveOptions
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
PipelineInterestsCubicOptions(const PipelineInterestsAdaptiveOptions& options =
|
||||
PipelineInterestsAdaptiveOptions())
|
||||
: PipelineInterestsAdaptiveOptions(options)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
bool enableFastConv = false; ///< use cubic fast convergence
|
||||
double cubicBeta = 0.7; ///< multiplicative decrease factor (from Linux kernel: 717/1024)
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const PipelineInterestsCubicOptions& options);
|
||||
|
||||
/**
|
||||
* @brief Implements Cubic window increase and decrease.
|
||||
*
|
||||
* This implementation follows the RFC8312 https://tools.ietf.org/html/rfc8312
|
||||
* and the Linux kernel implementation https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_cubic.c
|
||||
*/
|
||||
class PipelineInterestsCubic : public PipelineInterestsAdaptive
|
||||
{
|
||||
public:
|
||||
using Options = PipelineInterestsCubicOptions;
|
||||
|
||||
public:
|
||||
PipelineInterestsCubic(Face& face, RttEstimator& rttEstimator,
|
||||
const Options& options = Options());
|
||||
|
||||
private:
|
||||
void
|
||||
increaseWindow() final;
|
||||
|
||||
void
|
||||
decreaseWindow() final;
|
||||
|
||||
private:
|
||||
const Options m_cubicOptions;
|
||||
|
||||
double m_wmax = 0.0; ///< window size before last window decrease
|
||||
double m_lastWmax = 0.0; ///< last wmax
|
||||
time::steady_clock::TimePoint m_lastDecrease; ///< time of last window decrease
|
||||
};
|
||||
|
||||
} // namespace chunks
|
||||
} // namespace ndn
|
||||
|
||||
#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_CUBIC_HPP
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
: isVerbose(false)
|
||||
, alpha(0.125)
|
||||
, beta(0.25)
|
||||
, k(4)
|
||||
, k(8)
|
||||
, initialRto(1000.0)
|
||||
, minRto(200.0)
|
||||
, maxRto(20000.0)
|
||||
|
||||
Reference in new issue
Block a user