quic/congestion_control/Copa.h (78 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <folly/Optional.h>
#include <quic/QuicException.h>
#include <quic/congestion_control/CongestionController.h>
#include <quic/congestion_control/third_party/windowed_filter.h>
#include <quic/state/AckEvent.h>
#include <quic/state/StateData.h>
#include <limits>
namespace quic {
using namespace std::chrono_literals;
constexpr std::chrono::microseconds kMinRTTWindowLength{10s};
/**
* Algorithm description https://fb.quip.com/kgubABy1yuYR
* Original paper
* https://www.usenix.org/system/files/conference/nsdi18/nsdi18-arun.pdf
*/
class Copa : public CongestionController {
public:
explicit Copa(QuicConnectionStateBase& conn);
void onRemoveBytesFromInflight(uint64_t) override;
void onPacketSent(const OutstandingPacket& packet) override;
void onPacketAckOrLoss(
const AckEvent* FOLLY_NULLABLE,
const LossEvent* FOLLY_NULLABLE) override;
void onPacketAckOrLoss(
folly::Optional<AckEvent> ack,
folly::Optional<LossEvent> loss) {
onPacketAckOrLoss(ack.get_pointer(), loss.get_pointer());
}
uint64_t getWritableBytes() const noexcept override;
uint64_t getCongestionWindow() const noexcept override;
CongestionControlType type() const noexcept override;
bool inSlowStart();
uint64_t getBytesInFlight() const noexcept;
void setAppIdle(bool, TimePoint) noexcept override;
void setAppLimited() override;
bool isAppLimited() const noexcept override;
void setBandwidthUtilizationFactor(
float /*bandwidthUtilizationFactor*/) noexcept override {}
bool isInBackgroundMode() const noexcept override {
return false;
}
void getStats(CongestionControllerStats& stats) const override;
private:
void onPacketAcked(const AckEvent&);
void onPacketLoss(const LossEvent&);
struct VelocityState {
uint64_t velocity{1};
enum Direction {
None,
Up, // cwnd is increasing
Down, // cwnd is decreasing
};
Direction direction{None};
// number of rtts direction has remained same
uint64_t numTimesDirectionSame{0};
// updated every srtt
uint64_t lastRecordedCwndBytes;
folly::Optional<TimePoint> lastCwndRecordTime{folly::none};
};
void checkAndUpdateDirection(const TimePoint ackTime);
void changeDirection(
VelocityState::Direction newDirection,
const TimePoint ackTime);
QuicConnectionStateBase& conn_;
uint64_t cwndBytes_;
bool isSlowStart_;
// time at which cwnd was last doubled during slow start
folly::Optional<TimePoint> lastCwndDoubleTime_{folly::none};
WindowedFilter<
std::chrono::microseconds,
MinFilter<std::chrono::microseconds>,
uint64_t,
uint64_t>
minRTTFilter_; // To get min RTT over 10 seconds
WindowedFilter<
std::chrono::microseconds,
MinFilter<std::chrono::microseconds>,
uint64_t,
uint64_t>
standingRTTFilter_; // To get min RTT over srtt/2
VelocityState velocityState_;
/**
* deltaParam determines how latency sensitive the algorithm is. Lower
* means it will maximime throughput at expense of delay. Higher value means
* it will minimize delay at expense of throughput.
*/
double deltaParam_{0.05};
// Whether we should use Copa's RTTstanding mechanism
bool useRttStanding_{false};
};
} // namespace quic