quic: avoid memory fragmentation issue

Original PR: https://github.com/nodejs/quic/pull/388

Previously, QuicPacket was allocating an std::vector<uint8_t> of
NGTCP2_MAX_PKT_SIZE bytes, then the packet would be serialized into the
buffer, and the std::vector would be resized based on the number of
bytes serialized. I suspect the memory fragmentation that you're seeing
is because of those resize operations not freeing memory in chunks that
are aligned with the allocation. This changes QuicPacket to use a stack
allocation that is always NGTCP2_MAX_PKT_SIZE bytes and the size of the
serialized packet is just recorded without any resizing. When the memory
is freed now, it should be freed in large enough chunks to cover
subsequent allocations.

Signed-off-by: James M Snell <jasnell@gmail.com>

PR-URL: https://github.com/nodejs/node/pull/33912
Reviewed-By: David Carlier <devnexen@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
This commit is contained in:
James M Snell committed 2020-06-18 15:00:13 -07:00
1 parent 16116f5f5f
commit d7d79f2163
3 files changed
+22 -14

No files matched your search

+3 -2
View File
@@ -18,6 +18,7 @@ namespace quic {
std::unique_ptr<QuicPacket> QuicPacket::Create(
const char* diagnostic_label,
size_t len) {
CHECK_LE(len, MAX_PKTLEN);
return std::make_unique<QuicPacket>(diagnostic_label, len);
}
@@ -27,8 +28,8 @@ std::unique_ptr<QuicPacket> QuicPacket::Copy(
}
void QuicPacket::set_length(size_t len) {
CHECK_LE(len, data_.size());
data_.resize(len);
CHECK_LE(len, MAX_PKTLEN);
len_ = len;
}
int QuicEndpoint::Send(
+6 -7
View File
@@ -83,14 +83,15 @@ bool IsShortHeader(
} // namespace
QuicPacket::QuicPacket(const char* diagnostic_label, size_t len) :
data_(len),
data_{0},
len_(len),
diagnostic_label_(diagnostic_label) {
CHECK_LE(len, NGTCP2_MAX_PKT_SIZE);
CHECK_LE(len, MAX_PKTLEN);
}
QuicPacket::QuicPacket(const QuicPacket& other) :
QuicPacket(other.diagnostic_label_, other.data_.size()) {
memcpy(data_.data(), other.data_.data(), other.data_.size());
QuicPacket(other.diagnostic_label_, other.len_) {
memcpy(&data_, &other.data_, other.len_);
}
const char* QuicPacket::diagnostic_label() const {
@@ -98,9 +99,7 @@ const char* QuicPacket::diagnostic_label() const {
diagnostic_label_ : "unspecified";
}
void QuicPacket::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("data", data_);
}
void QuicPacket::MemoryInfo(MemoryTracker* tracker) const {}
QuicSocketListener::~QuicSocketListener() {
if (socket_)
+13 -5
View File
@@ -112,6 +112,13 @@ class JSQuicSocketListener : public QuicSocketListener {
void OnDestroy() override;
};
// This is just a formality as the QUIC spec normatively
// defines that the ipv4 max pktlen is always going to be
// larger than the ipv6 max pktlen, but in the off chance
// ever changes (which is unlikely) we check here.
constexpr size_t MAX_PKTLEN =
std::max<size_t>(NGTCP2_MAX_PKTLEN_IPV4, NGTCP2_MAX_PKTLEN_IPV6);
// A serialized QuicPacket to be sent by a QuicSocket instance.
class QuicPacket : public MemoryRetainer {
public:
@@ -141,7 +148,7 @@ class QuicPacket : public MemoryRetainer {
// QuicPacket instance will be freed.
static inline std::unique_ptr<QuicPacket> Create(
const char* diagnostic_label = nullptr,
size_t len = NGTCP2_MAX_PKTLEN_IPV4);
size_t len = MAX_PKTLEN);
// Copy the data of the QuicPacket to a new one. Currently,
// this is only used when retransmitting close connection
@@ -151,11 +158,11 @@ class QuicPacket : public MemoryRetainer {
QuicPacket(const char* diagnostic_label, size_t len);
QuicPacket(const QuicPacket& other);
uint8_t* data() { return data_.data(); }
size_t length() const { return data_.size(); }
uint8_t* data() { return data_; }
size_t length() const { return len_; }
uv_buf_t buf() const {
return uv_buf_init(
const_cast<char*>(reinterpret_cast<const char*>(data_.data())),
const_cast<char*>(reinterpret_cast<const char*>(&data_)),
length());
}
inline void set_length(size_t len);
@@ -166,7 +173,8 @@ class QuicPacket : public MemoryRetainer {
SET_SELF_SIZE(QuicPacket);
private:
std::vector<uint8_t> data_;
uint8_t data_[MAX_PKTLEN];
size_t len_ = MAX_PKTLEN;
const char* diagnostic_label_ = nullptr;
};