提升multi_index_container并减慢operator ++

时间:2014-12-21 09:03:15

标签: c++ boost stl boost-multi-index boost-range

这是this MIC question的后续问题。将项添加到引用包装器的向量时,我花费大约80%的时间在++运算符中,无论我选择何种迭代方法 查询的工作原理如下

VersionView getVersionData(int subdeliveryGroupId, int retargetingId,
                             const std::wstring &flightName) const {
    VersionView versions;
    for (auto i = 0; i < 3; ++i) {
      for (auto j = 0; j < 3; ++j) {
        versions.insert(m_data.get<mvKey>().equal_range(boost::make_tuple(subdeliveryGroupId + i, retargetingId + j,
                                 flightName)));
      }
    }
    return versions;
  }

我尝试过以下方法来填充参考包装

template <typename InputRange> void insert(const InputRange &rng) {
    // 1)   base::insert(end(), rng.first, rng.second); // 12ms
    // 2)   std::copy(rng.first, rng.second, std::back_inserter(*this)); // 6ms
    /* 3)   size_t start = size();  // 12ms
                    auto tmp = std::reference_wrapper<const
       VersionData>(VersionData(0,0,L""));
                    resize(start + boost::size(rng), tmp);
                    auto beg = rng.first;
                    for (;beg != rng.second; ++beg, ++start)
                    {
                         this->operator[](start) = std::reference_wrapper<const VersionData>(*beg);
                    }
    */
    std::copy(rng.first, rng.second, std::back_inserter(*this));
  }

无论我做什么,我为operator ++或者只是增加迭代器的大小方法付费 - 这意味着我仍然坚持使用++。所以问题是,是否有办法更快地迭代结果范围。如果没有这样的方法是否值得尝试并继续执行equal_range添加新参数,该参数保存对reference_wrapper的容器的引用,该容器将填充结果而不是创建范围?

编辑1:示例代码 http://coliru.stacked-crooked.com/a/8b82857d302e4a06/
由于this bug,它不会在Coliru上编译 编辑2:调用树,在操作符上花费时间++ Calltree Hotpath 编辑3:一些具体的东西。首先,我没有启动这个线程只是因为operator ++在总体执行时间上花了太多时间而我不喜欢它只是“因为”但是在这个时刻它是我们性能测试的主要瓶颈。每个请求通常在几百微秒内处理,请求类似于这个(它们稍微复杂一点)处理~1000-1500微,它仍然是可以接受的。最初的问题是,一旦数据结构中的项目数量增加到数十万,性能就会恶化到20毫秒。现在切换到MIC(这大大提高了代码的可读性,可维护性和整体优雅性)后,我可以达到每个请求13毫秒,其中80%-90%花费在operator ++上。现在问题是否可以以某种方式改善或者我应该为我寻找一些焦油和羽毛? :)

4 个答案:

答案 0 :(得分:1)

getVersionData中花费operator++执行时间的80%这一事实并不表示任何性能问题本身 - 大多数情况下,它会告诉您equal_range和相比之下,std::reference_wrapper插入速度更快。换句话说,当您分析一些代码时,您通常会找到花费最多时间的位置,但这是否有问题取决于所需的整体性能。

答案 1 :(得分:1)

@kreuzerkrieg,您的示例代码不会对vector std::reference_wrapper进行任何类型的插入!相反,您将equal_range的结果投射到boost::any_range中,预计在迭代时速度相当慢 - 基本上,增量操作会解析为虚拟调用。

所以,除非我在这里严重遗漏了某些内容,否则示例代码的性能或缺乏性能与实际代码中的任何问题无关(假设为VersionView,其中您未显示代码,没有使用boost::any_range)。

那就是说,如果你能负担得起用等效的散列索引替换你的有序索引,迭代可能会更快,但是如果你没有显示真实的东西,那么这是一个彻头彻尾的镜头。

答案 2 :(得分:1)

我认为你完全在测量错误的东西。当我从3x3x11111扩展到10x10x111111(因此索引中的项目数量为111x)时,它仍然会在290ms内运行。

填充这些东西需要更多时间。即使取消分配容器似乎也需要更多时间。

什么不重要?

我提供了一些权衡版本,这主要表明在调整内容方面没有任何意义: View On Coliru

  • 有一个开关可以避开any_range(如果你关心性能,那就没用了)
  • 有一个调整轻量级的开关:

    #define USE_FLYWEIGHT 0 // 0: none 1: full 2: no tracking 3: no tracking no locking
    

    再一次,它只是表明你可以很容易地没有,并且应该考虑这样做,除非你需要字符串的内存优化(?)。如果是,请考虑使用OPTIMIZE_ATOMS方法:

  • OPTIMIZE_ATOMS基本上会为那里的wstring飞行重量。由于所有字符串都在这里重复,因此它将具有强大的存储效率(尽管实现快速而且脏,应该进行改进)。这个想法在这里应用得更好:How to improve performance of boost interval_map lookups

这里有一些基本的时间:

enter image description here

正如您所看到的,基本上没有什么对查询/迭代性能真正重要

任何迭代器:它们是否重要?

它可能是编译器的罪魁祸首。在我的编译(gcc 4.8.2)中它没有什么大的,但是看看累积循环 的反汇编没有 任何迭代器:

enter image description here

从我突出显示的部分可以看出,算法,lambda和迭代器遍历似乎并不多。现在的any_iterator情况不太清楚,如果你的编译优化得不太好,我可以想象它没有内联基本操作使得迭代变慢。 (现在猜一点)

答案 3 :(得分:0)

好的,我应用的解决方案如下: 除了odered_non_unique索引(&#39; byKey&#39;)之外,我还添加了random_access索引。加载数据后,我用m_data.get.begin()重新排列随机索引。然后,当查询MIC以获取数据时,我只使用自定义谓词在随机索引上执行boost :: equal_range,该谓词模拟在&#39; byKey&#39;的排序中应用的相同逻辑。指数。就是这样,它给了我快速&#39; size()&#39; (据我所知,O(1))和快速遍历。 现在我准备好你的烂西红柿了:))

编辑1: 当然,我已经将any_range从双向遍历标记更改为随机访问标记