Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allocation of value types

When you assign an instance of a value type to another instance, the object is copied bit-by-bit to the target location:

private struct Word
{
    public Word(char c) { ... }
}

public void Method(Word a)
{
    Word b = a; //a is copied and stored in b
}

But given the following code:

private Word _word;

public void Method() {
    _word = new Word('x');
}

I suspect that the right-hand side (RHS) expression is evaluated first - which instantiates a value type on the stack - and then the value is copied and stored on the location of the _word field, which is on the heap.

The alternative would be to take the left-hand side into consideration, and instantiate the value type directly on _word, avoiding having to copy the object.

Is my suspicion correct? If it is, I suppose it's safe to assume that the first block of code would perform better than the second.

//1 instantiation + 10k copies
Word[] words = new Word[10000];
Word word = new Word('x');

for (int i = 0; i < 10000; i++)
    words[i] = word;


//10k instantiations + 10k copies
Word[] words = new Word[10000];

for (int i = 0; i < 10000; i++)
    words[i] = new Word('x');

Note: I'm not trying to micro-optimize anything.

Edit: The core of my question is, as Lee puts it: Are structs allocated in place directly, or do they need to be allocated then copied?

like image 787
dcastro Avatar asked Jan 11 '23 20:01

dcastro


1 Answers

When you assign an instance of a value type to another instance, the object is copied bit-by-bit to the target location

When you assign an instance of a value type to a variable of the same type, the value is copied to the target location, yes. But that is true of reference types as well: the reference is copied bit by bit to the target location. The referent of course stays right where it is.

I suspect that the right-hand side (RHS) expression is evaluated first

The specification states that the left hand side is evaluted to produce a variable, then the right hand side is evaluated to produce a value, and then the assignment happens.

In the examples you give the evaluation of the left hand side does not produce an observable side effect and therefore its evaluation can be re-ordered by the optimizers in the C# compiler, the jitter or the CPU if any of them so choose. But if you had something like

x[i++] = v();

then the side effect on the left hand side has to happen before the call on the right hand side.

The core of my question is: Are structs allocated in place directly, or do they need to be allocated then copied?

The specification states that structures are allocated in a temporary location -- which would typically be the stack or a register in practice -- and then copied to their final destination. However, there are some situations in which the optimizer can determine that it is impossible for the user to notice if the mutation happens "in place" at the final destination. This is a copy elision optimization, and the C# compiler will perform this optimization if it feels it can get away with it.

For more details see my article on the subject:

http://ericlippert.com/2010/10/11/debunking-another-myth-about-value-types/

like image 161
Eric Lippert Avatar answered Jan 19 '23 01:01

Eric Lippert