使用大静态数组在Visual C++ 2010中编译时间过长。

10
我们有一个C++项目,其中有几个由预处理工具生成并编译到项目中的大型静态数据表(结构体数组)。我们一直在使用VC++ 2008,但现在准备升级到2010,这些数据表却突然需要很长时间才能编译。例如,其中一个表大约有3,000个条目,每个条目都是一个包含几个整数和指针的结构体,全部是静态初始化的。这个文件在VC++ 2008中编译需要大约15秒,但在VC++ 2010中需要30分钟!作为实验,我尝试将此表平均分成8个表,每个表放在自己的 .cpp 文件中,它们每个都可以在20-30秒内编译。这让我想到编译器内部的某些东西与这些表的长度成平方关系。
cl.exe 的内存使用率在约400 MB左右,我的机器有12 GB的RAM,在它达到这个值之后我没有看到任何I/O活动,所以我认为这不是磁盘缓存问题。有人知道这里可能发生了什么吗?是否有一些编译器功能可以关闭以恢复正常的编译时间?以下是表中数据的示例:
//  cid (0 = 0x0)
{
    OID_cid,
    OTYP_Cid,
    0 | FOPTI_GetFn,
    NULL,
    0,
    NULL,
    (PFNGET_VOID) static_cast<PFNGET_CID>(&CBasic::Cid),
    NULL,
    CID_Basic,
    "cid",
    OID_Identity,
    0,
    NULL,
},

//  IS_DERIVED_FROM (1 = 0x1)
{
    OID_IS_DERIVED_FROM,
    OTYP_Bool,
    0 | FOPTI_Fn,
    COptThunkMgr::ThunkOptBasicIS_DERIVED_FROM,
    false,
    NULL,
    NULL,
    NULL,
    CID_Basic,
    "IS_DERIVED_FROM",
    OID_Nil,
    0,
    &COptionInfoMgr::s_aFnsig[0],
},

//  FIRE_TRIGGER_EVENT (2 = 0x2)
{
    OID_FIRE_TRIGGER_EVENT,
    OTYP_Void,
    0 | FOPTI_Fn,
    COptThunkMgr::ThunkOptBasicFIRE_TRIGGER_EVENT,
    false,
    NULL,
    NULL,
    NULL,
    CID_Basic,
    "FIRE_TRIGGER_EVENT",
    OID_Nil,
    0,
    NULL,
},

//  FIRE_UNTRIGGER_EVENT (3 = 0x3)
{
    OID_FIRE_UNTRIGGER_EVENT,
    OTYP_Void,
    0 | FOPTI_Fn,
    COptThunkMgr::ThunkOptBasicFIRE_UNTRIGGER_EVENT,
    false,
    NULL,
    NULL,
    NULL,
    CID_Basic,
    "FIRE_UNTRIGGER_EVENT",
    OID_Nil,
    0,
    NULL,
},

正如您所看到的,它包含各种整数和枚举类型,以及一些文字字符串、函数指针和其他静态数据表中的指针。


作为一种解决方法,您可以尝试我很久以前写的实现无数组的数组的hack:http://bytes.com/topic/c/answers/527211-code-puzzle-implementing-arrays-without-arrays-heap - Joseph Garvin
你确定这段时间是用于实际编译、链接还是两者之间分配的吗? - Michael Price
2
谢谢@JosephGarvin的建议,但我非常确定将模板嵌套3,000层不会改善我们的编译时间。 :) - Nathan Reed
@MichaelPrice,只是编译这些大型数据表格时花费的时间非常长,而不是实际代码的普通.cpp文件。在VS2010中链接我们的项目并没有比以前更糟糕。 - Nathan Reed
1
提交带有投诉的文件至 http://connect.microsoft.com/。 - Mooing Duck
显示剩余8条评论
5个回答

6

也许值得关闭文件上的所有优化(反正这并不会给你带来任何好处),以防它正是优化器导致了N^2性能问题。


我简直不敢相信我之前没有尝试过这个,但是关闭优化后,编译时间又降到了约15秒。谢谢! - Nathan Reed

5

我曾遇到过同样的问题。有一个包含大约40'000个元素的const数组。编译时间约为15秒。当我从“const uint8_t pData[] = {...}”更改为“static const uint8_t pData[] = {...}”时,编译时间缩短至不到1秒。


这正是我所需要的!编译时间的差异就像白天和黑夜一样:D - Marat Isaw

1
你可以尝试在C/C++设置中关闭Pure MISL CLR支持。这对我有用。

1

我曾经看到过(不记得在哪里了)一种将大型静态数据直接转换为目标文件的技术。然后你的 C++ 代码将数组声明为 extern,链接器会将两者匹配起来。这样,数组数据就根本不需要经历编译步骤。

微软的 C/C++ 工具 CVTRES.exe 基于类似的原理,但它没有生成符号,而是生成了一个需要特殊 API 访问的单独资源部分(FindResourceLoadResourceLockResource)。

啊哈,这是我记得找到的其中一个工具:bin2coff 作者还有一堆相关工具


另外,你可以尝试减少依赖关系,这样特定的源文件就不需要重新编译了。然后,最小重建将自动使用现有的.obj文件。甚至可以将.obj文件检入源代码控制系统中。

1
一种技巧可能是将其移动到自己的项目中(静态库),仅在更改时重新构建它。 - Mooing Duck
如果文件没有改变,它们就不会被重建。问题是,我们实际上经常更改其中的数据。 :/ - Nathan Reed

0
尝试将您的数组设置为静态常量,这样可以减少编译时间(但不是文件大小),我在一个类似的情况下看到过这样做可以将编译时间降至可以忽略不计。

它已经声明为常量。它不能是静态的,因为它需要对项目的其他部分可见(外部链接)。 - Nathan Reed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接