【问题标题】:VS2012 "Generating Code" slow with large hardcoded arraysVS2012“生成代码”速度慢,带有大型硬编码数组
【发布时间】:2015-09-21 08:46:01
【问题描述】:

我们有一个工具可以在头文件中生成一个类,该文件由硬编码数组生成。该自动生成由使用自动生成值的实际实现继承。

自动生成的例子:

class MyTestAutoGen
{
    std::vector<int> m_my_parameter1;
    std::vector<int> m_my_parameter2;
    ...

public:
    MyTestAutoGen()    
    {
        SetDefaultValueFor_my_parameter1();
        SetDefaultValueFor_my_parameter2();
        ...
    }

    void SetDefaultValueFor_my_parameter1()
    {
        int tmp[] = {121,221,333,411,225,556,227,.......};
        m_my_parameter1.assign(tmp, tmp + 65025);
    }

    void SetDefaultValueFor_my_parameter2()
    {
        int tmp[] = {333,444,333,987,327,16728,227,.......};
        m_my_parameter2.assign(tmp, tmp + 65025);
    }

    ...
 };

编译这需要很多时间,在 VS 的输出窗口中,我可以看到它挂在编译的“生成代码”阶段,但它会在大约 15-30 分钟后完成编译,除非编译器崩溃堆栈溢出。

我尝试启用“多处理编译”和“并行代码生成”标志,但没有显示任何改进。禁用“整个程序优化”不是一个选项,因为在应用程序初始化后它应该尽可能优化。

我对这个问题的解决方法是修改自动生成的模板以将值保存在编码的二进制字符串中,以便数据可能存储在 TEXT 区域而不是库/可执行文件的 STATIC 区域。所以现在自动生成的代码看起来像(字符串的十六进制值只是为了显示):

class MyTestAutoGen
{
    std::vector<int> m_my_parameter1;
    std::vector<int> m_my_parameter2;
    ...

public:
    MyTestAutoGen()    
    {
        SetDefaultValueFor_my_parameter1();
        SetDefaultValueFor_my_parameter2();
        ...
    }

    void SetDefaultValueFor_my_parameter1()
    {
        std::string codedDefaultValue = "\x079\0\0\0.......";
        std::stringstream str(codedDefaultValue);

        int tmp;
        for (int i = 0; i < codedDefaultValue.length() / sizeof(int); i++)
        {
            str.read((char*) &tmp, sizeof(int));
            m_my_parameter1.push_back(tmp);
        }
    }

    void SetDefaultValueFor_my_parameter2()
    {
        std::string codedDefaultValue = "\x04d\x001.......";
        std::stringstream str(codedDefaultValue);

        int tmp;
        for (int i = 0; i < codedDefaultValue.length() / sizeof(int); i++)
        {
            str.read((char*) &tmp, sizeof(int));
            m_my_parameter2.push_back(tmp);
        }
    }

    ...
 };

这个编译速度很快,但不容易阅读(这个文件不应该手动编辑,也不要随意查看)。

有没有更好的方法来做到这一点而不破坏它作为跨平台,不禁用优化并将其保留为头文件?

【问题讨论】:

  • 为什么不能禁用 LTO?我认为如果开发可执行文件不那么快也没关系,在开发阶段快速编译更重要。您仍然可以在产品构建中使用 LTO。
  • 其他开发人员正在使用这个 autogen 文件,他们需要在运行时测试他们的“重”实现,所以对他们来说它既是开发又是生产。创建 2 个构建配置(有和没有)对于所有相关方来说都很麻烦。
  • 为什么一定要放在头文件里?您不能自动生成包含SetDefaultValueFor_my_parameter1 定义的 CPP 文件吗?
  • @TripeHound 我在一个文件中完成它以简化外部集成。我尝试手动将代码提取到 cpp 文件并将其添加到项目中,但它对速度没有任何影响
  • "我们有一个在头文件中生成类的工具" 您可以改用直接生成目标文件的工具。

标签: c++ arrays performance visual-studio-2012


【解决方案1】:

您的自动生成的数字似乎是恒定的。这由static const表示:

static const int tmp[] = {121,221,333,411,225,556,227,.......};

有一个模糊的规则,对于“大”数组,不应该使用自动存储(即普通的局部变量);改用static(或者,使用动态分配,但这里不需要)。此外,由于您的号码不会更改,请也使用const

顺便说一句,打破这个“规则”会影响编译时间,这真是令人惊讶!

【讨论】:

  • 你能解释一下为什么会这样吗?
【解决方案2】:

如果您必须保留解决方法:为了提高可读性,您可能需要提取解码字符串的函数。

void fill(std::vector<int>& dest, const std::string& src)
{
  std::stringstream str(src);
  const size_t size = src.length() / sizeof(int);
  int tmp;
  dest.clear();
  dest.reserve(size);
  for (int i = 0; i < size; i++)
  {
    str.read((char*) &tmp, sizeof(int));
    dest.push_back(tmp);
  }
}

void SetDefaultValueFor_my_parameter1()
{
  fill(m_my_parameter1, "\x079\0\0\0.......");
}

【讨论】:

    【解决方案3】:

    我的直觉是,您正在堆栈中推送大量未指定大小的数组。因为堆栈缓冲区溢出是一个特别的安全问题。 MSVC 对他们很小心。但是将数组放在堆栈上是没有意义的。

    相反,在全局范围内执行

    const int count = 65025;
    int param1_initializer[count] = {121,221,333,411,225,556,227, ... }
    

    请注意,您已经对数组长度进行了硬编码,因此强烈建议让编译器有机会检查它。此外,这使编译器更容易知道需要多少个初始化器,因此编译时间也可能会受益。

    [编辑] 由于这还不够,而且我们知道我们正在使用 MSVC,请使用 #pragma optimize 关闭数组定义的所有优化。反正也不是可以优化的。

    【讨论】:

    • 感谢您指出。我已尝试修改代码生成器以按照您的建议进行生成,但编译时间仍然很长,尽管它确实比以前花费更少。
    • 我已经将所有的 SetDefaulrValueFor 函数实现都包含在 on 和 off 的编译指示中,但是编译仍然非常缓慢(超过 4 分钟并且还在计数)。
    猜你喜欢
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    • 2017-08-13
    • 2015-09-04
    相关资源
    最近更新 更多