2 回答
TA贡献1790条经验 获得超9个赞
首先,我对 Go 的了解不够,无法对其做出判断,但答案将适用于 C 的情况。
如果您只是在研究像ints 这样的原始类型,那么我会说这两种技术之间没有性能差异。
当structs 发挥作用时,通过指针修改变量有一个非常小的优势(纯粹基于您在代码中所做的事情)
#include <stdio.h>
struct Person {
int age;
const char *name;
const char *address;
const char *occupation;
};
struct Person getReturnedPerson() {
struct Person thePerson = {26, "Chad", "123 Someplace St.", "Software Engineer"};
return thePerson;
}
void changeExistingPerson(struct Person *thePerson) {
thePerson->age = 26;
thePerson->name = "Chad";
thePerson->address = "123 Someplace St.";
thePerson->occupation = "Software Engineer";
}
int main(void) {
struct Person someGuy = getReturnedPerson();
struct Person theSameDude;
changeExistingPerson(&theSameDude);
return 0;
}
GCC x86-64 11.2
没有优化
通过函数的 return返回struct变量比较慢,因为必须通过分配所需的值来“构建”变量,然后将变量复制到返回值。
当您通过指针间接修改变量时,除了将所需的值写入内存地址(基于您传入的指针)之外,别无他法
.LC0:
.string "Chad"
.LC1:
.string "123 Someplace St."
.LC2:
.string "Software Engineer"
getReturnedPerson:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-40], rdi
mov DWORD PTR [rbp-32], 26
mov QWORD PTR [rbp-24], OFFSET FLAT:.LC0
mov QWORD PTR [rbp-16], OFFSET FLAT:.LC1
mov QWORD PTR [rbp-8], OFFSET FLAT:.LC2
mov rcx, QWORD PTR [rbp-40]
mov rax, QWORD PTR [rbp-32]
mov rdx, QWORD PTR [rbp-24]
mov QWORD PTR [rcx], rax
mov QWORD PTR [rcx+8], rdx
mov rax, QWORD PTR [rbp-16]
mov rdx, QWORD PTR [rbp-8]
mov QWORD PTR [rcx+16], rax
mov QWORD PTR [rcx+24], rdx
mov rax, QWORD PTR [rbp-40]
pop rbp
ret
changeExistingPerson:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax], 26
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax+8], OFFSET FLAT:.LC0
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax+16], OFFSET FLAT:.LC1
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax+24], OFFSET FLAT:.LC2
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 64
lea rax, [rbp-32]
mov rdi, rax
mov eax, 0
call getReturnedPerson
lea rax, [rbp-64]
mov rdi, rax
call changeExistingPerson
mov eax, 0
leave
ret
轻微优化
但是,今天的大多数编译器都可以弄清楚您在这里尝试做什么,并将平衡两种技术之间的性能。
如果你想绝对小气,传递指针仍然会稍微快几个时钟周期。
在从函数返回一个变量时,你至少还需要设置返回值的地址。
mov rax, rdi
但是在传递指针时,甚至没有这样做。
但除此之外,这两种技术没有性能差异。
.LC0:
.string "Chad"
.LC1:
.string "123 Someplace St."
.LC2:
.string "Software Engineer"
getReturnedPerson:
mov rax, rdi
mov DWORD PTR [rdi], 26
mov QWORD PTR [rdi+8], OFFSET FLAT:.LC0
mov QWORD PTR [rdi+16], OFFSET FLAT:.LC1
mov QWORD PTR [rdi+24], OFFSET FLAT:.LC2
ret
changeExistingPerson:
mov DWORD PTR [rdi], 26
mov QWORD PTR [rdi+8], OFFSET FLAT:.LC0
mov QWORD PTR [rdi+16], OFFSET FLAT:.LC1
mov QWORD PTR [rdi+24], OFFSET FLAT:.LC2
ret
main:
mov eax, 0
ret
TA贡献1816条经验 获得超4个赞
我认为对您的问题的简短回答(至少对于 C,我不熟悉 GO 内部)是 C 函数是按值传递的,通常也按值返回,因此必须复制数据对象,人们担心所有的性能复制。对于大型对象或深度复杂的对象(包含指向其他内容的指针),将被复制的值作为指针通常更有效或更合乎逻辑,因此函数可以“操作”数据而无需复制它. 话虽如此,现代编译器在确定参数数据是否适合寄存器或有效复制返回的结构等内容方面非常聪明。底线是现代 C 代码做最适合您的应用程序或对您来说最清楚的事情。如果至少在开始时会降低可读性,请避免过早优化。还有编译器资源管理器(https://godbolt.org/)是你的朋友,如果你想检查不同风格的效果,特别是在优化方面。
- 2 回答
- 0 关注
- 99 浏览
添加回答
举报