【问题标题】:C++ std::vector inserting two elements alternative algorithm failsC++ std::vector 插入两个元素替代算法失败
【发布时间】:2019-12-03 13:02:05
【问题描述】:

在我的项目中,我需要将两个元素插入到两个索引中。我正在实现一个替代实现而不是向量插入,因为两个插入调用移位向量元素两次,我可以用一个移位来做同样的事情。但是,替代方案要慢得多。这种行为的解释是什么?

#include <vector>
#include <chrono>
#include <iostream>

void insert2(std::vector<int>& items, size_t first, size_t last, int item = -1) {
    // assert(last < items.size() + 2);
    // assert(first < last);
    // assert(0 <= first);
    // Creating two temporary objects
    // items.reserve(std::max(items.capacity(), items.size() + 2));
    items.emplace_back(); items.emplace_back();

    // Moving elements from the back to last
    for(auto p = items.end() - 1, q = items.begin() + last; p != q; --p) {
        // *p = std::move(*(p - 2));
        *p = *(p - 2);
    }
    // Emplace at last
    // new(&items[last]) ...
    items[last] = item;
    // Moving elements from last to first
    for(auto p = items.begin() + last - 1, q = items.begin() + first; p != q; --p) {
        // *p = std::move(*(p - 1));
        *p = *(p - 1);
    }
    // Emplace at first
    // new(&items[first]) ...
    items[first] = item;
}

auto now() {
    return std::chrono::steady_clock::now();
}

int main() {
    const size_t N = 100;
    const size_t M = 100;
    auto begin = now();

    begin = now();
    for(size_t n = 0; n < N; n++) { // run the same N times
        for(size_t i = 0; i < M + 1; i++) {
            for(size_t j = i + 1; j < M + 2; j++) {
                std::vector<int> v(M);
                insert2(v, i, j);
            }
        }
    }
    std::cout << "insert2 " << std::chrono::duration_cast<std::chrono::nanoseconds>(now() - begin).count() / (1000.0 * N) << "us\n";

    begin = now();
    for(size_t n = 0; n < N; n++) { // run the same N times 
        for(size_t i = 0; i < M + 1; i++) {
            for(size_t j = i + 1; j < M + 2; j++) {
                std::vector<int> v(M);
                v.insert(v.begin() + i, -1);
                v.insert(v.begin() + j, -1);
            }
        }
    }
    std::cout << "insert1 " << std::chrono::duration_cast<std::chrono::nanoseconds>(now() - begin).count() / (1000.0 * N) << "us\n";
}

我的 Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz 输出,带 O0

insert2 7941.29us
insert1 4005.15us

与 O3,

insert2 763.64us
insert1 688.365us

Live demo on quick-bench

【问题讨论】:

  • 要了解我们需要阅读您的代码的实际问题,并且使用奇怪的宏对可读性没有帮助。当我看到DODO(N) 并且必须搜索您的代码以了解它的含义时,这不是对风格的挑剔,而是真正的斗争,仅此而已......
  • 我添加了live demo
  • @DanielLangr 不确定我这样做是否正确,但 it is a bit faster。需要进行单元测试来验证这一点。
  • @MarekR 我还用你的基准和一个更大的向量加上避免重新分配做了一个实验,然后是你的自定义版本gets faster。此外,我创建了一个特殊的基准来完全避免分配和自定义版本are faster as well there

标签: c++ performance memory-management stl stdvector


【解决方案1】:

我使用了@MarekR 创建的基准并对其进行了修改,使得基准循环内不会发生(重新)分配,请参阅http://quick-bench.com/UX9aEcrP06xBe51qKX3LjZWMU38。然后我只对向量大小的 1/3 和 2/3 做了一次双插入。对于 100 个整数元素的向量(常量 N),自定义版本实际上速度较慢,但​​是对于 1000 个元素,它已经更快了。而且,对于 1M 元素,自定义版本几乎快了 1.5 倍,这与备用元素的数量相对应“moves”。使用std::vector::insert,您需要移动N 元素,而使用自定义版本只需N * 2 / 3

说实话,我仍然不知道为什么自定义版本对于小矢量来说速度较慢。无论如何,我想你可能也对这个答案感兴趣。

【讨论】:

  • 谢谢。虽然和我的用例有点不同。
猜你喜欢
  • 2021-04-08
  • 1970-01-01
  • 2016-02-04
  • 1970-01-01
  • 2020-07-14
  • 2011-06-22
  • 2016-09-21
  • 2012-06-20
  • 1970-01-01
相关资源
最近更新 更多