diff --git a/README.md b/README.md index e086904..7fc520b 100755 --- a/README.md +++ b/README.md @@ -52,13 +52,7 @@ source /etc/profile ``` # Run example 1 put the file quic-main.cc(scratch) to ns3.33/scratch/ -2 set string varible quic_cert_path in quic-main.cc -3 set string varible log_path in quic-main.cc to collect traced data. -``` -std::string quic_cert_path("/home/xxx/quiche/utils/data/quic-cert/"); -std::string log_path=std::string("/home/xxx/traces/")+algo+"/"; -``` -3 Run example(BBR) +2 Run example(BBR) ``` source /etc/profile ./waf --run "scratch/quic-main --cc=bbr" @@ -73,8 +67,8 @@ data can be found under folder ("/home/xxx/traces/") copy the file plot-script/data_plot.sh to "/home/xxx/traces/bbr" plot the results: ``` -chmod 777 data_plot.sh -./data_plot.sh +chmod 777 data-plot.sh +./data-plot.sh ``` ## bbr inflight packets: @@ -95,3 +89,8 @@ one way delay: ![avatar](https://github.com/SoonyangZhang/quic-on-ns3/blob/main/results/1-copa-owd.png) goodput: ![avatar](https://github.com/SoonyangZhang/quic-on-ns3/blob/main/results/1-copa-goodput.png) +## vegas +one way delay: +![avatar](https://github.com/SoonyangZhang/quic-on-ns3/blob/main/results/1-vegas-owd.png) +goodput: +![avatar](https://github.com/SoonyangZhang/quic-on-ns3/blob/main/results/1-vegas-goodput.png) diff --git a/quic/model/cc-algo/vegas_sender.cc b/quic/model/cc-algo/vegas_sender.cc new file mode 100644 index 0000000..be1a3ab --- /dev/null +++ b/quic/model/cc-algo/vegas_sender.cc @@ -0,0 +1,234 @@ +#include "vegas_sender.h" +#include "../model/ns3-quic-congestion-factory.h" +#include +#include +#include +#include + +#include "gquiche/quic/core/congestion_control/rtt_stats.h" +#include "gquiche/quic/core/quic_time.h" +#include "gquiche/quic/core/quic_time_accumulator.h" +#include "gquiche/quic/platform/api/quic_bug_tracker.h" +#include "gquiche/quic/platform/api/quic_flag_utils.h" +#include "gquiche/quic/platform/api/quic_flags.h" +#include "gquiche/quic/platform/api/quic_logging.h" + +namespace quic{ +namespace{ + const QuicByteCount kDefaultMinimumCongestionWindow = 4 *kDefaultTCPMSS; + const QuicByteCount kAlphaCongestionWindow=2*kDefaultTCPMSS; + const QuicByteCount kBetaCongestionWindow=4*kDefaultTCPMSS; + const QuicByteCount kGammaCongestionWindow=1*kDefaultTCPMSS; + const QuicTime::Delta kMinRTTWindowLength = QuicTime::Delta::FromSeconds(10); +} + +VegasSender::VegasSender(QuicTime now, + const RttStats* rtt_stats, + const QuicUnackedPacketMap* unacked_packets, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_tcp_congestion_window, + QuicRandom* random, + QuicConnectionStats* stats): +rtt_stats_(rtt_stats), +unacked_packets_(unacked_packets), +random_(random), +stats_(stats), +congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS), +initial_congestion_window_(initial_tcp_congestion_window *kDefaultTCPMSS), +max_congestion_window_(max_tcp_congestion_window * kDefaultTCPMSS), +min_congestion_window_(kDefaultMinimumCongestionWindow), +slowstart_threshold_(max_tcp_congestion_window * kDefaultTCPMSS), +pacing_rate_(QuicBandwidth::Zero()), +baseRTTFilter_(kMinRTTWindowLength.ToMicroseconds(),QuicTime::Delta::Zero(),0), +min_rtt_(QuicTime::Delta::Infinite()), +rtt_count_(0), +num_acked_packets_(0), +vegas_mode_(true){ + if (stats_) { + // Clear some startup stats if |stats_| has been used by another sender, + // which happens e.g. when QuicConnection switch send algorithms. + stats_->slowstart_count = 0; + stats_->slowstart_duration = QuicTimeAccumulator(); + } +} +VegasSender::~VegasSender(){} + +bool VegasSender::InSlowStart() const{ + return GetCongestionWindow() < GetSlowStartThreshold(); +} +bool VegasSender::InRecovery() const{ + return largest_acked_packet_number_.IsInitialized() && + largest_sent_at_last_cutback_.IsInitialized() && + largest_acked_packet_number_ <= largest_sent_at_last_cutback_; +} +bool VegasSender::ShouldSendProbingPacket() const{ + return false; +} +void VegasSender::SetInitialCongestionWindowInPackets(QuicPacketCount congestion_window){ + if(InSlowStart()){ + initial_congestion_window_ = congestion_window * kDefaultTCPMSS; + congestion_window_ = congestion_window * kDefaultTCPMSS; + } +} + +void VegasSender::OnCongestionEvent(bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets){ + if(acked_packets.size()>0){ + auto vrtt=rtt_stats_->latest_rtt()+QuicTime::Delta::FromMicroseconds(1); + QuicTime::Delta wall_time=event_time-QuicTime::Zero(); + baseRTTFilter_.Update(vrtt,wall_time.ToMicroseconds()); + if(min_rtt_>vrtt){ + min_rtt_=vrtt; + } + rtt_count_++; + } + bool before=InRecovery(); + + for (const LostPacket& lost_packet : lost_packets) { + OnPacketLost(lost_packet.packet_number, lost_packet.bytes_lost, + prior_in_flight); + } + for (const AckedPacket& acked_packet : acked_packets) { + OnPacketAcked(acked_packet.packet_number, acked_packet.bytes_acked, + prior_in_flight, event_time); + } + if(InRecovery()!=before){ + if(InRecovery()){ + //vegas_disable + vegas_mode_=false; + }else{ + //vegas_enable + vegas_mode_=true; + beg_send_next_=largest_sent_packet_number_; + min_rtt_=QuicTime::Delta::Infinite(); + rtt_count_=0; + } + } +} +void VegasSender::OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable){ + QUICHE_DCHECK(!largest_sent_packet_number_.IsInitialized() || + largest_sent_packet_number_ < packet_number); + largest_sent_packet_number_ = packet_number; + if(!beg_send_next_.IsInitialized()){ + beg_send_next_=largest_sent_packet_number_; + } +} +void VegasSender::OnRetransmissionTimeout(bool /*packets_retransmitted*/){ + +} +bool VegasSender::CanSend(QuicByteCount bytes_in_flight){ + return bytes_in_flightSmoothedOrInitialRtt(); + const QuicBandwidth bandwidth = + QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); + return bandwidth * (InSlowStart() ? 2 : (InRecovery() ? 1 : 1.25)); +} +QuicBandwidth VegasSender::BandwidthEstimate() const { + QuicTime::Delta srtt = rtt_stats_->smoothed_rtt(); + if (srtt.IsZero()) { + // If we haven't measured an rtt, the bandwidth estimate is unknown. + return QuicBandwidth::Zero(); + } + return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); +} +QuicByteCount VegasSender::GetCongestionWindow() const { + return congestion_window_; +} +QuicByteCount VegasSender::GetSlowStartThreshold() const{ + return slowstart_threshold_; +} +CongestionControlType VegasSender::GetCongestionControlType() const{ + return (CongestionControlType)kVegas; +} +std::string VegasSender::GetDebugState() const{ + return "copa"; +} +void VegasSender::OnPacketLost(QuicPacketNumber packet_number, + QuicByteCount lost_bytes, + QuicByteCount prior_in_flight){ + if (largest_sent_at_last_cutback_.IsInitialized() &&packet_number <= largest_sent_at_last_cutback_) { + return; + } + congestion_window_=congestion_window_/2; + congestion_window_=std::max(congestion_window_,min_congestion_window_); + slowstart_threshold_ = congestion_window_; + largest_sent_at_last_cutback_=largest_sent_packet_number_; + num_acked_packets_ = 0; +} +void VegasSender::OnPacketAcked(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount prior_in_flight, + QuicTime event_time){ + largest_acked_packet_number_.UpdateMax(acked_packet_number); + if(!vegas_mode_){ + //reno mode + IncreaseCongestionWindowAsReno(); + return ; + } + QUICHE_CHECK(beg_send_next_.IsInitialized()); + if(acked_packet_number>=beg_send_next_){ + beg_send_next_=largest_sent_packet_number_; + if(rtt_count_<=2){ + IncreaseCongestionWindowAsReno(); + }else{ + QuicTime::Delta base_rtt=baseRTTFilter_.GetBest(); + QuicByteCount target_cwnd=congestion_window_*base_rtt.ToMicroseconds()/min_rtt_.ToMicroseconds(); + target_cwnd=((target_cwnd+kDefaultTCPMSS-1)/kDefaultTCPMSS)*kDefaultTCPMSS; + QUICHE_CHECK(min_rtt_>=base_rtt); + QuicByteCount diff=congestion_window_*(min_rtt_.ToMicroseconds()-base_rtt.ToMicroseconds())/base_rtt.ToMicroseconds(); + if(diff>kGammaCongestionWindow&&InSlowStart()){ + /* Going too fast. Time to slow down + * and switch to congestion avoidance. + */ + + /* Set cwnd to match the actual rate + * exactly: + * cwnd = (actual rate) * baseRTT + * Then we add 1 because the integer + * truncation robs us of full link + * utilization. + */ + congestion_window_=std::min(congestion_window_,target_cwnd); + slowstart_threshold_=congestion_window_; + }else if(InSlowStart()){ + congestion_window_ += kDefaultTCPMSS; + }else{ + if(diff>kBetaCongestionWindow){ + if(congestion_window_>min_congestion_window_){ + congestion_window_-=kDefaultTCPMSS; + } + }else if(diff=congestion_window_) { + congestion_window_ += kDefaultTCPMSS; + num_acked_packets_ = 0; + } + } +} +} diff --git a/quic/model/cc-algo/vegas_sender.h b/quic/model/cc-algo/vegas_sender.h new file mode 100644 index 0000000..136d6be --- /dev/null +++ b/quic/model/cc-algo/vegas_sender.h @@ -0,0 +1,113 @@ +#pragma once +#include +#include +#include + +#include "gquiche/quic/core/congestion_control/bandwidth_sampler.h" +#include "gquiche/quic/core/congestion_control/send_algorithm_interface.h" +#include "gquiche/quic/core/congestion_control/windowed_filter.h" +#include "gquiche/quic/core/crypto/quic_random.h" +#include "gquiche/quic/core/quic_bandwidth.h" +#include "gquiche/quic/core/quic_packet_number.h" +#include "gquiche/quic/core/quic_packets.h" +#include "gquiche/quic/core/quic_time.h" +#include "gquiche/quic/core/quic_unacked_packet_map.h" +#include "gquiche/quic/platform/api/quic_export.h" +#include "gquiche/quic/platform/api/quic_flags.h" +namespace quic{ +class RttStats; +QUIC_EXPORT_PRIVATE class VegasSender:public SendAlgorithmInterface{ +public: + VegasSender(QuicTime now, + const RttStats* rtt_stats, + const QuicUnackedPacketMap* unacked_packets, + QuicPacketCount initial_tcp_congestion_window, + QuicPacketCount max_tcp_congestion_window, + QuicRandom* random, + QuicConnectionStats* stats); + VegasSender(const VegasSender&) = delete; + VegasSender& operator=(const VegasSender&) = delete; + ~VegasSender() override; + + // Start implementation of SendAlgorithmInterface. + bool InSlowStart() const override; + bool InRecovery() const override; + bool ShouldSendProbingPacket() const override; + + void SetFromConfig(const QuicConfig& config, + Perspective perspective) override{} + void ApplyConnectionOptions(const QuicTagVector& connection_options) override{} + + void AdjustNetworkParameters(const NetworkParams& params) override{} + void SetInitialCongestionWindowInPackets( + QuicPacketCount congestion_window) override; + void OnCongestionEvent(bool rtt_updated, + QuicByteCount prior_in_flight, + QuicTime event_time, + const AckedPacketVector& acked_packets, + const LostPacketVector& lost_packets) override; + void OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, + QuicPacketNumber packet_number, + QuicByteCount bytes, + HasRetransmittableData is_retransmittable) override; + void OnPacketNeutered(QuicPacketNumber packet_number) override{} + void OnRetransmissionTimeout(bool /*packets_retransmitted*/) override; + void OnConnectionMigration() override {} + bool CanSend(QuicByteCount bytes_in_flight) override; + QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override; + QuicBandwidth BandwidthEstimate() const override; + QuicByteCount GetCongestionWindow() const override; + QuicByteCount GetSlowStartThreshold() const override; + CongestionControlType GetCongestionControlType() const override; + std::string GetDebugState() const override; + void OnApplicationLimited(QuicByteCount bytes_in_flight) override{} + void PopulateConnectionStats(QuicConnectionStats* stats) const override{} + // End implementation of SendAlgorithmInterface. + +private: + + void OnPacketLost(QuicPacketNumber largest_loss, + QuicByteCount lost_bytes, + QuicByteCount prior_in_flight); + void OnPacketAcked(QuicPacketNumber acked_packet_number, + QuicByteCount acked_bytes, + QuicByteCount prior_in_flight, + QuicTime event_time); + void IncreaseCongestionWindowAsReno(); + // Determines the appropriate pacing rate for the connection. + void CalculatePacingRate(); + + const RttStats* rtt_stats_; + const QuicUnackedPacketMap* unacked_packets_; + QuicRandom* random_; + QuicConnectionStats* stats_; + + // Track the largest packet that has been sent. + QuicPacketNumber largest_sent_packet_number_; + // Track the largest packet that has been acked. + QuicPacketNumber largest_acked_packet_number_; + // Track the largest packet number outstanding when a CWND cutback occurs. + QuicPacketNumber largest_sent_at_last_cutback_; + // The maximum allowed number of bytes in flight. + QuicByteCount congestion_window_; + // The initial value of the |congestion_window_|. + QuicByteCount initial_congestion_window_; + // The largest value the |congestion_window_| can achieve. + QuicByteCount max_congestion_window_; + // The smallest value the |congestion_window_| can achieve. + QuicByteCount min_congestion_window_; + // Slow start congestion window in bytes, aka ssthresh. + QuicByteCount slowstart_threshold_; + // The current pacing rate of the connection. + QuicBandwidth pacing_rate_; + using RTTFilter=WindowedFilter,uint64_t,uint64_t> ; + RTTFilter baseRTTFilter_; + QuicTime::Delta min_rtt_; + int32_t rtt_count_; + QuicPacketNumber beg_send_next_; + uint64_t num_acked_packets_; + bool vegas_mode_; + +}; +} \ No newline at end of file diff --git a/quic/model/ns3-quic-congestion-factory.cc b/quic/model/ns3-quic-congestion-factory.cc index 5bb9811..2ec60b5 100644 --- a/quic/model/ns3-quic-congestion-factory.cc +++ b/quic/model/ns3-quic-congestion-factory.cc @@ -5,6 +5,7 @@ #include "ns3-quic-congestion-factory.h" #include "ns3-quic-no-destructor.h" #include "cc-algo/copa_sender.h" +#include "cc-algo/vegas_sender.h" namespace quic{ class Ns3QuicCongestionFactory:public AbstractCongestionFactory{ public: @@ -50,6 +51,10 @@ SendAlgorithmInterface* Ns3QuicCongestionFactory::Create( algo=new CopaSender(clock->ApproximateNow(), rtt_stats, unacked_packets, initial_congestion_window, max_congestion_window, random, stats); + }else if(kVegas==v){ + algo=new VegasSender(clock->ApproximateNow(), rtt_stats, unacked_packets, + initial_congestion_window, max_congestion_window, + random, stats); }else{ algo=new TcpCubicSenderBytes(clock, rtt_stats, true /* use Reno */, initial_congestion_window, diff --git a/quic/model/ns3-quic-congestion-factory.h b/quic/model/ns3-quic-congestion-factory.h index 1a1a2b4..8c09cb7 100644 --- a/quic/model/ns3-quic-congestion-factory.h +++ b/quic/model/ns3-quic-congestion-factory.h @@ -4,6 +4,7 @@ namespace quic{ enum CongestionControlType2{ kCC0=CongestionControlType::kBBRv2, kCopa, + kVegas, }; void RegisterExternalCongestionFactory(); } \ No newline at end of file diff --git a/quic/wscript b/quic/wscript index 7d7c225..f200eb1 100644 --- a/quic/wscript +++ b/quic/wscript @@ -28,6 +28,7 @@ def build(bld): module.env.append_value("LIB", ["quiche_shared",'protobuf']) module.source = [ 'model/cc-algo/copa_sender.cc', + 'model/cc-algo/vegas_sender.cc', 'model/ns3-client-network-helper.cc', 'model/ns3-packet-writer.cc', 'model/ns3-quic-alarm-engine.cc', diff --git a/results/1-vegas-goodput.png b/results/1-vegas-goodput.png new file mode 100644 index 0000000..0dec5f6 Binary files /dev/null and b/results/1-vegas-goodput.png differ diff --git a/results/1-vegas-inflight.png b/results/1-vegas-inflight.png new file mode 100644 index 0000000..a3da305 Binary files /dev/null and b/results/1-vegas-inflight.png differ diff --git a/results/1-vegas-owd.png b/results/1-vegas-owd.png new file mode 100644 index 0000000..789a3ca Binary files /dev/null and b/results/1-vegas-owd.png differ diff --git a/results/1-vegas-send-rate.png b/results/1-vegas-send-rate.png new file mode 100644 index 0000000..07d592c Binary files /dev/null and b/results/1-vegas-send-rate.png differ diff --git a/scratch/quic-main.cc b/scratch/quic-main.cc index 89a5f1e..d67185d 100644 --- a/scratch/quic-main.cc +++ b/scratch/quic-main.cc @@ -89,6 +89,9 @@ void test_app(Time app_start,Time app_stop,uint8_t client_log_flag, }else if(0==cc_name.compare("copa")){ cc_type=static_cast(quic::kCopa); algo=cc_name; + }else if(0==cc_name.compare("vegas")){ + cc_type=static_cast(quic::kVegas); + algo=cc_name; } std::string log_path=trace_folder+cc_name+"/";