对指针执行取消引用操作有多贵?
我可以想象内存传输在某种程度上与对象大小成比例,但我想知道解除引用操作部分有多贵。
答案 0 :(得分:69)
取消引用,在翻译成机器代码时,可能意味着不同的事情,具体取决于您对解除引用的对象所做的事情。通过指针访问类的单个成员通常很便宜。例如,如果c是指向具有class C
成员n的int
实例的指针,则类似这样的内容:
int n = c->n;
可能会转换为一个或两个机器指令,并可能加载具有单个内存访问权限的寄存器。
另一方面,这意味着制作c:
指向的对象的完整副本C d = *c;
这个的成本将取决于C的大小,但请注意,它是主要费用的副本,'dereference'部分实际上只是'使用'复制指令中的指针地址。
请注意,访问大对象的成员通常需要指针偏移计算和内存访问,无论该对象是否是本地对象。通常只有非常小的对象被优化为仅存在于寄存器中。
如果你担心指针超过引用的成本,那就不要了。它们之间的区别在于语言语义的差异,并且在生成机器代码时,指针和引用访问看起来完全相同。
答案 1 :(得分:42)
这取决于你使用解除引用的指针做什么。仅仅取消引用操作本身并不起作用。如果您的指针是T
T*
类型的左值
struct a {
int big[42];
};
void f(a * t) {
// does nothing. Only interesting for standard or compiler writers.
// it just binds the lvalue to a reference t1.
a & t1 = *t;
}
如果您实际从取消引用操作返回的左值表示的对象中获取值,则编译器必须复制对象包含的数据。对于简单的POD,这仅仅是memcpy
:
a aGlobalA;
void f(a * t) {
// gets the value of of the object denoted by *t, copying it into aGlobalA
aGlobalA = *t;
}
我的gcc端口为f:
输出此代码 sub $29, $29, 24 ; subtract stack-pointer, creating this frame
stw $31, $29, 20 ; save return address
add $5, $0, $4 ; copy pointer t into $5 (src)
add $4, $0, aGlobalA ; load address of aGlobalA into $4 (dst)
add $6, $0, 168 ; put size (168 bytes) as 3rd argument
jal memcpy ; call memcpy
ldw $31, $29, 20 ; restore return address
add $29, $29, 24 ; add stack-pointer, destroying this frame
jr $31
优化的机器代码将使用内联代码而不是对memcpy
的调用,但这实际上只是一个实现细节。重要的是,仅*t
没有执行任何代码,但访问该对象的值实际上需要复制它。
我们是否需要使用具有用户定义的复制赋值运算符的类型,事务更复杂:
struct a {
int big[42];
void operator=(a const&) { }
};
现在,相同函数f
的代码如下所示:
sub $29, $29, 8
add $29, $29, 8
jr $31
哈。但这不是一个惊喜,不是吗?毕竟,编译器应该调用我们的operator=
,如果它什么都不做,整个函数也什么都不做!
我认为我们可以得出的结论是,这完全取决于如何使用operator*
的返回值。如果我们只有一个我们取消引用的指针,我们在上面看到生成的代码在很大程度上取决于环境。如果我们取消引用具有过载operator*
的类类型,我没有展示它的行为。但实质上,它只是表现得像我们在operator=
看到的那样。所有测量均使用-O2
完成,因此编译器正确内联调用:)
答案 2 :(得分:17)
解除对普通系统指针的最重要因素是您可能会生成缓存未命中。 SDRAM存储器中的随机访问花费数十纳秒(例如64)。在gigaherz处理器上,这意味着您的处理器闲置数百(或数千)个周期,而在此期间无法执行任何其他操作。
仅限基于SRAM的系统(您只能在嵌入式软件中找到),或者当您的软件经过缓存优化时,其他帖子中讨论的因素才会发挥作用。
答案 3 :(得分:8)
取消引用可能很昂贵,主要是因为它需要从内存中获取数据的指令,这些数据可能很远并且没有显示引用的位置。在这种情况下,处理器应从非缓存内存甚至硬盘中获取数据(如果出现硬页错误)。
答案 4 :(得分:5)
解除引用(多个)成本CPU周期。
而不是写作:
string name = first->next->next->next->name;
int age = first->next->next->next->age;
this is O(n)
把它写成:
node* billy_block = first->next->next->next;
string name = billy_block->name;
int age = billy_block->age;
this is O(1)
因此,您的代码不会“询问”每个块只是为了到达第四个块。
多个解除引用就像拥有一个只知道他们旁边的邻居的邻居。
想象一下,如果你问第一个街区的人你的朋友比利居住在哪里,他会告诉你他不认识你的朋友,他会告诉你他只知道他们旁边的邻居,然后他会只是告诉你问他的邻居,然后你会问他的邻居,他会回答与第一个街区相同的事情,你一直问,直到你到达你朋友的街区。效率不高
答案 5 :(得分:0)
指针的解除引用不应该比将地址复制到(地址)寄存器多得多。多数民众赞成。