[libcxx-commits] [libcxx] [libc++] Implement single element vector::insert in terms of emplace (PR #210284)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 24 01:40:32 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210284

>From 3aa2eddc8224998dfb8ea413f27c394f9cbfdca4 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 11:34:33 +0200
Subject: [PATCH] [libc++] Implement single element vector::insert in terms of
 emplace

---
 libcxx/include/__vector/vector.h              |  53 ++-----
 .../sequence/sequence_container_benchmarks.h  | 132 +++++++++++++++++-
 2 files changed, 134 insertions(+), 51 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 5d2ef8fc2a583..c12fc039473fe 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -58,6 +58,7 @@
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/remove_const_ref.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
 #include <__utility/exception_guard.h>
@@ -504,9 +505,14 @@ class _LIBCPP_WARN_UNUSED vector {
     this->__destruct_at_end(__layout_.__end_ptr() - 1);
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x) {
+    return emplace(__position, __x);
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x) {
+    return emplace(__position, std::move(__x));
+  }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x);
   template <class... _Args>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator emplace(const_iterator __position, _Args&&... __args);
 
@@ -1128,49 +1134,6 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
-  pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(__x);
-    } else {
-      __move_range(__p, __end, __p + 1);
-      const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
-      if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
-        ++__xr;
-      *__p = *__xr;
-    }
-  } else {
-    _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-    __v.emplace_back(__x);
-    __p = __layout_.__relocate_with_pivot(__v, __p);
-  }
-  return __make_iter(__p);
-}
-
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
-  pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(std::move(__x));
-    } else {
-      __move_range(__p, __end, __p + 1);
-      *__p = std::move(__x);
-    }
-  } else {
-    _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-    __v.emplace_back(std::move(__x));
-    __p = __layout_.__relocate_with_pivot(__v, __p);
-  }
-  return __make_iter(__p);
-}
-
 template <class _Tp, class _Allocator>
 template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index 68cd74a9ca782..07956ec0bea95 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -60,6 +60,10 @@ void sequence_container_benchmarks(std::string container) {
     benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(32)->Arg(8192);
   };
 
+  auto bench_unsized = [&](std::string operation, auto f) {
+    benchmark::RegisterBenchmark(container + "::" + operation, f);
+  };
+
   /////////////////////////
   // Constructors
   /////////////////////////
@@ -184,7 +188,7 @@ void sequence_container_benchmarks(std::string container) {
   /////////////////////////
   // Insertion
   /////////////////////////
-  for (auto gen : generators)
+  for (auto gen : generators) {
     bench("insert(begin)" + tostr(gen), [gen](auto& st) TEST_ALIGN_BENCHMARK {
       auto const size = st.range(0);
       std::vector<ValueType> in;
@@ -205,8 +209,29 @@ void sequence_container_benchmarks(std::string container) {
       }
     });
 
+    bench("emplace(begin)" + tostr(gen), [gen](auto& st) {
+      auto const size = st.range(0);
+      std::vector<ValueType> in;
+      std::generate_n(std::back_inserter(in), size, gen);
+      DoNotOptimizeData(in);
+
+      Container c(in.begin(), in.end());
+      DoNotOptimizeData(c);
+
+      ValueType value = gen();
+      benchmark::DoNotOptimize(value);
+
+      for ([[maybe_unused]] auto _ : st) {
+        c.emplace(c.begin(), value);
+        DoNotOptimizeData(c);
+
+        c.erase(std::prev(c.end())); // avoid growing indefinitely
+      }
+    });
+  }
+
   if constexpr (std::random_access_iterator<typename Container::iterator>) {
-    for (auto gen : generators)
+    for (auto gen : generators) {
       bench("insert(middle)" + tostr(gen), [gen](auto& st) TEST_ALIGN_BENCHMARK {
         auto const size = st.range(0);
         std::vector<ValueType> in;
@@ -224,9 +249,30 @@ void sequence_container_benchmarks(std::string container) {
           c.insert(mid, value);
           DoNotOptimizeData(c);
 
-          c.erase(c.end() - 1); // avoid growing indefinitely
+          c.pop_back(); // avoid growing indefinitely
+        }
+      });
+      bench("emplace(middle)" + tostr(gen), [gen](auto& st) {
+        auto const size = st.range(0);
+        std::vector<ValueType> in;
+        std::generate_n(std::back_inserter(in), size, gen);
+        DoNotOptimizeData(in);
+
+        Container c(in.begin(), in.end());
+        DoNotOptimizeData(c);
+
+        ValueType value = gen();
+        benchmark::DoNotOptimize(value);
+
+        for ([[maybe_unused]] auto _ : st) {
+          auto mid = c.begin() + (size / 2); // requires random-access iterators in order to make sense
+          c.emplace(mid, value);
+          DoNotOptimizeData(c);
+
+          c.pop_back(); // avoid growing indefinitely
         }
       });
+    }
   }
 
   if constexpr (requires(Container c) { c.reserve(0); }) {
@@ -319,7 +365,7 @@ void sequence_container_benchmarks(std::string container) {
     if constexpr (has_capacity) {
       // For containers where we can observe capacity(), push_back a single element
       // without reserving to ensure the container needs to grow
-      for (auto gen : generators)
+      for (auto gen : generators) {
         bench("push_back() (growing)" + tostr(gen), [gen](auto& st) TEST_ALIGN_BENCHMARK {
           auto const size = st.range(0);
           std::vector<ValueType> in;
@@ -348,12 +394,41 @@ void sequence_container_benchmarks(std::string container) {
             st.ResumeTiming();
           }
         });
+        bench("emplace_back() (growing)" + tostr(gen), [gen](auto& st) {
+          auto const size = st.range(0);
+          std::vector<ValueType> in;
+          std::generate_n(std::back_inserter(in), size, gen);
+          DoNotOptimizeData(in);
+
+          auto at_capacity = [](Container c) {
+            while (c.size() < c.capacity())
+              c.push_back(c.back());
+            return c;
+          };
+
+          std::vector<Container> c(BatchSize, at_capacity(Container(in.begin(), in.end())));
+
+          while (st.KeepRunningBatch(BatchSize)) {
+            for (std::size_t i = 0; i != BatchSize; ++i) {
+              c[i].emplace_back(in[i]);
+              DoNotOptimizeData(c[i]);
+            }
+
+            st.PauseTiming();
+            for (std::size_t i = 0; i != BatchSize; ++i) {
+              c[i] = at_capacity(Container(in.begin(), in.end()));
+              assert(c[i].size() == c[i].capacity());
+            }
+            st.ResumeTiming();
+          }
+        });
+      }
     }
 
     // For containers where we can reserve, push_back a single element after reserving to
     // ensure the container doesn't grow
     if constexpr (has_reserve) {
-      for (auto gen : generators)
+      for (auto gen : generators) {
         bench("push_back() (with reserve)" + tostr(gen), [gen](auto& st) TEST_ALIGN_BENCHMARK {
           auto const size = st.range(0);
           std::vector<ValueType> in;
@@ -374,10 +449,35 @@ void sequence_container_benchmarks(std::string container) {
             c.erase(c.end() - BatchSize, c.end());
           }
         });
+
+        bench_unsized("emplace_back() (with reserve)" + tostr(gen), [gen](auto& st) {
+          constexpr auto batch_size = 8192;
+
+          std::vector<ValueType> in;
+          std::generate_n(std::back_inserter(in), batch_size, gen);
+          DoNotOptimizeData(in);
+
+          Container c;
+          // Ensure the container has enough capacity
+          c.reserve(batch_size);
+          DoNotOptimizeData(c);
+
+          while (st.KeepRunningBatch(batch_size)) {
+            for (std::size_t i = 0; i != BatchSize; ++i) {
+              c.emplace_back(in[i]);
+            }
+            DoNotOptimizeData(c);
+
+            st.PauseTiming();
+            c.clear();
+            st.ResumeTiming();
+          }
+        });
+      }
     }
 
     // push_back many elements: this is amortized constant for std::vector but not all containers
-    for (auto gen : generators)
+    for (auto gen : generators) {
       bench("push_back() (many elements)" + tostr(gen), [gen](auto& st) TEST_ALIGN_BENCHMARK {
         auto const size = st.range(0);
         std::vector<ValueType> in;
@@ -397,6 +497,26 @@ void sequence_container_benchmarks(std::string container) {
           st.ResumeTiming();
         }
       });
+      bench("emplace_back() (many elements)" + tostr(gen), [gen](auto& st) {
+        auto const size = st.range(0);
+        std::vector<ValueType> in;
+        std::generate_n(std::back_inserter(in), size, gen);
+        DoNotOptimizeData(in);
+
+        Container c;
+        DoNotOptimizeData(c);
+        while (st.KeepRunningBatch(size)) {
+          for (int i = 0; i != size; ++i) {
+            c.emplace_back(in[i]);
+          }
+          DoNotOptimizeData(c);
+
+          st.PauseTiming();
+          c = Container();
+          st.ResumeTiming();
+        }
+      });
+    }
 
 #if defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L
     for (auto gen : generators)



More information about the libcxx-commits mailing list