【问题标题】:Boost MultiIndexContainer, how to do delayed indexing for fast insertion?Boost MultiIndexContainer,如何为快速插入做延迟索引?
【发布时间】:2015-04-15 20:16:35
【问题描述】:

我在MultiIndexContainer下面用过

typedef multi_index_container<PositionSummary*,
        indexed_by<
                ordered_unique<
                        composite_key<PositionSummary, const_mem_fun<PositionSummary, int, &PositionSummary::positiondate>,
                                const_mem_fun<PositionSummary, const std::string&, &PositionSummary::accountid>,
                                const_mem_fun<PositionSummary, const std::string&, &PositionSummary::instid> > >,
                ordered_unique<
                        composite_key<PositionSummary, const_mem_fun<PositionSummary, int, &PositionSummary::positiondate>,
                                const_mem_fun<PositionSummary, const std::string&, &PositionSummary::instid>,
                                const_mem_fun<PositionSummary, const std::string&, &PositionSummary::accountid> > > 
                    > > PositionSummaryContainer;

我插入了 10000 个实例*36 个帐户*100 天=3600 万条记录

//Begin testing of the multiIndexContainter
std::cout << "Begin inserting data from array into the multiIndexContainter" << std::endl;
timer.reset();
timer.begin();
for (int i = 0; i < numOfDays_; i++)
{
    for (int j = 0; j < accountSize_; j++)
    {
        for (int k = 0; k < instSize_; k++)
        {
            PositionSummary* ps = psArray_[(i * accountSize_ + j) * instSize_ + k];
            uniqueIndex.insert(ps);
        }
    }

}

printMemoryUsage();
timer.end();
std::cout << "Time take is " << timer.getInterval() << std::endl;

我发现插入速度有点慢,大约每秒 20K+ 记录... 有没有办法提高这个插入速度? 我的数据在 Oracle 中,已正确编入索引,因此不存在数据结构损坏的危险。我知道在 oracle 中您可以先加载然后构建索引以节省时间,如果有办法,我可以对 MultiIndexContainer 做同样的事情吗? 顺便说一下,并行查询速度还算满意,在4 cpu(8kernal)的机器上查询全部36m的记录只需要2.8秒,代码如下

#pragma omp parallel for collapse(2)
    for (int i = 0; i < numOfDays_; i++)
    {

        for (int j = 0; j < accountSize_; j++)
        {
            const int& date = dates_[i];
            const std::string& accountID = accountIDs_[j];
            for (int k = 0; k < instSize_; k++)
            {
                const std::string& instID = instIDs_[i];
                PositionSummaryContainer::iterator it = uniqueIndex.find(boost::make_tuple(date, accountID, instID));
                if (it != uniqueIndex.end())
                {
#pragma omp atomic
                    sum2 += (*it)->marketvalue();
                }
            }
            //std::cout << "accountID: " << accountID << std::endl;
        }

    }

【问题讨论】:

  • 如果psArray_ 已经被适当组织(也许它甚至是内存映射的?)你也许可以直接查询它

标签: c++ performance boost


【解决方案1】:

啊,我想我找到了钥匙。就这么简单。

psContainer_.insert(&psArray_[0], &psArray_[accountSize_ * instSize_ * numOfDays_]);

现在插入速度从 53 秒降低到 5.29 秒。十倍提升。我认为 boost 作者知道他们使用这种方式进行了一些批量插入,只是我没有注意到。

Boost 是一个很棒的库,但是文档不是那么详尽。

【讨论】:

    【解决方案2】:

    据我所知,这不是一项功能。
    (Is using a map where value is std::shared_ptr a good design choice for having multi-indexed lists of classes?)

    • 我必须说,拥有两个等效的复合索引似乎有点代码味道。在我看来,您应该拥有更少或更多独立的索引。
    • 通过指针存储PositionSummary 也可能是一种气味;它很容易杀死内存位置
    • 使用显示的使用模式,使用多索引容器是多余的。你可以(应该)只使用例如boost::flat_set(基本上是您的使用示例中使用的主索引的vector&lt;PositionSummary&gt; 顺序)。见

    • 您可以在上述任何场景中使用持久容器(使用 Boost Interprocess managed_shared_memory/managed_mapped_file 段分配器。有关示例,请参阅我的许多答案)。这将完全/很大程度上消除加载时间

    • 你可以使用#pragma omp parallel for reduction(+:sum2),这将大大减少sum2变量的缓存失效

    • 看起来很像您在两个正交的维度(日期/帐户)上即时合并度量。这应该让您想起“数据透视表”,从而想起 OLAP(多维数据集)报告数据库。

      您可以将批量数据导出到 OLAP 分析/报告引擎(Business Objects (ROLAP)、MS 分析服务器 (OLAP)、Oracle Essbase¹),而不是“导出”大量数据到您的专有 C++ 应用程序中,并使用它们经过优化的报告和(预)整合功能。

      有一种专用的查询语言:MDX for OLAP databases,您可以像使用它一样使用它,例如C++ 中的 OCI


    ¹ 前 Arbor Essbase -> Hyperion Essbase -> Oracle Essbase

    【讨论】:

    • 我已经通过一些越来越少的横向想法大大扩展了答案
    • 感谢您的详尽回答。实际项目比这段代码复杂得多,这段代码应该能说明问题。但我正在做多维查询和聚合(可能非常复杂,可能需要位置摘要中没有的数据)。
    • 好的,很好。我仍然对重叠索引感到有些惊讶(您会意识到这是优化插入时间的主要领域)。内存映射二进制文件似乎是摆脱加载时间的最佳选择,然后(example)。它仍然让我想起太多的“NIH 综合症报告应用程序”。 OLAP 确实非常强大,但经常被开发人员低估。
    • 我多年前开发了Essbase Applications,用于石油化工行业的财务会计和财务管理,但目前的案例是金融行业的金融工程领域,聚合逻辑可能非常复杂,例如对于债券期货期权,是 Deltaholding合约规模*基础市场价值,并且对于每种工具类型都会有所不同。而且数据量不大,速度要求很重要。
    • @Michael 我们做了很多类似的事情 :) Anyhoops,很高兴知道你在正确的轨道上。祝你好运。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多