我应该避免在初始化元素然后增加其容量后在std :: vector中通过索引设置元素吗?

时间:2020-05-14 19:07:37

标签: c++ vector

我在努力提高代码速度,偶然发现可以初始化向量,手动增加其容量,然后通过索引将其元素设置为所需的值,而不是常规的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:

  1. 正如评论中所建议的那样,我试图存储字符串,但正如人们期望的那样,它引发了错误。
  2. 然后,我也尝试使用整数,但这一次是在调试时,这还会引发错误!因此,似乎将发布模式(Visual Studi Community 2019)与整数一起使用不会触发错误。
  3. 另一个评论者是对的,大小保持为0(通过使用释放模式和整数容器打印大小来检查)。
  4. 根据另一个评论者的说法,使用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;

0 个答案:

没有答案
相关问题