有没有一些C代码的例子,在编译为C++代码后会导致(显著的)性能降低/提高?

3
这个问题可能在之前被问过。
有没有一些C代码的例子,在编译为C++代码后会导致(显著的)性能降低/提高?
UPD1:了解性能提升也是很有趣的。因此,添加了“提升”。
UPD2. 可能有用:当在C和C++中编译时,相同的代码是否会产生不同的行为? UPD3. “UnspecB/UB/IB方面”很有趣。然而,更有趣的可能是将C代码限制为严格符合C程序(C11,4p5)的同时,它也是一个不包含违反第5条到第33条和附录D规则(N4917,4.1.1p2.1)的UnspecB/UB/IB-free良好形式的C++程序,并且具有相同的(抽象)语义。

3
C++有时更加严格,C中完全正常的行为在C++中可能是未定义的行为。但我无法想到任何一种情况下行为仍然是定义的,只是变得更慢。 - undefined
6
#if __cplusplus // 很多代码 #endif - undefined
1
代码在每种语言中编译时,是否必须具有相同的功能性? - undefined
2
这可能并不真正符合“相同”的程序,但是在C++中使用std::sort对用户定义类型的数组进行排序很可能比在C中使用qsort要快得多。 - undefined
@Red.Wave,仍然不太支持“将设计良好的C程序修改为设计良好的C++程序...更加安全。”我认为我们的经验有所不同。祝好运。 - undefined
显示剩余10条评论
4个回答

4
考虑函数重载的影响:
long double a = ...; 
long double b = ...; 
long double c = pow(a, b);

在C中,函数是一个`double`类型的函数。在C++中,函数是一个`long double`类型的函数。C语言预计会更快一些(或者不考虑类型转换的成本)。
float a = ...; 
float b = ...; 
float c = pow(a, b);

在C中,函数是一个双精度的。在C++中,函数是一个单精度的。C++预计会更快。
在C语言中,sizeof('a') == sizeof(int)。在C++中,sizeof('a') == sizeof(char)。如果大小影响循环等操作,这肯定会导致性能差异。

回复:pow。在技术上,调用不同类型(float、double、long double)的函数是否会导致不同的程序语义? - undefined
@pmor 或许吧。我经常看到一些简单粗糙的 C 代码使用了 <math.h> 中的 double 函数,而使用 floatlong double 会更合理。很难确定作者的真实意图。 - undefined
是否有任何想法,使用 _Float16(如果支持)是否通常比(32位)float具有更好的性能? - undefined
C被设计成对于整数,存在一种“恰到好处”的类型,通常用于计算,即int/unsigned。对于浮点数类型,double也是类似的情况。由于现代硬件/库通常可以比double更快地执行float,并且比float更快地执行子float,因此希望_Float16能够提供更好的性能,但这取决于具体的实现。_Float16占用的空间肯定更小。我不会指望子float更快,只是确实更小。 - undefined

4
考虑以下代码,虽然它并没有太多意义,但很容易想象出更复杂的代码,它们有意义并且执行类似的操作。
union
{
    long a;
    short b[sizeof(long)/sizeof(short)];
} foo;

short* ptr;

extern void work();

void func()
{
   foo.a = 1;                   // 1
   *ptr = 42;                   // 2
   if (foo.a == 1) work();      // 3
}

假设代码的其余部分没有未定义行为,并且在任何一种语言中都具有相同的含义。
当作为C++编译时,`func`至少在某些实现中会稍微快一些。在线演示。如您所见,当作为C++编译时,它没有条件跳转,而当作为C编译时,它确实有一个。
原因是两种语言中有不同的未定义行为。
在C++中,如果`ptr`指向`foo.b`,程序就会有未定义行为,因为修改一个联合体的一个成员然后检查另一个成员是非法的。因此,编译器假设`ptr`不指向`foo.b`,并且`foo.a`的值在第2行不会改变,因此它仍然具有第1行的值,因此在第3行没有必要检查它。
另一方面,在C语言中,如果ptr指向foo.b,行为是完全(实现)定义的。在第2行,foo.a可能会改变,而编译器无法确定它是否确实发生了改变。因此,在第3行需要进行检查。实际上,foo.a的值不会改变,因为我们知道程序既是有效的C++代码,也是有效的C代码,但编译器无法知道这一点。

如果我们说整个程序是无未定义行为的,那么一个假设的C语言实现可以发现ptr永远不会指向foo.b,并进行相同的优化。你说得对,目前的实现还没有这样做。 - undefined
@Caleth 一般情况下,编译器无法证明这样的事情,即使它拥有关于程序的所有信息(相当于停机问题)。 - undefined
回复:“修改是非法的”,“行为是完全(实现)定义的”。如果有时间,你能指出C和C++标准中相关的段落吗? - undefined
1
@pmor C++ 这里这里; C 这里. - undefined
你的“这里和这里”指向同一个链接。考虑修复一下。 - undefined
@pmor 我忘记它应该指向哪里了 :( 或许等我有时间的时候再看看吧。 - undefined

1
这不是一个真实世界的例子,但这是一个编译后速度几乎是C语言的4倍的程序。
int s = 0;

int main(void) {    
    struct s { char c; };
    volatile int n = 0;
    for (int i = 0; i < (1 << 20) / sizeof(s); ++i) {
        n = i;
    }
}

从这里得到的灵感


2
@pmg:需要使用sizeof (s)来利用struct ss作为类型名称放在与C++中的int s相同的命名空间中的事实。解决方法是更改struct s的定义,而不是更改sizeof - undefined
11
这是C和C++中语义不同的程序示例,而不是相同语义的性能差异 - undefined
4
@Caleth 是的,但从技术上讲,它确实回答了这个问题 :) 不过我同意,这可能不是原帖作者想要的答案。 - undefined
@pmor 样本代码的可观察行为是对volatile int进行一些写操作,C和C++在写操作次数上有所不同,因为它们对sizeof(s)的评估规则不同。从技术上讲,这是否具有定义行为是由实现定义的,因为int i可能无法容纳(1 << 20) / sizeof(s),它只需要至少是16位。 - undefined
我同意,这可能不是原帖作者所想要的。在回答问题之前,你应该通过评论或请求澄清来寻求改进问题,以确保它与原帖作者的意图一致。 - undefined
显示剩余5条评论

0
这在定义上是不可能的。将C编译为C++时,只有三种选择:
1. 有效的C代码在C++中无效,因此你无法获得可运行的代码,因此无法进行比较。 2. 有效的C代码在C++中要么是未定义行为,要么表示不同的含义,因此你会得到一个执行不同操作的可运行代码(通常不是你想要的),因此这两者无法进行比较。 3. 其余(大部分)情况属于“C++是C的子集的超集”,只要你在这个子集范围内,根据定义,你会得到相同的代码。

2
看到我的回答 :) - undefined
@n.m.couldbeanAI 你的例子和我的例子不一样,我的例子是"Valid C is UB C++"。这两段代码不同,因为检查被跳过了。 - undefined
2
在我的例子中,无论是在C还是C++中都没有UB(未定义行为),而且代码的含义完全相同。 - undefined

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