在调试(c ++ MSVC)中提高flatbuffer性能的任何方法

Posted

技术标签:

【中文标题】在调试(c ++ MSVC)中提高flatbuffer性能的任何方法【英文标题】:Any way to improve flatbuffer performance in debug (c++ MSVC) 【发布时间】:2016-03-18 13:10:43 【问题描述】:

我正在公司中尝试使用平面缓冲区来替代原始结构。我们需要序列化的类相当大,我注意到 flatbuffer 序列化的开销超出了我们在运行调试构建时所能承受的范围。

我用以下简单的测试程序复制了我的发现(数据类型类似于我们生产代码中的数据类型):

#include "stdafx.h"
#include <flatbuffers/flatbuffers.h>
#include "footprints_generated.h"
#include <vector>
#include <iostream>
#include <chrono>

using namespace Serialization::Dummy::FakeFootprints;

flatbuffers::FlatBufferBuilder builder;

flatbuffers::Offset<XYZData> GenerateXYZ()

    return CreateXYZData(builder,
        1.0,
        2.0,
        3.0,
        4.0,
        5.0,
        6.0,
        7.0,
        8.0,
        9.0,
        10.0,
        11.0,
        12.0,
        13.0,
        14.0,
        15.0,
        16.0,
        17.0,
        18.0,
        19.0,
        20.0);


flatbuffers::Offset<Fake> GenerateFake()

    std::vector<flatbuffers::Offset<XYZData>> vec;
    for(int i = 0; i < 512; i++)
    
        vec.push_back(GenerateXYZ());
    

    auto XYZVector = builder.CreateVector(vec);

    return CreateFake(builder,
        1.0,
        2.0,
        3.0,
        4.0,
        5.0,
        6.0,
        7.0,
        8.0,
        9.0,
        10.0,
        XYZVector);


int main()

    auto start = std::chrono::steady_clock::now();

    for(auto i = 0; i < 1000; i++)
    
        auto fake = GenerateFake();
    

    auto end = std::chrono::steady_clock::now();
    auto diff = end - start;
    std::cout << std::chrono::duration <double, std::milli>(diff).count() << " ms" << std::endl;

    std::string dummy;
    std::cin >> dummy;

在我的电脑上调试大约需要 40 秒(发布时大约需要 400 毫秒)。 我正在寻找任何方法来提高调试版本的性能。分析表明大部分时间都花在了 std::vector 代码上,所以我尝试将 _ITERATOR_DEBUG_LEVEL 设置为零,但这并没有带来任何显着的性能提升。

【问题讨论】:

【参考方案1】:

我注意到您在向量上使用了push_back(),但我没有看到对reserve() 的调用。因此,您的代码可能会花费大量时间进行堆分配。我建议你在进入调用GenerateXYZ() 的循环之前输入vec.reserve(512)

【讨论】:

感谢您的建议。不幸的是,保留并没有导致性能发生显着变化。 (虽然你当然是对的,防止向量中的重新分配会更快) 在开始执行 push_back() 之前将“vec”声明为全局变量,然后声明 vec.clear()。 是的,众所周知,STL 的调试速度很慢。为什么不声明一个 flatbuffers::Offset vec[512] 呢?无论哪种方式,如果配置文件主要显示 std::vector,这怎么是 FlatBuffers 问题? 大家好,感谢您的回答。我应该对分析结果更清楚:它指向由 flatbuffers 代码调用的 std::vector 操作。只是为了确保我应用了@Aardappel 建议的更改(用全局数组替换了向量),这并没有真正对运行时间产生任何影响。需要明确的是:我并不是说 flatbuffers 存在问题,我只是遇到了这个问题(这对我在生产代码中使用 flatbuffers 来说将是一个交易破坏者)并寻找一种方法来克服它们。跨度> 所以在去掉 std::vector 的使用后调试速度仍然慢了 100 倍?这当然很奇怪。您是否比较了调试和发布的配置文件?您是否与另一个序列化程序(例如 protobuf)进行了比较?【参考方案2】:

再次遇到同样的问题并决定使用编译器设置来查看哪些具有最剧烈的影响。以防其他人偶然发现这篇文章,这就是我发现的:

从与问题中类似的示例应用程序开始。运行时间约为 40 秒。

为任何合适的函数 (/Ob2) 启用内联函数:12.5 秒 pdb (/Zi) 中没有“编辑并继续”:7.6 秒 省略基本运行时检查:4.5 秒 禁用迭代器调试 (_HAS_ITERATOR_DEBUGGING=0):2.2 秒 禁用最小重建 (/Gm-):1.6 秒 启用速度优化 (/O2):400 毫秒

当然,这实际上将配置转换为标准发布配置,但我们能够使用这些选项的子集将 flatbuffer 性能提高到不再是我们应用程序瓶颈的程度。

【讨论】:

事实上,例如迭代器调试有所作为向您展示了很多开销是在使用 std::vector 时。它将花费 99% 的时间在您的 512x for 循环中,并且其中唯一的 FlatBuffers 调用 (GenerateXYZ) 不使用迭代器,并且是相当基本的代码,当然不是我期望的在调试模式下命中 100 倍。的确,与 STL 非常相似,FlatBuffers 在发布模式下针对速度进行了优化,但为了清晰起见,它牺牲了调试速度,因为它使用了大量通常都被内联的函数层。 “迭代器调试产生影响的事实表明,很多开销都在使用 std::vector” 我的测试代码中不再有向量,因为我已经删除了它们在之前的 cmets 之后。我现在改用flatbuffers::Offset&lt;XYZData&gt; vec[512];。据我从分析中得知,大部分时间都浪费在了分层(如你所说)和 TrackField 函数中的 push_backs 上。 以防万一这对您有所帮助,我对此进行了深入研究,发现大部分迭代器检查开销来自 :_Orphan_range ,它是从 push_back() (在 TrackField 中)调用的,并且EndTable 中迭代器的使用。 啊,你是对的,忘记了。嗯,为了调试模式的速度,我想我们可能会将该向量硬编码到一个数组中。 太好了,谢谢。我很想知道这将如何影响我们在调试构建中的负载。

以上是关于在调试(c ++ MSVC)中提高flatbuffer性能的任何方法的主要内容,如果未能解决你的问题,请参考以下文章

调试信息是不是显示 C++/MSVC 中的代码?

flatBuffer安装及使用教程

dll 运行时错误(C/C++/GCC/MSVC)

为什么Android开发者应该使用FlatBuffers替代JSON?

为什么Android开发者应该使用FlatBuffers替代JSON?

Qt下MSVC/Mingw平台dump/crash log报告调试方法差异