我在努力提高代码速度,偶然发现可以初始化向量,手动增加其容量,然后通过索引将其元素设置为所需的值,而不是常规的push_back()
。这是一个在向量中存储从0到 N 的元素的实例:
std::vector<int> vec;
vec.reserve(N);
for (int = 0; i < N; ++i) {
vec[i] = i; // instead of vec.push_back(i);
}
我发现与使用push_back()
相比,此方法要快得多(在我的计算机上,使用N = 1e9时为1663毫秒,而使用N = 1e9时为3918毫秒)。另一方面,有人告诉我这不是合法代码,会给我留下损坏的向量实例。虽然当我检查时,所有元素都是正确的。
这是死罪吗?还是应该避免仅仅因为访问索引值会产生垃圾,除非事先进行设置?
谢谢!
P.S .:我知道可以用std::vector<int> vec(N)
初始化向量,而不必担心访问无效数据。并不是那么快。
编辑1:
resize()
使其有效。这是我使用的可憎的东西:
#include <chrono>
#include <iostream>
#include <vector>
double measure_vector_without_reserve(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
std::vector<int> vec;
for (int i = 0; i < elements_to_load; ++i) {
vec.push_back(i);
}
vec[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the vector took '" << duration << "' ms.\n";
return double(duration);
}
double measure_vector_with_reserve(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
std::vector<int> vec;
vec.reserve(elements_to_load);
for (int i = 0; i < elements_to_load; ++i) {
vec[i] = i;
}
vec[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the vector after reserving space took '" << duration << "' ms.\n";
return double(duration);
}
double measure_vector_initialized_with_size(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
std::vector<int> vec(elements_to_load);
for (int i = 0; i < elements_to_load; ++i) {
vec[i] = i;
}
vec[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the vector initialized with size '" << duration << "' ms.\n";
return double(duration);
}
double measure_vector_initialized_with_size_and_value(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
std::vector<int> vec(elements_to_load, 0);
for (int i = 0; i < elements_to_load; ++i) {
vec[i] = i;
}
vec[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the vector initialized with size and value took '" << duration << "' ms.\n";
return double(duration);
}
double measure_array_c_style(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
int *arr = new int[elements_to_load];
for (int i = 0; i < elements_to_load; ++i) {
arr[i] = i;
}
arr[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the array took '" << duration << "' ms.\n";
delete arr;
return double(duration);
}
double measure_vector_with_reserve_push_back(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
std::vector<int> vec;
vec.reserve(elements_to_load);
for (int i = 0; i < elements_to_load; ++i) {
vec.push_back(i);
}
vec[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the vector with .push_back() after reserving space took '" << duration << "' ms.\n";
return double(duration);
}
double measure_vector_with_reserve_emplace_back(int elements_to_load)
{
auto t1 = std::chrono::high_resolution_clock::now();
std::vector<int> vec;
vec.reserve(elements_to_load);
for (int i = 0; i < elements_to_load; ++i) {
vec.emplace_back(i);
}
vec[int(elements_to_load / 2)] = 500;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Filling up the vector with .emplace_back() after reserving space took '" << duration << "' ms.\n";
return double(duration);
}
int main(void)
{
int elements_to_load = 1000000000; // 1e9
int iterations = 10;
// Measure multiple times in different order, so boosted CPU clocks play less factor
std::vector<double> results1;
std::vector<double> results2;
std::vector<double> results3;
std::vector<double> results4;
std::vector<double> results5;
std::vector<double> results6;
std::vector<double> results7;
// Play one to warm up the engine
measure_vector_without_reserve(elements_to_load);
for (int i = 0; i != iterations; ++i) {
results1.push_back(measure_vector_without_reserve(elements_to_load));
results2.push_back(measure_vector_with_reserve(elements_to_load));
results3.push_back(measure_vector_initialized_with_size(elements_to_load));
results4.push_back(measure_vector_initialized_with_size_and_value(elements_to_load));
results5.push_back(measure_array_c_style(elements_to_load));
results6.push_back(measure_vector_with_reserve_push_back(elements_to_load));
results7.push_back(measure_vector_with_reserve_emplace_back(elements_to_load));
}
auto avg = [&](std::vector<double> vec) -> double {
double sum = 0;
for (double elem : vec) {
sum += elem;
}
return sum / static_cast<double>(vec.size());
};
std::cout << "'" << elements_to_load << "' elements set to a vector without reserving space took '" << avg(results1)<< "' ms. on average\n";
std::cout << "'" << elements_to_load << "' elements set to a vector with [] after reserving space took '" << avg(results2)<< "' ms. on average\n";
std::cout << "'" << elements_to_load << "' elements set to a vector after initializing with size took '"<< avg(results3) << "' ms. on average\n";
std::cout << "'" << elements_to_load << "' elements set to a vector after initializing with size and value took '"<< avg(results4) << "' ms. on average\n";
std::cout << "'" << elements_to_load << "' elements set to a C style array took '" << avg(results5)<< "' ms. on average\n";
std::cout << "'" << elements_to_load << "' elements set to a vector with .push_back() after reserving space took '" << avg(results6)<< "' ms. on average\n";
std::cout << "'" << elements_to_load << "' elements set to a vector with .emplace_back() after reserving space took '" << avg(results7)<< "' ms. on average\n";
return 0;