53

How expensive is it to perform the dereference operation on a pointer?

I can imagine that the memory transfer is somehow proportional to the object size, but I want to know how expensive the dereference operation part is.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
tunnuz
  • 23,338
  • 31
  • 90
  • 128

6 Answers6

78

Dereferencing, when translated into machine code, can mean different things depending on what you do with the dereferenced object. Accessing a single member of a class through a pointer is typically cheap. For example if c is a pointer to an instance of class C with an int member n then something like this:

int n = c->n;

Might translate into one or two machine instructions and might load a register with a single memory access.

On the other hand this implies making a complete copy of the object pointed to by c:

C d = *c;

The cost of this will depend on the size of C, but note that it is the copy that is the major expense and the 'dereference' part is really just 'using' the pointer address in the copy instructions.

Note that accessing members of large objects typically requires pointer offset calculation and memory access whether or not the object is a local object or not. Typically only very small objects are optimized to live only in registers.

If you are concerned about the cost of pointers over references then don't be. The difference between these are a language semantics difference and by the time the machine code is generated pointer and reference access look exactly the same.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • In some cases references can offer the optimizer more opportunities for cleverness, because a references cannot be modified, while a pointer can be. – Roddy Jan 11 '09 at 15:25
  • @Roddy: I think there are opportunities if using references as parameters to inline methods instead of pointers. – mmmmmmmm Jan 28 '09 at 18:29
  • 3
    One thing to note that if the dereferenced pointer is passed into a function by reference, not by value, then no extra copy will be created based on the top answer to [this question](https://stackoverflow.com/questions/11347111/dereferencing-a-pointer-when-passing-by-reference). – HaiXin Tie Oct 18 '17 at 19:09
  • So, `c->n` eventually converted into a machine code which calculates something like `pointer + offset_of_n`? If so, do you have any idea about how this calculation differs depending on where that object lives (in a CPU register or main memory)? – stdout Aug 25 '20 at 06:58
45

It depends on what you do with the dereferenced pointer. A mere dereference operation does nothing in itself. It just gets an lvalue of type T which represents your object, if your pointer is a 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; 
}

If you actually get the value out of that object denoted by the lvalue returned by the dereference operation, the compiler has to copy the data the object contains. For a simple POD, that is just a mere memcpy:

a aGlobalA;
void f(a * t) {
    // gets the value of of the object denoted by *t, copying it into aGlobalA
    aGlobalA = *t; 
}

My gcc port outputs this code for 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

Optimized machine code would use in-line code instead of a call to memcpy, but that's really just an implementation detail. What is important is, that merely *t isn't executing any code, but accessing the value of that object actually needs to copy it.

Would we have to do with a type having a user defined copy assignment operator, affairs are more complex:

struct a {
    int big[42];
    void operator=(a const&) { }
};

The code for the same function f now looks like:

    sub     $29, $29, 8
    add     $29, $29, 8
    jr      $31

Hah. But it wasn't such a surprise, wasn't it? After all, the compiler is supposed to call our operator=, and if it does nothing, the whole function also does nothing!

Conclusion

I think the conclusion we can draw is, it all depends on how the returned value of operator* is used. If we have just a pointer that we dereference, we see above that the code generated largely depends on the circumstances. I haven't showed how it behaves if we dereference a class type having overloaded operator* . But essentially, it's just behaving like we saw with operator=. All measurements were done with -O2, so the compiler properly inlined calls :)

Lii
  • 11,553
  • 8
  • 64
  • 88
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • "It's as expensive as it gets" means "it's very expensive" which, as the rest of your answer explains, is not really the case. – ChrisN Jan 10 '09 at 19:46
  • 1
    Oh i like how you really provide ASM for this ( don't like the asm-syntax you've preffered though ). Hehe. +1 – Filip Ekberg Jan 10 '09 at 19:59
  • In a scenario where I have to work with a lib that uses raw pointers, I want to keep a clean design using references. Does data copying occur when I call a function "void somefunc(SomeObj& obj)" with a dereferencing like this: somefunc(*mySomeObjPtr) ? – Tom Jul 27 '11 at 07:45
  • ^ old, but the reply is no, as seen in other threads, which show the exact same principles apply to this sort of 'dereferencing into a reference', e.g. http://stackoverflow.com/questions/14193620/converting-a-pointer-to-reference-costly Again: copying as a result of dereferencing only occurs - indirectly - if the dereferenced object is next assigned to a non-reference var, just as it would with the (non-pointer'd) object itself – underscore_d Dec 07 '15 at 22:25
18

The most important factor in dereferencing pointers on ordinary systems is that you're likely to generate a cache miss. A random access in SDRAM memory costs tens of nanoseconds (e.g. 64). On gigaherz processors, this means that your processor is idling hundreds (or > thousand) of cycles, without being able of doing anything else in the meantime.

Only on SRAM based systems (which you'll only find in embedded software), or when your software is cache optimized, the factors discussed in the other posts come into play.

user52875
  • 3,020
  • 22
  • 21
  • Any good sources where we can read about the frequency/magnitude of this? – underscore_d Dec 07 '15 at 22:30
  • Wikipedia on cas latency: https://en.wikipedia.org/wiki/CAS_latency Take into account that you also need to add precharge and RAS to CAS delay, so you can probably double the numbers that you find in the table in the article. Notice that the latency time for SDRAM accesses hardly improved since the SDRAM era. – user52875 Dec 30 '15 at 12:16
11

Dereferencing can be expensive mostly because it costs an instruction to fetch data from memory which might be far away and do not exhibit locality of reference. In that case, the processor should fetch data from non-cached memory and even hard disk (in case of a hard page fault).

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
7

Dereferencing(multiple) cost CPU cycles.

Instead of writing:

string name = first->next->next->next->name;
int age = first->next->next->next->age;

this is O(n)


Write it as:

node* billy_block = first->next->next->next;

string name = billy_block->name;
int age = billy_block->age;

this is O(1)

So your code will not "ask" each and every block just to get to the fourth block.

Multiple dereferencing is like having a neighborhood who only knows a neighbor next to them.

Imagine if you ask a person from the first block where does your friend Billy resides, he will tell you he doesn't know your friend, he'll tell you he only know the neighbor next to them, then he'll just tell you to ask his neighbor, then you'll ask his neighbor, he'll answer the same thing as the first block did, you keep asking until you arrive at your friend's block. Not very efficient

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • 9
    Any half-decent compiler should optimize the first into the second. – Roddy Jan 11 '09 at 15:29
  • 1
    We can never be sure, there are some compilers that will just simply do our bidding. – Michael Buen Jan 11 '09 at 17:42
  • 8
    In this case, compilers will catch the Common SubExpression (CSE). If there's a function call between them, say string name = first->next->next->next->name(), any ->next could change and compilers will quickly give up. That's why getters/setters should be inline. – MSalters Jan 12 '09 at 10:23
0

The dereferencing of a pointer shouldn't be much more than copying an address to a (address)register. Thats all.

EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
  • 1
    Dereferencing usually means reading from that address as well. Which may vary in cost depending on cache, locality and such. – jalf Jan 10 '09 at 18:48
  • 1
    It does not matter what the dereferenced pointer is "usually" used for. Dereferencing is a well defined operation and it doesnt mean much more that copying an address from a memory location to an address register (or whatever is nessecary on the specific platform). See the answer of litb. – EricSchaefer Jan 10 '09 at 21:26