Add a flag to preallocate memory for unacked packets in both QuicUnackedPacketMap and BandwidthSampler via a command-line flag.

Protected by FLAGS_quic_preallocate_unacked_packets.

PiperOrigin-RevId: 764817335
diff --git a/quiche/common/quiche_protocol_flags_list.h b/quiche/common/quiche_protocol_flags_list.h
index b19aa72..de2a763 100644
--- a/quiche/common/quiche_protocol_flags_list.h
+++ b/quiche/common/quiche_protocol_flags_list.h
@@ -280,4 +280,10 @@
     uint64_t, quic_multiplexer_alarm_granularity_us, 1000,
     "Alarm update granularity used by the QUICHE multiplexer alarm")
 
+QUICHE_PROTOCOL_FLAG(
+    size_t, quic_preallocate_unacked_packets, 0,
+    "Preallocate the specified number of entries in the unacked packet map.  "
+    "If QuicConnection::GetUnackedMapInitialCapacity() already specifies a "
+    "number, the larger of two applies.")
+
 #endif
diff --git a/quiche/quic/core/congestion_control/bandwidth_sampler.cc b/quiche/quic/core/congestion_control/bandwidth_sampler.cc
index f41c80e..1493c08 100644
--- a/quiche/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quiche/quic/core/congestion_control/bandwidth_sampler.cc
@@ -5,6 +5,7 @@
 #include "quiche/quic/core/congestion_control/bandwidth_sampler.h"
 
 #include <algorithm>
+#include <cstddef>
 #include <ostream>
 
 #include "quiche/quic/core/quic_types.h"
@@ -148,7 +149,13 @@
       max_ack_height_tracker_(max_height_tracker_window_length),
       total_bytes_acked_after_last_ack_event_(0),
       overestimate_avoidance_(false),
-      limit_max_ack_height_tracker_by_send_rate_(false) {}
+      limit_max_ack_height_tracker_by_send_rate_(false) {
+  const size_t preallocate_count =
+      GetQuicFlag(quic_preallocate_unacked_packets);
+  if (preallocate_count > 0) {
+    connection_state_map_.Reserve(preallocate_count);
+  }
+}
 
 BandwidthSampler::BandwidthSampler(const BandwidthSampler& other)
     : total_bytes_sent_(other.total_bytes_sent_),
diff --git a/quiche/quic/core/packet_number_indexed_queue.h b/quiche/quic/core/packet_number_indexed_queue.h
index e896f87..ba50642 100644
--- a/quiche/quic/core/packet_number_indexed_queue.h
+++ b/quiche/quic/core/packet_number_indexed_queue.h
@@ -5,6 +5,8 @@
 #ifndef QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
 #define QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
 
+#include <cstddef>
+
 #include "quiche/quic/core/quic_constants.h"
 #include "quiche/quic/core/quic_packet_number.h"
 #include "quiche/quic/core/quic_types.h"
@@ -91,6 +93,9 @@
     return first_packet_ + entries_.size() - 1;
   }
 
+  // Reserves the specified memory capacity in the underlying deque.
+  void Reserve(size_t capacity) { entries_.reserve(capacity); }
+
  private:
   // Wrapper around T used to mark whether the entry is actually in the map.
   struct QUICHE_NO_EXPORT EntryWrapper : T {
diff --git a/quiche/quic/core/quic_unacked_packet_map.cc b/quiche/quic/core/quic_unacked_packet_map.cc
index c9d3541..419982b 100644
--- a/quiche/quic/core/quic_unacked_packet_map.cc
+++ b/quiche/quic/core/quic_unacked_packet_map.cc
@@ -4,6 +4,7 @@
 
 #include "quiche/quic/core/quic_unacked_packet_map.h"
 
+#include <algorithm>
 #include <cstddef>
 #include <limits>
 #include <type_traits>
@@ -18,6 +19,7 @@
 #include "quiche/quic/core/quic_utils.h"
 #include "quiche/quic/platform/api/quic_bug_tracker.h"
 #include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
 #include "quiche/common/simple_buffer_allocator.h"
 
 namespace quic {
@@ -730,4 +732,9 @@
   return content;
 }
 
+void QuicUnackedPacketMap::ReserveInitialCapacity(size_t initial_capacity) {
+  const size_t flag_capacity = GetQuicFlag(quic_preallocate_unacked_packets);
+  unacked_packets_.reserve(std::max(initial_capacity, flag_capacity));
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/quic_unacked_packet_map.h b/quiche/quic/core/quic_unacked_packet_map.h
index 30e3394..fc6e89d 100644
--- a/quiche/quic/core/quic_unacked_packet_map.h
+++ b/quiche/quic/core/quic_unacked_packet_map.h
@@ -257,9 +257,7 @@
     return supports_multiple_packet_number_spaces_;
   }
 
-  void ReserveInitialCapacity(size_t initial_capacity) {
-    unacked_packets_.reserve(initial_capacity);
-  }
+  void ReserveInitialCapacity(size_t initial_capacity);
 
   std::string DebugString() const {
     return absl::StrCat(
diff --git a/quiche/quic/core/quic_unacked_packet_map_test.cc b/quiche/quic/core/quic_unacked_packet_map_test.cc
index b859bc7..3bfcc37 100644
--- a/quiche/quic/core/quic_unacked_packet_map_test.cc
+++ b/quiche/quic/core/quic_unacked_packet_map_test.cc
@@ -760,7 +760,7 @@
   EXPECT_FALSE(unacked_packets_.GetLastPacketContent() & (1 << ACK_FRAME));
 }
 
-TEST_P(QuicUnackedPacketMapTest, ReserveInitialCapacityTest) {
+TEST_P(QuicUnackedPacketMapTest, ReserveInitialCapacity) {
   QuicUnackedPacketMap unacked_packets(GetParam());
   ASSERT_EQ(QuicUnackedPacketMapPeer::GetCapacity(unacked_packets), 0u);
   unacked_packets.ReserveInitialCapacity(16);
@@ -771,6 +771,18 @@
   ASSERT_EQ(QuicUnackedPacketMapPeer::GetCapacity(unacked_packets), 16u);
 }
 
+TEST_P(QuicUnackedPacketMapTest, ReserveInitialCapacityViaFlag) {
+  SetQuicFlag(quic_preallocate_unacked_packets, 1024);
+  QuicUnackedPacketMap unacked_packets(GetParam());
+  ASSERT_EQ(QuicUnackedPacketMapPeer::GetCapacity(unacked_packets), 0u);
+  unacked_packets.ReserveInitialCapacity(16);
+  QuicStreamId stream_id(1);
+  SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id));
+  unacked_packets.AddSentPacket(&packet, TransmissionType::NOT_RETRANSMISSION,
+                                now_, true, true, ECN_NOT_ECT);
+  ASSERT_EQ(QuicUnackedPacketMapPeer::GetCapacity(unacked_packets), 1024u);
+}
+
 TEST_P(QuicUnackedPacketMapTest, DebugString) {
   EXPECT_EQ(unacked_packets_.DebugString(),
             "{size: 0, least_unacked: 1, largest_sent_packet: uninitialized, "