Return 值 const 和 const 赋值 - 反汇编
Return value const& and const assignment - dissassembly
最近我一直在分析旧代码的某些部分,在某些情况下,从函数返回的值被分配给 const
变量,有时分配给 const&
。出于好奇,我已经切换到 dissasembly 以查看差异。不过在进入正题之前我先画一个简单的例子来参考一下代码:
struct Data
{
int chunk[1024];
};
Data getData()
{
return Data();
}
int main()
{
const std::string varInit{ "abc" }; // 1
const std::string varVal = "abc"; // 2
const std::string& varRef = "abc"; // 3
const Data dataVal = getData(); // 4
const Data& dataRef = getData(); // 5
return 0;
}
以上代码的以下反汇编是使用禁用优化的 VS2015 获得的。
我不是 asm 专家,但乍一看我会说 (1)
和 (2)
执行了类似的操作。尽管如此,令我惊讶的是 (3)
与以前的版本相比,在变量值分配期间未使用 const&
进行了两个额外的操作(lea
和 mov
)。
当数据按值从函数返回时,可以观察到同样的情况。 (5)
比 (4)
多了两个操作。
问题很窄:
- 这些额外的操作从何而来,它们的目的是什么?一般不像这里:What's the purpose of the LEA instruction 但在当前上下文中。
- 这至少会影响底层数据大小可忽略不计的对象的性能吗? (与示例中使用的
Data
结构相反)
- 打开优化后会有什么影响吗? (对于发布版本)
顺便说一下,我已经阅读了 关于在分配值时使用 const 和 const& 的优缺点,这些值可能有些相关但不是问题的一部分。
如果 (3) 编译器在 [ebp-84]
处创建 'hidden' 本地变量 'std::string' 将其命名为 _84
并执行这样的代码
const std::string _84 = "abc";
const std::string& varRef = _84;// lea + move (by sense varRef = &_84)
X& v
- 与 X* v
相同的意义和二进制代码 - v
实际上是指向 X
的指针,在这两种情况下,只是使用了不同的语法
与案例(5)相同
const Data _20a0 = getData();
const Data& dataRef = _20a0; // by sense dataRef = &_20a0, but by syntax dataRef = _20a0
或者说如果你换行
const Data& dataRef = getData();
写行
const Data& dataRef = dataVal;
您认为此行恰好采用 2 条 asm 指令:
lea eax,[dataVal]
mov [dataRef],eax
代码(4,5)和Data getData()
签名绝对是噩梦,无语
为了更清楚
关于 return 结构 'by value' - 函数只能 return 注册为结果(al
、ax
、eax
和 rax
in x64 ) 或 2 个寄存器 - edx:eax
(8 字节,高位 edx)或 rdx:rax
(x64 中的 16 字节)
万一
Data getData()
- 不可能 return Data
原样。怎么样?!?
所以你的函数真的转换为
Data* getData(Data* p)
{
Data x;
memcpy(p, &x, sizeof(Data));
return p;
}
并编码为
//const Data dataVal = getData();
Data _41A0, _3198, dataVal;
memcpy(&_3198, getData(&_41A0), sizeof(Data));
memcpy(&dataVal, &_3198, sizeof(Data));
//const Data& dataRef = getData();
Data _41A0, _3198, _20a0, &dataRef;
memcpy(&_51a8, getData(&_61b0), sizeof(Data));
memcpy(&_20a0, &_51a8, sizeof(Data));
dataRef = &_20a0;// very small influence compare all other
尝试计算编译器有多少无意义的 memcpy?
需要这样写代码
void InitData(Data* p);
Data dataVal;
InitData(&dataVal);
最近我一直在分析旧代码的某些部分,在某些情况下,从函数返回的值被分配给 const
变量,有时分配给 const&
。出于好奇,我已经切换到 dissasembly 以查看差异。不过在进入正题之前我先画一个简单的例子来参考一下代码:
struct Data
{
int chunk[1024];
};
Data getData()
{
return Data();
}
int main()
{
const std::string varInit{ "abc" }; // 1
const std::string varVal = "abc"; // 2
const std::string& varRef = "abc"; // 3
const Data dataVal = getData(); // 4
const Data& dataRef = getData(); // 5
return 0;
}
以上代码的以下反汇编是使用禁用优化的 VS2015 获得的。
(1)
和 (2)
执行了类似的操作。尽管如此,令我惊讶的是 (3)
与以前的版本相比,在变量值分配期间未使用 const&
进行了两个额外的操作(lea
和 mov
)。
当数据按值从函数返回时,可以观察到同样的情况。 (5)
比 (4)
多了两个操作。
问题很窄:
- 这些额外的操作从何而来,它们的目的是什么?一般不像这里:What's the purpose of the LEA instruction 但在当前上下文中。
- 这至少会影响底层数据大小可忽略不计的对象的性能吗? (与示例中使用的
Data
结构相反) - 打开优化后会有什么影响吗? (对于发布版本)
顺便说一下,我已经阅读了
如果 (3) 编译器在 [ebp-84]
处创建 'hidden' 本地变量 'std::string' 将其命名为 _84
并执行这样的代码
const std::string _84 = "abc";
const std::string& varRef = _84;// lea + move (by sense varRef = &_84)
X& v
- 与 X* v
相同的意义和二进制代码 - v
实际上是指向 X
的指针,在这两种情况下,只是使用了不同的语法
与案例(5)相同
const Data _20a0 = getData();
const Data& dataRef = _20a0; // by sense dataRef = &_20a0, but by syntax dataRef = _20a0
或者说如果你换行
const Data& dataRef = getData();
写行
const Data& dataRef = dataVal;
您认为此行恰好采用 2 条 asm 指令:
lea eax,[dataVal]
mov [dataRef],eax
代码(4,5)和Data getData()
签名绝对是噩梦,无语
为了更清楚
关于 return 结构 'by value' - 函数只能 return 注册为结果(al
、ax
、eax
和 rax
in x64 ) 或 2 个寄存器 - edx:eax
(8 字节,高位 edx)或 rdx:rax
(x64 中的 16 字节)
万一
Data getData()
- 不可能 return Data
原样。怎么样?!?
所以你的函数真的转换为
Data* getData(Data* p)
{
Data x;
memcpy(p, &x, sizeof(Data));
return p;
}
并编码为
//const Data dataVal = getData();
Data _41A0, _3198, dataVal;
memcpy(&_3198, getData(&_41A0), sizeof(Data));
memcpy(&dataVal, &_3198, sizeof(Data));
//const Data& dataRef = getData();
Data _41A0, _3198, _20a0, &dataRef;
memcpy(&_51a8, getData(&_61b0), sizeof(Data));
memcpy(&_20a0, &_51a8, sizeof(Data));
dataRef = &_20a0;// very small influence compare all other
尝试计算编译器有多少无意义的 memcpy?
需要这样写代码
void InitData(Data* p);
Data dataVal;
InitData(&dataVal);