5

这个问题谈到了在 C 中无法轻易实现的排序功能的优化: qsort 与 std::sort 的性能?

与 C++ 相比,是否有更多的编译器优化示例在 C 中是不可能或至少难以实现的?

4

3 回答 3

8

正如@sehe 在评论中提到的那样。它与抽象有关。换句话说,如果该语言允许编码人员更好地表达意图,那么它可以发出以更优化的方式实现该意图的代码。

一个简单的例子是std::fill。当然对于基本类型,您可以使用memset,但是,假设它是一个 32 位的数组unsigned longstd::fill 知道数组大小是 32 位的倍数。根据编译器的不同,它甚至可以假设数组在 32 位边界上正确对齐。

所有这些结合起来可能允许编译器发出一次将值设置为 32 位的代码,而无需运行时检查以确保这样做是有效的。如果幸运的话,编译器会识别到这一点,并用一个特别有效的架构特定版本的代码替换它。

(实际上 gcc 和可能其他主流编译器实际上对任何可能被认为等同于 a 的东西都这样做了memset,包括std::fill)。

通常,memset以对这些类型的事物进行运行时检查以选择最佳代码路径的方式实现。虽然这种差异可能可以忽略不计,但我们的想法是我们更好地表达了用特定值“填充”数组的意图,因此编译器能够做出稍微更好的选择。

其他更复杂的语言特性很好地利用意图的表达来获得更大的收益,但这是最简单的例子。

需要明确的是,我的观点不是std::fill“更好” ,而是 C++ 如何允许更好地向编译器表达意图memset的示例,允许它在编译时拥有更多信息,从而使一些优化更容易实现.

于 2012-04-09T21:32:23.160 回答
3

这在一定程度上取决于您认为这里的优化是什么。如果您纯粹将其视为“std::sort vs. qsort”,那么还有数以千计的其他类似优化。在以下情况下,使用 C++ 模板可以支持内联,其中 C 中唯一合理的选择是使用指向函数的指针,并且几乎没有已知的编译器会内联被调用的代码。根据您的观点,这要么是单个优化,要么是它们的整个(开放式)系列。

另一种可能性是使用模板元编程将某些东西转换为通常必须在运行时使用 C 计算的编译时常量。理论上,您通常可以通过嵌入幻数来做到这一点。这可以通过 a #defineinto C 实现,但可能会失去上下文、灵活性或两者兼而有之(例如,在 C++ 中,您可以在编译时定义一个常量,从该输入执行任意计算,并生成其余部分使用的编译时常量代码。鉴于您可以在 a 中执行的计算更加有限#define,因此几乎不可能经常这样做。

还有一种可能性是函数重载和模板特化。这些是分开的,但给出了相同的基本结果:使用专用于特定类型的代码。在 C 中,为了使您处理的函数数量保持合理,您经常最终编写代码(例如)将所有整数转换为 a long,然后对其进行数学运算。模板、模板特化和重载使得使用保持较小类型其本机大小的代码变得相对容易,这可以显着提高速度(特别是当它可以启用数学向量化时)。

最后一个明显的可能性源于简单地提供相当多的预先构建的数据结构和算法,并允许将这些东西打包以便相对容易、有效地重用。我怀疑我什至可以用我知道的相对低效的数据结构和/或算法来计算我用 C 编写代码的次数,仅仅是因为不值得花时间去寻找(或适应)一个更有效的手头的任务。是的,如果它真的成为一个主要瓶颈,我会费心去寻找或编写更好的东西——但是做一些比较,用 C++ 编写时速度翻倍仍然是相当普遍的。

然而,我应该补充一点,所有这些无疑都可以用 C 语言实现,至少在理论上是这样。如果您从语言复杂性理论和计算理论模型(例如图灵机)之类的角度来处理这个问题,那么毫无疑问 C 和 C++ 是等价的。只要有足够的工作为每个函数编写专门的版本,理论上你就可以/可以用 C 做所有这些事情,就像用 C++ 做的一样。

从你可以计划在实际项目中真正编写什么代码的角度来看,故事变化非常快——你能做什么的限制主要取决于你可以合理管理的东西,而不是像所代表的计算理论模型那样由语言。在 C 中几乎完全是理论上的优化级别不仅实用,而且在 C++ 中非常常规。

于 2012-04-09T22:16:35.193 回答
-1

即使是qsortvsstd::sort示例也是无效的。如果需要 C 实现,它可以放置 in 的内联版本qsortstdlib.h并且任何体面的 C 编译器都可以处理内联比较函数。通常不这样做的原因是它非常臃肿并且性能优势令人怀疑——C++ 人往往不关心的问题......

于 2012-04-09T21:56:43.937 回答