Skip to content

Commit 53d2e30

Browse files
authored
FEAT: implement the DCQCN (#548)
closes #176
1 parent a88c106 commit 53d2e30

7 files changed

Lines changed: 397 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#include "dcqcn.hpp"
2+
3+
#include "scheduler/scheduler.hpp"
4+
5+
namespace sim {
6+
DCQCN::DCQCN(const ParamsDQCCN& a_params)
7+
: m_params(a_params),
8+
m_current_rate(a_params.rpg_min_rate),
9+
m_target_rate(m_current_rate),
10+
m_alpha(m_params.initial_alpha_value) {}
11+
12+
void DCQCN::start() {
13+
Scheduler& sched = Scheduler::get_instance();
14+
TimeNs now = sched.get_current_time();
15+
16+
// enqueue first alpha update event
17+
sched.add(now + m_params.dce_tcp_rtt, [this]() { on_alpha_timer(); });
18+
19+
// enqueue first rate increase timer event
20+
sched.add(now + m_params.rpg_time_reset, [this, last_cnp = m_last_cnp]() {
21+
on_rate_increase_timer(last_cnp);
22+
});
23+
24+
sched.add(now + m_params.rate_reduce_monitor_period,
25+
[this]() { on_rate_reduce_monitor_period(); });
26+
}
27+
28+
void DCQCN::stop() { m_stop_request = true; }
29+
30+
void DCQCN::on_cnp() {
31+
if (m_stop_request) {
32+
return;
33+
}
34+
35+
TimeNs now = Scheduler::get_instance().get_current_time();
36+
m_last_cnp = now;
37+
m_bytes_from_last_byte_reset = SizeByte(0ul);
38+
m_time_counter = 0;
39+
}
40+
41+
void DCQCN::on_data_delivery(SizeByte size) {
42+
if (m_stop_request) {
43+
return;
44+
}
45+
46+
m_bytes_from_last_byte_reset += size;
47+
while (m_bytes_from_last_byte_reset >= m_params.rpg_byte_reset) {
48+
m_bytes_from_last_byte_reset -= m_params.rpg_byte_reset;
49+
m_byte_counter++;
50+
on_rate_increase_event();
51+
}
52+
}
53+
54+
SpeedMbps DCQCN::get_rate() const { return m_current_rate; }
55+
56+
void DCQCN::on_rate_reduce_monitor_period() {
57+
if (m_stop_request) {
58+
return;
59+
}
60+
61+
Scheduler& sched = Scheduler::get_instance();
62+
TimeNs now = sched.get_current_time();
63+
if (m_last_cnp &&
64+
now <= m_last_cnp.value() + m_params.rate_reduce_monitor_period) {
65+
// found CNP over last m_params.rate_reduce_monitor_period => reset
66+
// timers
67+
68+
if (m_params.clamp_tgt_rate && !m_dec_target_rate) {
69+
m_target_rate = m_current_rate;
70+
}
71+
m_dec_target_rate = true;
72+
73+
// decrement current rate
74+
m_current_rate =
75+
m_current_rate *
76+
std::max(m_params.rpg_min_dec_fac,
77+
(1 - m_alpha / static_cast<double>(1 << m_params.rpg_gd)));
78+
m_current_rate = std::max(m_current_rate, m_params.rpg_min_rate);
79+
m_bytes_from_last_byte_reset = SizeByte(0ul);
80+
m_byte_counter = 0;
81+
m_time_counter = 0;
82+
}
83+
84+
sched.add(now + m_params.rpg_time_reset, [this, last_cnp = m_last_cnp]() {
85+
on_rate_increase_timer(last_cnp);
86+
});
87+
}
88+
89+
void DCQCN::on_alpha_timer() {
90+
if (m_stop_request) {
91+
return;
92+
}
93+
94+
Scheduler& sched = Scheduler::get_instance();
95+
TimeNs now = sched.get_current_time();
96+
static constexpr int two_pow = (1 << 10);
97+
if (m_last_cnp && now <= m_last_cnp.value() + m_params.dce_tcp_rtt) {
98+
// cnp detected over last m_params.dce_tcp_rtt => increment alpha
99+
m_alpha =
100+
(m_params.dce_tcp_g / static_cast<double>(two_pow)) * m_alpha +
101+
two_pow - m_params.dce_tcp_g;
102+
} else {
103+
// no cnp over last last m_params.dce_tcp_rtt => decrement alpha
104+
m_alpha = (m_params.dce_tcp_g / static_cast<double>(two_pow)) * m_alpha;
105+
}
106+
}
107+
108+
void DCQCN::on_rate_increase_timer(std::optional<TimeNs> last_elapced_cnp) {
109+
if (m_stop_request) {
110+
return;
111+
}
112+
113+
if (m_last_cnp != last_elapced_cnp) {
114+
// there was cnp over m_params.rpg_time_reset => event should be
115+
// cancelled
116+
return;
117+
}
118+
// no cnp over last m_params.rpg_time_reset => update time counter &
119+
// reschedule event
120+
m_time_counter++;
121+
on_rate_increase_event();
122+
Scheduler& sched = Scheduler::get_instance();
123+
TimeNs now = sched.get_current_time();
124+
sched.add(now + m_params.rpg_time_reset,
125+
[this, new_last_elapced_cnp = m_last_cnp]() {
126+
on_rate_increase_timer(new_last_elapced_cnp);
127+
});
128+
}
129+
130+
void DCQCN::on_rate_increase_event() {
131+
if (m_stop_request) {
132+
return;
133+
}
134+
135+
if (std::max(m_time_counter, m_byte_counter) < m_params.rpg_threshold) {
136+
// fast recovery; no target rate update
137+
} else if (std::min(m_time_counter, m_byte_counter) <=
138+
m_params.rpg_threshold) {
139+
// additive increase
140+
m_target_rate += m_params.rpg_ai_rate;
141+
} else {
142+
// hyper increase
143+
m_target_rate += m_params.rpg_hai_rate;
144+
}
145+
m_current_rate = (m_current_rate + m_target_rate) / 2.0;
146+
}
147+
148+
} // namespace sim
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#pragma once
2+
#include <optional>
3+
4+
#include "types.hpp"
5+
6+
namespace sim {
7+
8+
// DQCCN congestion control realization
9+
// Based on NVIDIA documentation:
10+
// https://enterprise-support.nvidia.com/s/article/DCQCN-CC-algorithm
11+
// https://enterprise-support.nvidia.com/s/article/dcqcn-parameters
12+
13+
struct ParamsDQCCN {
14+
// -------------Rate increment-------------
15+
16+
// The time period between rate increase events.
17+
TimeUs rpg_time_reset = TimeUs(300);
18+
19+
// The sent bytes counter between rate increase events.
20+
SizeByte rpg_byte_reset = SizeByte(64 * 32767ul);
21+
22+
// The threshold of rate increase events for moving to next rate increase
23+
// phase.
24+
std::size_t rpg_threshold = 1;
25+
26+
// The rate increase value in the Additive Increase phase.
27+
SpeedMbps rpg_ai_rate = SpeedMbps(5);
28+
29+
// The rate increase value in the Hyper Increase phase.
30+
SpeedMbps rpg_hai_rate = SpeedMbps(50);
31+
32+
// -------------Alpha update-------------
33+
34+
// This parameter sets the initial value of alpha that should be used when
35+
// receiving the first CNP for a flow.
36+
int initial_alpha_value = 1023;
37+
38+
// Controls aggressiveness of alpha's updates
39+
// The lower G is, the more aggressive are the changes.
40+
int dce_tcp_g = 1019;
41+
42+
// The Time period between alpha updates.
43+
TimeUs dce_tcp_rtt = TimeUs(1);
44+
45+
// -------------Rate decrement-------------
46+
47+
// The time period between rate reductions.
48+
TimeUs rate_reduce_monitor_period = TimeUs(4);
49+
50+
// Rates (current, target) on first CNP (0 – 85% of line rate).
51+
SpeedMbps rate_to_set_on_first_cnp = SpeedMbps(0);
52+
53+
// If true, every rate decreases. The target rate is updated to the current
54+
// rate.
55+
// Otherwise, the target rate is updated to the current rate only on the
56+
// first decrement after the increment event.
57+
bool clamp_tgt_rate = false;
58+
59+
// The coefficient between alpha and the rate reduction factor.
60+
// Log2 of value in fixed point with 10 in the fraction part
61+
int rpg_gd = 11;
62+
63+
// Minimal rate limit of the QP.
64+
SpeedMbps rpg_min_rate = SpeedMbps(1);
65+
// Maximal rate limit of the QP.
66+
double rpg_min_dec_fac = 0.5;
67+
};
68+
69+
class DCQCN {
70+
public:
71+
explicit DCQCN(const ParamsDQCCN& a_params);
72+
73+
// enqueue initial events
74+
void start();
75+
76+
// stop creating new events
77+
void stop();
78+
79+
// Calls when sender got congestion notification
80+
void on_cnp();
81+
82+
// Calls when sender got asknowledge of receiving data_size data
83+
void on_data_delivery(SizeByte data_size);
84+
85+
SpeedMbps get_rate() const;
86+
87+
private:
88+
void on_rate_reduce_monitor_period();
89+
void on_alpha_timer();
90+
91+
void on_rate_increase_timer(std::optional<TimeNs> last_elapced_cnp);
92+
void on_rate_increase_event();
93+
94+
ParamsDQCCN m_params;
95+
SpeedMbps m_current_rate;
96+
SpeedMbps m_target_rate;
97+
bool m_dec_target_rate = false;
98+
99+
// Size & time counters (T & BC on Increment scheme part of NVIDIA docs)
100+
SizeByte m_bytes_from_last_byte_reset = SizeByte(0ul);
101+
std::uint32_t m_time_counter = 0;
102+
std::uint32_t m_byte_counter = 0;
103+
104+
// Last time CNP was got
105+
std::optional<TimeNs> m_last_cnp = std::nullopt;
106+
107+
int m_alpha;
108+
bool m_stop_request = false;
109+
};
110+
111+
} // namespace sim

source/scheduler/scheduler.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ bool Scheduler::tick() {
3535
return true;
3636
}
3737

38+
uint32_t Scheduler::tick_to(TimeNs time_point) {
39+
time_point += TimeNs(1);
40+
if (time_point <= m_current_event_local_time) {
41+
return 0;
42+
}
43+
bool arrived = false;
44+
uint32_t events_count = 0;
45+
add(time_point, [&arrived]() { arrived = true; });
46+
while (!arrived && tick()) {
47+
events_count++;
48+
}
49+
if (arrived) {
50+
events_count--;
51+
}
52+
return events_count;
53+
}
54+
3855
void Scheduler::clear() {
3956
m_near_events.clear();
4057
std::priority_queue<NewEvent, std::vector<NewEvent>,

source/scheduler/scheduler.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Scheduler {
3636

3737
void clear(); // Clear all events
3838
bool tick();
39+
uint32_t tick_to(TimeNs time_point);
3940
TimeNs get_current_time();
4041

4142
private:

source/types.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88
#include "units/units.hpp"
99

1010
using TimeNs = Time<Nanosecond>;
11+
using TimeUs = Time<Microsecond>;
12+
1113
using SizeByte = Size<Byte>;
14+
1215
using SpeedGbps = Speed<GBit, Second>;
16+
using SpeedMbps = Speed<MBit, Second>;
17+
1318
using Id = std::string;
1419
using OnDeliveryCallback = std::function<void()>;
1520

source/units/speed.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class Speed {
4343
return Speed<Bit, Nanosecond>(m_value_bit_per_ns * mult);
4444
}
4545

46+
constexpr ThisSpeed operator/(double mult) const {
47+
return Speed<Bit, Nanosecond>(m_value_bit_per_ns / mult);
48+
}
49+
4650
constexpr double operator/(ThisSpeed speed) const {
4751
return m_value_bit_per_ns / speed.value_bit_per_ns();
4852
}

0 commit comments

Comments
 (0)