This PR optimizes the input iterator overload of `assign(_InputIterator, _InputIterator)` in `std::vector<_Tp, _Allocator>` by directly assigning to already initialized memory, rather than first destroying existing elements and then constructing new ones. By eliminating unnecessary destruction and construction, the proposed algorithm enhances the performance by up to 2x for trivial element types (e.g., `std::vector<int>`), up to 2.6x for non-trivial element types like `std::vector<std::string>`, and up to 3.4x for more complex non-trivial types (e.g., `std::vector<std::vector<int>>`). ### Google Benchmarks Benchmark tests (`libcxx/test/benchmarks/vector_operations.bench.cpp`) were conducted for the `assign()` implementations before and after this patch. The tests focused on trivial element types like `std::vector<int>`, and non-trivial element types such as `std::vector<std::string>` and `std::vector<std::vector<int>>`. #### Before ``` ------------------------------------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------------------------------------- BM_AssignInputIterIter/vector_int/1024/1024 1157 ns 1169 ns 608188 BM_AssignInputIterIter<32>/vector_string/1024/1024 14559 ns 14710 ns 47277 BM_AssignInputIterIter<32>/vector_vector_int/1024/1024 26846 ns 27129 ns 25925 ``` #### After ``` ------------------------------------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------------------------------------- BM_AssignInputIterIter/vector_int/1024/1024 561 ns 566 ns 1242251 BM_AssignInputIterIter<32>/vector_string/1024/1024 5604 ns 5664 ns 128365 BM_AssignInputIterIter<32>/vector_vector_int/1024/1024 7927 ns 8012 ns 88579 ```
168 lines
5.0 KiB
C++
168 lines
5.0 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef BENCHMARK_GENERATE_INPUT_H
|
|
#define BENCHMARK_GENERATE_INPUT_H
|
|
|
|
#include <algorithm>
|
|
#include <climits>
|
|
#include <cstddef>
|
|
#include <random>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
static const char Letters[] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
|
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
|
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
|
static const std::size_t LettersSize = sizeof(Letters);
|
|
|
|
inline std::default_random_engine& getRandomEngine() {
|
|
static std::default_random_engine RandEngine(std::random_device{}());
|
|
return RandEngine;
|
|
}
|
|
|
|
inline char getRandomChar() {
|
|
std::uniform_int_distribution<> LettersDist(0, LettersSize - 1);
|
|
return Letters[LettersDist(getRandomEngine())];
|
|
}
|
|
|
|
template <class IntT>
|
|
inline IntT getRandomInteger(IntT Min, IntT Max) {
|
|
std::uniform_int_distribution<unsigned long long> dist(Min, Max);
|
|
return static_cast<IntT>(dist(getRandomEngine()));
|
|
}
|
|
|
|
inline std::string getRandomString(std::size_t Len) {
|
|
std::string str(Len, 0);
|
|
std::generate_n(str.begin(), Len, &getRandomChar);
|
|
return str;
|
|
}
|
|
|
|
template <class IntT>
|
|
inline std::vector<IntT> getDuplicateIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> inputs(N, static_cast<IntT>(-1));
|
|
return inputs;
|
|
}
|
|
|
|
template <class IntT>
|
|
inline std::vector<IntT> getSortedIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> inputs;
|
|
inputs.reserve(N);
|
|
for (std::size_t i = 0; i < N; i += 1)
|
|
inputs.push_back(i);
|
|
return inputs;
|
|
}
|
|
|
|
template <class IntT>
|
|
std::vector<IntT> getSortedLargeIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> inputs;
|
|
inputs.reserve(N);
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
inputs.push_back(i + N);
|
|
return inputs;
|
|
}
|
|
|
|
template <class IntT>
|
|
std::vector<IntT> getSortedTopBitsIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> inputs = getSortedIntegerInputs<IntT>(N);
|
|
for (auto& E : inputs)
|
|
E <<= ((sizeof(IntT) / 2) * CHAR_BIT);
|
|
return inputs;
|
|
}
|
|
|
|
template <class IntT>
|
|
inline std::vector<IntT> getReverseSortedIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> inputs;
|
|
inputs.reserve(N);
|
|
std::size_t i = N;
|
|
while (i > 0) {
|
|
--i;
|
|
inputs.push_back(i);
|
|
}
|
|
return inputs;
|
|
}
|
|
|
|
template <class IntT>
|
|
std::vector<IntT> getPipeOrganIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> v;
|
|
v.reserve(N);
|
|
for (std::size_t i = 0; i < N / 2; ++i)
|
|
v.push_back(i);
|
|
for (std::size_t i = N / 2; i < N; ++i)
|
|
v.push_back(N - i);
|
|
return v;
|
|
}
|
|
|
|
template <class IntT>
|
|
std::vector<IntT> getRandomIntegerInputs(std::size_t N) {
|
|
std::vector<IntT> inputs;
|
|
inputs.reserve(N);
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
inputs.push_back(getRandomInteger<IntT>(0, std::numeric_limits<IntT>::max()));
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<std::string> getRandomStringInputsWithLength(std::size_t N, std::size_t len) { // N-by-len
|
|
std::vector<std::string> inputs;
|
|
inputs.reserve(N);
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
inputs.push_back(getRandomString(len));
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<std::string> getDuplicateStringInputs(std::size_t N) {
|
|
std::vector<std::string> inputs(N, getRandomString(1024));
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<std::string> getRandomStringInputs(std::size_t N) {
|
|
return getRandomStringInputsWithLength(N, 1024);
|
|
}
|
|
|
|
template <class IntT>
|
|
std::vector<std::vector<IntT>> getRandomIntegerInputsWithLength(std::size_t N, std::size_t len) { // N-by-len
|
|
std::vector<std::vector<IntT>> inputs;
|
|
inputs.reserve(N);
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
inputs.push_back(getRandomIntegerInputs<IntT>(len));
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<std::string> getPrefixedRandomStringInputs(std::size_t N) {
|
|
std::vector<std::string> inputs;
|
|
inputs.reserve(N);
|
|
constexpr int kSuffixLength = 32;
|
|
const std::string prefix = getRandomString(1024 - kSuffixLength);
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
inputs.push_back(prefix + getRandomString(kSuffixLength));
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<std::string> getSortedStringInputs(std::size_t N) {
|
|
std::vector<std::string> inputs = getRandomStringInputs(N);
|
|
std::sort(inputs.begin(), inputs.end());
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<std::string> getReverseSortedStringInputs(std::size_t N) {
|
|
std::vector<std::string> inputs = getSortedStringInputs(N);
|
|
std::reverse(inputs.begin(), inputs.end());
|
|
return inputs;
|
|
}
|
|
|
|
inline std::vector<const char*> getRandomCStringInputs(std::size_t N) {
|
|
static std::vector<std::string> inputs = getRandomStringInputs(N);
|
|
std::vector<const char*> cinputs;
|
|
for (auto const& str : inputs)
|
|
cinputs.push_back(str.c_str());
|
|
return cinputs;
|
|
}
|
|
|
|
#endif // BENCHMARK_GENERATE_INPUT_H
|