Return C 中的值优化和复制省略
Return value optimization and copy elision in C
有些人不知道它是 possible to pass and return structs by value in C. My question is about the compiler making unnecessary copies when returning structs in C. Do C compilers such as GCC use Return value optimization(RVO) 优化,或者这只是一个 C++ 概念?我读到的关于 RVO 和复制省略的所有内容都是关于 C++ 的。
让我们考虑一个例子。
我目前正在 C 中实现 double-double data type (或者更确切地说是 float-float 开始,因为我发现它很容易进行单元测试)。考虑以下代码。
typedef struct {
float hi;
float lo;
} doublefloat;
doublefloat quick_two_sum(float a, float b) {
float s = a + b;
float e = b - (s - a);
return (doublefloat){s, e};
}
编译器会制作 doublefloat
值 I return 的临时副本还是可以删除临时副本?
C 中的命名 return 值优化 (NRVO) 怎么样?我还有一个功能
doublefloat df64_add(doublefloat a, doublefloat b) {
doublefloat s, t;
s = two_sum(a.hi, b.hi);
t = two_sum(a.lo, b.lo);
s.lo += t.hi;
s = quick_two_sum(s.hi, s.lo);
s.lo += t.lo;
s = quick_two_sum(s.hi, s.lo);
return s;
}
在这种情况下,我return正在命名结构。这种情况下的临时副本可以省略吗?
应该指出,这是 C 的一般问题,我在这里使用的代码示例只是示例(当我优化它时,无论如何我将使用带有内在函数的 SIMD)。我知道我可以查看汇编输出以查看编译器的功能,但我认为这是一个有趣的问题。
它在 C++ 中被大量提及的原因是因为在 C++ 中,RVO 有副作用(即不调用临时对象的析构函数,也不调用结果对象的复制构造函数或赋值运算符)。
在 C 中,没有可能的副作用,只有潜在的性能改进。我认为某些编译器没有理由不能执行这种优化。至少,标准里没有禁止的。
无论如何,优化取决于编译器和优化级别,所以我不会在关键代码路径上打赌,除非使用的编译器定义明确并且预计不会更改(这种情况仍然经常发生)。
RVO/NRVO 在 C 中的 "as-if" 规则下是明确允许的。
在 C++ 中,您可以获得可观察到的副作用,因为您重载了构造函数、析构函数、and/or 赋值运算符以产生这些副作用(例如,当其中一个操作发生时打印出一些东西),但在 C 中,您无法重载这些运算符,而且内置的运算符没有明显的副作用。
如果不重载它们,复制省略不会产生明显的副作用,因此没有什么可以阻止编译器执行此操作。
有些人不知道它是 possible to pass and return structs by value in C. My question is about the compiler making unnecessary copies when returning structs in C. Do C compilers such as GCC use Return value optimization(RVO) 优化,或者这只是一个 C++ 概念?我读到的关于 RVO 和复制省略的所有内容都是关于 C++ 的。
让我们考虑一个例子。 我目前正在 C 中实现 double-double data type (或者更确切地说是 float-float 开始,因为我发现它很容易进行单元测试)。考虑以下代码。
typedef struct {
float hi;
float lo;
} doublefloat;
doublefloat quick_two_sum(float a, float b) {
float s = a + b;
float e = b - (s - a);
return (doublefloat){s, e};
}
编译器会制作 doublefloat
值 I return 的临时副本还是可以删除临时副本?
C 中的命名 return 值优化 (NRVO) 怎么样?我还有一个功能
doublefloat df64_add(doublefloat a, doublefloat b) {
doublefloat s, t;
s = two_sum(a.hi, b.hi);
t = two_sum(a.lo, b.lo);
s.lo += t.hi;
s = quick_two_sum(s.hi, s.lo);
s.lo += t.lo;
s = quick_two_sum(s.hi, s.lo);
return s;
}
在这种情况下,我return正在命名结构。这种情况下的临时副本可以省略吗?
应该指出,这是 C 的一般问题,我在这里使用的代码示例只是示例(当我优化它时,无论如何我将使用带有内在函数的 SIMD)。我知道我可以查看汇编输出以查看编译器的功能,但我认为这是一个有趣的问题。
它在 C++ 中被大量提及的原因是因为在 C++ 中,RVO 有副作用(即不调用临时对象的析构函数,也不调用结果对象的复制构造函数或赋值运算符)。
在 C 中,没有可能的副作用,只有潜在的性能改进。我认为某些编译器没有理由不能执行这种优化。至少,标准里没有禁止的。
无论如何,优化取决于编译器和优化级别,所以我不会在关键代码路径上打赌,除非使用的编译器定义明确并且预计不会更改(这种情况仍然经常发生)。
RVO/NRVO 在 C 中的 "as-if" 规则下是明确允许的。
在 C++ 中,您可以获得可观察到的副作用,因为您重载了构造函数、析构函数、and/or 赋值运算符以产生这些副作用(例如,当其中一个操作发生时打印出一些东西),但在 C 中,您无法重载这些运算符,而且内置的运算符没有明显的副作用。
如果不重载它们,复制省略不会产生明显的副作用,因此没有什么可以阻止编译器执行此操作。