取消引用指针有多贵?

时间:2009-01-10 18:14:33

标签: c++ pointers

对指针执行取消引用操作有多贵?

我可以想象内存传输在某种程度上与对象大小成比例,但我想知道解除引用操作部分有多贵。

6 个答案:

答案 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)

指针的解除引用不应该比将地址复制到(地址)寄存器多得多。多数民众赞成。