c++ - 临时对象在 C++ 中是不可避免的吗?
问题描述
我们阅读了 C++ 中在代码中创建临时对象的不同实例。参见例如。现在,考虑以下代码片段。
const int rows{250};
const int cols{250};
const double k{0.75};
double A[rows][cols];
double B[rows][cols];
double C[rows][cols];
// Code that initialises A and B
...
for (int i{}; i<rows; ++i) {
for (int j{}; j<cols; ++j) {
C[i][j] = k*A[i][j]*B[i][j]/(A[i][j]*A[i][j] + B[i][j]*B[i][j]);
}
}
在计算内部 for 循环中方程的 RHS 中的分子和分母时是否创建了临时变量?显然,可以部分地评估表达式并将结果存储在中间变量中,如下所示。
for (int i{}; i<rows; ++i) {
for (int j{}; j<cols; ++j) {
double temp1 = A[i][j]*B[i][j];
double temp2 = k*temp1;
double temp3 = A[i][j]*A[i][j];
double temp4 = B[i][j]*B[i][j];
double temp5 = temp3 + temp4;
C[i][j] = temp2/temp5;
}
}
后一种方法是否不会引入额外的计算步骤,因此不会为内部 for 循环带来更多开销?
解决方案
用“注册”一词替换“临时”一词;)
一般来说,编译过程的第一步是编译器将规范化(https://en.wikipedia.org/wiki/Canonicalization)代码,使其采用标准化形式。不管你如何格式化你的代码,或者是否使用 temps,编译器都会在这两种情况下重新排列代码,使其或多或少相同。很有可能,它最终会为您的代码的两个版本生成以下内容:
double temp1 = A[i][j]*B[i][j];
double temp2 = k*temp1;
double temp3 = A[i][j]*A[i][j];
double temp4 = B[i][j]*B[i][j];
double temp5 = temp3 + temp4;
C[i][j] = temp2/temp5;
从那里,规范化的形式将被转换为汇编。在伪代码中,这大致与 x64 编译器可能生成的程序集的行一致:
xmm0 = load(B[i][j])
xmm1 = load(A[i][j])
xmm2 = xmm0 * xmm1; // A[i][j]*B[i][j];
xmm3 = load(k)
xmm3 = xmm3 * xmm2; // k*temp1;
xmm1 = xmm1 * xmm1; // A[i][j]*A[i][j];
xmm0 = xmm0 * xmm0; // B[i][j]*B[i][j];
xmm0 = xmm1 + xmm0 // temp3 + temp4;
xmm0 = xmm3 / xmm0 // temp2 / temp5;
store(C[i][j], xmm0)
几乎每个编译器都会尝试最小化加载和存储的数量(因为它们可能非常昂贵 - 例如缓存未命中、错误共享等),其余的临时文件将存储为寄存器(假设你不运行出去!)。
如果您有一个相对复杂的对象,那么您可能希望确保避免制作该对象的临时副本。即使那样,只要复制构造函数在类之外没有副作用,编译器很有可能会忽略这些副本(例如返回值优化)。
基本上这不是你真正需要担心的事情。无论如何,编译器几乎都会忽略您,重新排列您认为合适的代码,并生成尽可能好的结果程序集。
推荐阅读
- python - 如何在kivy中将按钮彼此相邻
- javascript - 如何通过私钥从新创建的帐户发送 eth?
- node.js - nodejs express req.body 未定义
- c# - 原始序列化对象以外的反序列化对象
- kotlin - 用于执行外部进程的 Kotlin 协程
- javascript - 奇怪的 js 无法在浏览器控制台中运行
- python - 显示表格中的随机项目
- postgresql - 如何在 PgAdmin4 中更改用户(不是管理员)密码
- scala - “他们使用 sbt、IDE 和其他工具作为编译器的接口”是否与“因此他们甚至可能没有安装 scalac”相反?
- java - 如何使用@TransactionTimeout 处理事务超时