PBrenek
PBrenek

Reputation: 571

C# Structs: Mutability versus Performance

I realize that structs are meant to be immutable, that mutating structs is evil, and the proper way to change values in a struct is by creating new instances. However, I am not clear on the memory and performance aspects/issues of new instances versus allowing the struct to be mutable.

Suppose I have the struct,

struct Vehicle
{
    public readonly int Number;
    public readonly double Speed, Direction;

    public Vehicle(int number, double speed, double direction)
    {
        this.Number = number;
        this.Speed = speed;
        this.Direction = direction;
    }
}

And create it as:

Vehicle car;

as well as

Vehicle plane = new Vehicle(1234, 145.70, 73.20)

If I need to assign to Number, Speed, or Direction later, I could remove the readonly and make the struct mutable - I know it is eveil to do this. - thereby "mutating" an already created struct value.

Alternatively I could create a new struct instance. So instead of saying car.Speed = 120.7; I could say car = new Vehicle(car.Number, 178.55, car.Direction);. That would create a new struct value almost like the old value, only with the Speed changed. But it wouldn't mutate the existing struct value.

Here is the issue. Suppose, as an extreme example, I need to update the speed and/or the direction many, many thousands of times per second. I would think that the creation of this many instances would severely impact memory and performance, and that in this case it would be better to allow the struct to be mutable.

Can anyone please clarify the memory and performance issues of a mutable struct versus the proper way to implement a struct in this type of an extreme case?

Upvotes: 5

Views: 2439

Answers (3)

supercat
supercat

Reputation: 81159

There are two distinct usage cases for structs; in some cases, one wants a type that encapsulates a single value and behaves mostly like a class, but with better performance and a non-null default value. In such cases, one should use what I would call an opaque structure; MSDN's guidelines for structures are written on the assumption that this is the only usage case.

In other cases, however, the purpose of the struct is simply to bind some variables together with duct tape. In those cases, one should use a transparent struct which simply exposes those variables as public fields. There is nothing evil about such types. What's evil is the notion that everything should behave like a class object, or that everything should be "encapsulated". If the semantics of the struct are such that:

  1. There is some fixed set of readable members (fields or properties) which expose its entire state
  2. Given any set of desired values for those members, one can create an instance with those values (no combinations of values are forbidden).
  3. The default value of the struct should be to have all those members set to the default values of their respective types.

and any change to them would break the code which uses it, then there is nothing which future versions of the struct could possibly do which a transparent struct would not be able to do, nor is there anything that a transparent struct would allow which a future version of the struct would be able to prevent. Consequently, encapsulation imposes cost without adding value.

I would suggest that one should endeavor to, whenever practical, make all structs either transparent or opaque. Further, I would suggest that because of deficiencies in the way .net handles struct methods, one should avoid having opaque structures' public members modify this except perhaps in property setters. It is ironic that while MSDN's guidelines suggest one shouldn't use a struct for things that don't represent a "single value", in the common scenario where one simply wants to pass a group of variables from one piece of code to another, transparent structs are vastly superior to opaque structs or class types, and the margin of superiority grows with the number of fields.

BTW, with regard to the original question, I would suggest that it's useful to represent that your program may want to deal with two kinds of things: (1) a car, and (2) information related to a particular car. I would suggest that it may be helpful to have a struct CarState, and have instances of Car hold a field of type CarState. This would allow instances of Car to expose their state to outside code and possibly allow outside code to modify their state under controlled circumstances (using methods like

delegate void ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);
delegate void ActionByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, T3 p3);

void UpdateState<TP1>(ActionByRef<CarState, TP1> proc, ref TP1 p1)
{ proc(ref myState, ref p1); }
void UpdateState<TP1,TP2>(ActionByRef<CarState, TP1,TP2> proc, ref TP1 p1, ref TP2 p2)
{ proc(ref myState, ref p1, ref p2); }

Note that such methods offer most of the performance advantages of having the car's state be a mutable class, but without the dangers associated with promiscuous object references. A Car can let outside code may update a car's state via the above methods without allowing outside code to modify its state at any other time.

BTW, I really wish .net had a way of specifying that a "safe" struct or class should be considered as encapsulating the members of one or more of its constituents [e.g. so that struct which held a Rectangle called R and an String called Name could be regarded as having a fields X, Y, Width, and Height which alias the corresponding struct fields. If that were possible, it would greatly facilitate situations where a type needs to hold more state than previously expected. I don't think the present CIL allows for such aliasing in a safe type, but there's no conceptual reason it couldn't.

Upvotes: 4

usr
usr

Reputation: 171178

I'll only answer the performance aspect of this question. I'll spare you the premature optimization warning and the discussion about semantics.

Yes, assigning the whole struct has a performance cost. It introduces assignments which were not present in the mutating variant. I do not trust the JIT to optimize them out as the .NET JIT is pretty simple (it is optimized for fast code-gen).

Probably, these assignments are very cheap because they are contiguous in memory and the memory cache line has been loaded already. No memory transaction needs to occur because of them.

The performance difference will be small (for reasonably sized structs). You will be able to measure it, though.

It also introduces clutter into your code to create a new instance of the struct for each mutation. I have encountered cases in practice where I found it to be best to use mutable structs from a code quality perspective. Those are just a few cases but they exist.

Upvotes: 1

Adam Maras
Adam Maras

Reputation: 26853

The question you need to answer for yourself is "what are the intended semantics of the type I'm writing?"

A struct should be used instead of a class when you're trying to write a value type. A good example of a value type is DateTime. Regardless of anything else that's going on, January 12th, 2013 at 3:33PM GMT-7 is always going to be January 12th, 2013 at 3:33PM GMT-7. A color is another good example; two colors made up of RGB 255, 0, 0 are never going to be different in any way.

The type you're trying to create, Vehicle, does not have value semantics as you want it to contain state that is meant to be updated for a specific instance of a car. Therefore, you should be writing a mutable class instead of a struct.

Upvotes: 3

Related Questions