【发布时间】:2011-09-03 22:19:44
【问题描述】:
我们一直在一家银行开发一个大型金融应用程序。它开始是 150k 行非常糟糕的代码。到 1 个月前,它下降到一半多一点,但可执行文件的大小仍然很大。我希望我们只是让代码更具可读性,但模板化代码仍在生成大量目标代码,我们只是在努力提高效率。
应用程序分为大约 5 个共享对象和一个主对象。更大的共享对象之一是 40Mb,即使代码缩减,也增长到 50。
代码开始增长我并不完全感到惊讶,因为毕竟我们正在添加一些功能。但令我惊讶的是它增长了 20%。当然,没有人接近编写 20% 的代码,所以我很难想象它是如何增长这么多的。那个模块对我来说有点难以分析,但在星期五,我有一个新的数据点,可以提供一些启示。
SOAP 服务器可能有 10 个提要。代码是自动生成的,很糟糕。每个服务都有一个解析器类,其代码完全相同,例如:
#include <boost/shared_ptr.hpp>
#include <xercesstuff...>
class ParserService1 {
public:
void parse() {
try {
Service1ContentHandler*p = new Service1ContentHandler( ... );
parser->setContentHandler(p);
parser->parser();
} catch (SAX ...) {
...
}
}
};
这些类是完全没有必要的,一个函数就可以工作。每个 ContentHandler 类都是使用相同的 7 或 8 个变量自动生成的,我可以通过继承共享这些变量。
因此,当我从代码中删除解析器类和所有内容时,我期望代码的大小会下降。但是只有 10 个服务,我没想到它会从 38Mb 下降到 36Mb。符号数量多得离谱。
我唯一能想到的是每个解析器都包含 boost::shared_ptr、一些 Xerces 解析器的东西,而且不知何故,编译器和链接器为每个文件重复存储所有这些符号。无论如何,我很想知道。
那么,谁能建议我如何去追查为什么像这样的简单修改会产生如此大的影响?我可以在模块上使用 nm 来查看里面的符号,但这会产生令人痛苦的、大量半可读的东西。
此外,当一位同事使用我的新库运行她的代码时,用户时间从 1 分 55 秒变为 1 分 25 秒。实时是高度可变的,因为我们正在等待缓慢的 SOAP 服务器(恕我直言,SOAP 是 CORBA 的一个非常糟糕的替代品......),但 CPU 时间相当稳定。我本来希望减少这么多代码大小会带来轻微的提升,但最重要的是,在具有大量内存的服务器上,考虑到我没有改变架构,我真的很惊讶速度受到如此大的影响XML 处理本身。
我将在周二更进一步,希望能获得更多信息,但如果有人知道我如何才能取得如此大的进步,我很想知道。
更新: 我验证了事实上,在任务中使用调试符号似乎根本不会改变运行时间。我通过创建一个包含很多东西的头文件来做到这一点,包括在这里产生影响的两个:boost shared pointers 和一些 xerces XML 解析器。似乎没有运行时性能受到影响(我检查过,因为两个答案之间存在意见分歧)。但是,我还验证了包含头文件会为每个实例创建调试符号,即使剥离的二进制大小没有改变。因此,如果您包含给定文件,即使您甚至不使用它,也会有固定数量的符号反对该对象,即使它们可能相同,它们在链接时也不会折叠在一起。
我的代码如下:
#include "includetorture.h"
void f1()
{
f2(); // call the function in the next file
}
我的特定包含文件的大小约为每个源文件 100k。据推测,如果我包含更多,它会更高。包含的总可执行文件约为 600k,没有大约 9k。我验证了增长与包含文件的数量成线性关系,但剥离后的代码大小是相同的,应该是这样。
显然,我错误地认为这是性能提升的原因。我想我现在已经考虑到了。尽管我没有删除太多代码,但我确实简化了很多大的 xml 字符串处理,并且大大减少了通过代码的路径,这大概就是原因。
【问题讨论】:
-
在你的标题中你提到了调试符号,但你没有在你的帖子的其余部分。我错过了什么吗?
-
@Bart 膨胀是因为可执行文件中的所有调试符号。如果剥离库,代码大约是大小的 10%。
标签: c++ performance debugging optimization symbols