我有两个用C ++编写的算法。据我所知,编译时是常规的
-O0 -NDEBUG(g ++)在比较两种算法的性能时(无症状地,它们是相同的)。
但我认为优化级别对其中一个是不公平的,因为它在每种情况下都使用STL。使用普通数组的程序在使用-O0选项编译时,速度比STL重算法快5倍。但是当我使用-O2 -NDEBUG编译它们时,性能差异并没有太大的不同。
有没有办法在优化级别-O0中充分利用STL(我在vector []运算符中获得了很高的性能)?
在比较两种算法时,您使用了哪些优化级别(以及可能的变量,如-NDEBUG)?
如果有人能够对比较C ++编写的算法性能的学术研究趋势有所了解,那将会有很大的帮助吗?
编辑:
好的,为了隔离优化级别的问题,我现在使用一种算法但两种不同的实现。
我用原始指针(int和boolean)将其中一个函数改为std :: vector和std :: vector ...使用-O0 -NDEBUG,性能为5.46s(原始指针)和11.1s(std :: vector) )。使用-O2 -NDEBUG,性能为2.02s(原始指针)和2.21s(std :: vector)。相同的算法,一个实现使用int和boolean的4/5动态数组。另一个是使用std :: vector和std :: vector代替。在其他所有情况下都是一样的
你可以看到-O0 std :: vector的性能比指针快了两倍。虽然在-O2中它们几乎相同。
但我真的很困惑,因为在学术领域,当他们在运行时发布算法结果时,他们用-O0编译程序。
我缺少一些编译器选项吗?
这取决于您想要优化的内容。
速度
我建议使用 -O2 -NDEBUG -ftree-vectorize
,如果您的代码专门设计为在x86或x86_64上运行,请添加 -msse2
。这将为您提供有关GIMPLE如何表现的广泛想法。
尺寸
我相信你应该用 -Os -fno-rtti -fno-exceptions -fomit-frame-pointer
。这将最小化可执行文件的大小到一定程度(假设C ++)。
在这两种情况下,算法的速度都不依赖于编译器,但是如果编译器可以“证明”它可以,则编译器可以大大改变代码的行为方式。
GCC检测“常用”代码,例如手动编码 min()
和 max()
并将它们转换为一条SSE指令(在x86 / x86_64上,当设置了-msse时)或在i686可用时使用cmov(SSE具有更高的优先级)。 GCC还可以自由地重新排序循环,展开和内联函数,如果它想要,甚至删除无用的代码。
至于你最新的编辑:
你可以看到在-O0 std :: vector中
跑得快两倍
指针。虽然在-O2他们几乎
一样。
那是因为 std::vector
仍然有代码抛出异常并可能使用rtti。尝试比较 -O2 -NDEBUG -ftree-vectorize -fno-rtti -fno-exceptions -fomit-frame-pointer
,你会发现std :: vector会比你的代码略胜一筹。 GCC知道什么是“内置”类型以及如何利用它们 在现实世界中使用 并乐意这样做 - 就像它知道什么 memset()
和 memcpy()
当复制尺寸已知时,是否以及如何相应地进行优化。
编译器优化通常不会改变算法的复杂度顺序,只会改变常量和线性比例因子。编译器相当聪明,但它们不是 那 聪明。
你打算用-O0编译你的代码以便发布吗?可能不会。您可以在编译时使用您实际打算使用的任何编译标志来比较算法的性能。
你有两种算法 实施 在C ++中。如果要比较两个实现的相对性能,则应使用将在最终产品中使用的优化级别。对我来说,就是这样 -O3
。
如果您想分析算法的复杂性,那么更多的是分析问题,您可以查看必须针对不同大小和输入特征执行的操作的总体计数。
作为开发人员编写性能问题的代码,最好注意编译器可以并且可能适用于您的代码的优化范围。不优化不公平地惩罚清楚编写的代码,但设计为易于针对已经“微优化”的代码进行优化。
我认为没有理由不在O2编译和运行它们。除非你把它作为纯粹的学术练习(即使你不太可能,优化会对算法的属性产生根本性的改变 - 尽管如此,我想如果GCC开始转向O(N)我会很高兴源自O(lgN)程序集),您将需要的信息与实际运行最终程序时的信息一致。您很可能不会使用O0优化发布程序,因此您不希望在O0优化下比较算法。
这种比较不是关于公平而是产生有用的信息。如果代码投入生产使用,您应该使用计划使用的优化级别。如果你基本上做研究,那么 您 如果没有亲自计划将其投入生产使用,你就会陷入更难以猜测将投入生产的人可能做的事情。
实际上,即使你正在进行开发而不是研究,你仍然会遇到一些问题 - 几乎不可能预测你最终可能会使用这个特定代码的优化级别。
就个人而言,我通常使用-O2和gcc。我的一般经验法则是使用打开自动内联的最低级别的优化。我编写了很多代码,希望编译器能够内联小函数 - 并专门编写代码以帮助完成(例如经常使用函子而不是函数)。如果编译器没有设置为那些内联生成代码,那么你就没有得到我真正想要的东西。当代码编译时,代码的性能并不意味着什么 - 我当然会这样做 不 计划用这种方式真正使用它。