Is the assignement of a value type considered to be atomic in .Net?
For example, consider the following program:
struct Vector3
{
public float X { get; private set; }
public float Y { get; private set; }
public float Z { get; private set; }
public Vector3(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public Vector3 Clone()
{
return new Vector3(X, Y, Z);
}
public override String ToString()
{
return "(" + X + "," + Y + "," + Z + ")";
}
}
class Program
{
private static Vector3 pos = new Vector3(0,0,0);
private static void ReaderThread()
{
for (int i = 0; i < int.MaxValue; i++)
{
Vector3 v = pos;
Console.WriteLine(v.ToString());
Thread.Sleep(200);
}
}
private static void WriterThread()
{
for (int i = 1; i < int.MaxValue; i++)
{
pos = new Vector3(i, i, i);
Thread.Sleep(200);
}
}
static void Main(string[] args)
{
Thread w = new Thread(WriterThread);
Thread r = new Thread(ReaderThread);
w.Start();
r.Start();
}
}
Can a program like this suffer from a High-Level data race? Or even a Data Race?
What I want to know here is: is there any possibility that v will either contain:
Garbage values due to a possible data race
Mixed components X, Y or Z that refer to both pos before assignement and pos after assignment. For example, if pos = (1,1,1) and then pos is assigned the new value of (2,2,2) can v = (1,2,2)?
Structs are value types. If you assign a struct to a variable/field/method parameter, the whole struct content will be copied from the source storage location to the storage location of the variable/field/method parameter (the storage location in each case being the size of the struct itself).
Copying a struct is not guaranteed to be an atomic operation. As written in the C# language specification:
Atomicity of variable references
Reads and writes of the following data types are atomic: bool, char,
byte, sbyte, short, ushort, uint, int, float, and reference types. In
addition, reads and writes of enum types with an underlying type in
the previous list are also atomic. Reads and writes of other types,
including long, ulong, double, and decimal, as well as user-defined
types, are not guaranteed to be atomic. Aside from the library
functions designed for that purpose, there is no guarantee of atomic
read-modify-write, such as in the case of increment or decrement.
So yes, it can happen that while one thread is in the process of copying the data from a struct storage location, another thread comes along and starts copying new data from another struct to that storage location. The thread copying from the storage location thus can end up copying a mix of old and new data.
As a side note, your code can also suffer from other concurrency problems due to how one of your threads is writing to a variable and how the variable is used by another thread. (An answer by user acelent to another question explains this rather well in technical detail, so i will just refer to it: https://stackoverflow.com/a/46695456/2819245) You can avoid such problems by encapsulating any access of such "thread-crossing" variables in a lock block. As an alternative to lock, and with regard to basic data types, you could also use methods provided by the Interlocked class to access thread-crossing variables/fields in a thread-safe manner (Alternating between both lock and Interlocked methods for the same thread-crossing variable is not a good idea, though).
Related
I have a class that has two int fields x and y, and a method Increment that increments both of these fields by dx and dy respectively. I would like to prevent the state of my class to become corrupted by silent arithmetic overflows (which would result to x or y or both becoming negative), so I am explicitly incrementing the fields in a checked block:
class MyClass
{
private int x;
private int y;
public void Increment(int dx, int dy)
{
checked { x += dx; y += dy; }
}
}
This should ensure that in case of arithmetic overflow the caller will receive an OverflowException, and the state of my class will remain intact. But then I realized that an arithmetic overflow could occur in the increment of y, after the x has already successfully incremented, resulting to a different type of state corruption, which is not less disruptive than the first. So I changed the implementation of the Increment method like this:
public void Increment2(int dx, int dy)
{
int x2, y2;
checked { x2 = x + dx; y2 = y + dy; }
x = x2; y = y2;
}
This seems like a logical solution to the problem, but now I am concerned that the compiler could "optimize" my carefully crafted implementation, and reorder the instructions in a way that would allow the x assignment to occur before the y + dy addition, resulting again to state corruption. I would like to ask if, according to the C# specification, this undesirable scenario is possible.
I am also considering removing the checked keyword, and instead compiling my project with the "Check for arithmetic overflow" option enabled (<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>). Could this make any difference, regarding a possible reordering of the instructions inside the Increment method?
Update: a more succinct arithmetic-overflow-safe implementation is possible by using tuple deconstruction. Is this version any different (less safe) than the verbose implementation?
public void Increment3(int dx, int dy)
{
(x, y) = checked((x + dx, y + dy));
}
Clarification: The MyClass is intended to be used in a single-threaded application. Thread-safety is not a concern (I know that it's not thread-safe, but it doesn't matter).
TL;DR; This is perfectly safe to do within a single thread.
The CLI, which implements your code in native machine language, is simply not allowed to reorder instructions in such a fashion as to have a visible side-effect, at least as far as observations from a single thread are concerned. It is forbidden by the specification.
Let's take a look at ECMA-335, the specification for the CLR and CLI, (my bold)
I.12.6.4 Optimization
Conforming implementations of the CLI are free to execute programs using any technology that guarantees, within a single thread of execution, that side-effects and exceptions generated by a thread are visible in the order specified by the CIL.
... snip ...
There are no ordering guarantees relative to exceptions injected into a thread by another thread (such exceptions are sometimes called “asynchronous exceptions” (e.g., System.Threading.ThreadAbortException).
[Rationale: An optimizing compiler is free to reorder side-effects and synchronous exceptions to the extent that this reordering does not change any observable program behavior. end rationale]
[Note: An implementation of the CLI is permitted to use an optimizing compiler, for example, to convert CIL to native machine code provided the compiler maintains (within each single thread of execution) the same order of side-effects and synchronous exceptions.
This is a stronger condition than ISO C++ (which permits reordering between a pair of sequence points) or ISO Scheme (which permits reordering of arguments to functions). end note]
So exceptions must occur in the order specified in the IL code compiled by C#, therefore if an overflow occurs in a checked context, the exception must be thrown before any observation of the next instructions. (In an unchecked context, there is no such guarantee, because there is no exception, however the difference is not observable on a single thread.)
Note that this does not mean that the two additions cannot occur before the stores into local variables or before the two overflow checks, because the locals cannot be observed once an exception is thrown. In a fully optimized build, the locals will probably be stored in CPU registers, and wiped in the event of an exception.
The CPU is also free to reorder internally, so long as the same guarantees apply.
There is one exception to all of this, barring the mentioned multi-threading allowance:
Optimizers are granted additional latitude for relaxed exceptions in methods. A method is E-relaxed for a kind of exception if the innermost custom attribute CompilationRelaxationsAttribute pertaining to exceptions of kind E is present and specifies to relax exceptions of kind E.
However the current Microsoft implementation does not provide such a relaxation option anyway.
As to using the tuple-deconstructing syntax, unfortunately the spec for C# 7 has not been released, but this page on Github indicates that it should also be side-effect free.
Make your class immutable. When you want to change something, return a new instance.
class MyClass
{
private int x;
private int y;
public MyClass Increment(int dx, int dy)
{
checked
{
return new MyClass { x = this.x + dx, y = this.y + dy };
}
}
}
And in your calling code, you'd replace
myClass.Increment( a, b );
with
myClass = myClass.Increment( a, b );
This ensures your class is always internally consistent.
If you don't want to return a new instance, you can get the same benefit by using an internal readonly struct.
public readonly struct Coords
{
public int X { get; init; }
public int Y { get; init; }
}
class MyClass
{
private Coords _coords;
public void Increment(int dx, int dy)
{
checked
{
var newValue = new Coords { X = _coords.X + dx, Y = _coords.Y + dy };
}
_coords = newValue;
}
}
Suppose you have two structs that have exactly the same memory layout. Is it possible to do a very fast unchecked memory cast from one to the other in C#/.NET?
//my code base
[StructLayout(LayoutKind.Sequential)]
public struct VectorA
{
float x;
float y;
float z;
}
//defined by a third party library
[StructLayout(LayoutKind.Sequential)]
public struct VectorB
{
float a;
float b;
float c;
}
//somewhere else in my code
var vectorA = new VectorA();
//then calling a method from the library
MethodFromThirdPartyLibrary((VectorB)vectorA); //compiler error
Of course it should be faster as a method that assigns the data fields and creates a new copy in memory.
Also: The 3d vector is only an example, same problem for matrices which is 16 floats and Vector2, Vector4, ...
EDIT: Improved code with more comments and better usage example.
Why would it be faster? Would it be faster in C++ than writing the copy explicitly as in C#? Remember, you only have 3 x 32-bit numbers you want to copy from one place to another, so it's not exactly a good fit for vectorization.
It's likely if you had an array of these structures that you could get some speed up using vectorized load/stores in an unrolled loop in assembler. But you've not stated that in the question.
The main overhead here is probably the method call, rather than the assignment:
static void VecAToB(ref VectorA vectorA, ref VectorB vectorB)
{
vectorB.x = vectorA.a;
vectorB.y = vectorA.b;
vectorB.z = vectorA.c;
}
You might like to try:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void VecAToB(ref VectorA vectorA, ref VectorB vectorB)
{
vectorB.x = vectorA.a;
vectorB.y = vectorA.b;
vectorB.z = vectorA.c;
}
Just wondering cause I often find myself giving input like coordinates (X, Y) and was wondering which case is better.
If I store 3 int in one array I have a reduction of the code to 1/3, but are there more reason to prefer array over multiple vars?
Example to clarify:
int[] coord = new int[2];
coord[0] = 3;
coord[1] = 2;
or
int x = 3;
int y = 2;
I'd say that if the coordinates are so tightly coupled that you always pass both of them together (which I believe to be true), you can create a struct to encapsulate them.
public struct Coords
{
private int x;
private int y;
public Coords(int x, int y)
{
this.x = x;
this.y = y;
}
public int X
{
get { return x; }
}
public int Y
{
get { return y; }
}
}
In such scenario you can pass it like this:
var c = new Coords(1, 2);
MyMethod(c);
You have an optimization tag attached to your question, but if the problem is not critical to your application's performance, I'd go with readability/design over nanoseconds.
It kinda depens on what you're using the values for.
If you're holding a ton of values, like in a game, you should make it easily readable for yourself and maybe other coders and clarify what a value means.
You shouldn't hold many values in one array, like say HP, MP, speed, rotation, height, width,... and not clarifying what they are.
But you should say HP=100; MP=80, ...
In cases like this, there's almost everytime a 'player' class though.
That class contains player.hitpoints, player.magicpoints, player.speed, ...
But for coordinates, what I think programmers use is an array with x, y (and sometime z) coordinates.
I have a class named serializableVector2:
[Serializable]
class serializableVector2
{
public float x, y;
public serializableVector2(int x, int y)
{
this.x = x;
this.y = y;
}
}
and I have a struct named savedMapTile:
[Serializable]
struct savedMapTile
{
public oreInstance ore;
public int backgroundTileId;
public int playerId;
public tree tree;
}
and I have a dictionary using these two classes:
[SerializeField]
Dictionary<serializableVector2, savedMapTile> savedTiles;
I am trying to load this dictionary modify it, and then save it again all using serialization.
I am deserializing the dictionary like so:
FileStream f = File.Open(saveFileName, FileMode.Open);
BinaryFormatter b = new BinaryFormatter();
savedTiles = (Dictionary<serializableVector2, savedMapTile>)b.Deserialize(f);
f.Close();
and I am serializing it like so:
FileStream f = File.Open(saveFileName, FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(f, savedTiles);
f.Close();
However, when I try to access an element in the dictionary that I know should exist I get the following error:
System.Collections.Generic.KeyNotFoundException: The given key was not
present in the dictionary.
I get this error from running this code:
id = (savedTiles[new serializableVector2(-19,13)].backgroundTileId);
What I find really strange is that I am able to print out the entirety of the dictionaries keys and its values as well. This is where I am getting the values -19 and 13 for the Vector2. I print the keys and values like so:
for (int i = 0; i < 100; i++ )
{
UnityEngine.Debug.Log(vv[i].x +" "+vv[i].y);
UnityEngine.Debug.Log(x[i].backgroundTileId);
}
At this point I'm really stumped, I have no clue what is going on. I can see the file being saved in windows explorer, I can access keys and values in the dictionary, but I cant seem to use it properly. It is also important to note that when I use the .Contains() method on the dictionary in a similar way to how I am trying to access a value, it always returns false.
This is for a Unity 5 project, using C# in visual studio running on windows 8.1.
Change your serializableVector2 from a class to a struct and you should be able to find things in your dictionary. Someone may correct me if I have this wrong, but to the best of my knowledge the Dictionary is going to call GetHashCode on the key and use that code to store the item in the dictionary. If you create two instances of your class with the same x and y coordinates and call GetHashCode you will see that two instances yield different hash codes. If you change it to a struct than they will produce the same hash code. I believe this is what is causing you to get the "Key not found" issues. On a somewhat related note, it does seem strange that the constructor takes int for the x and y and then stores them as floats. You may want to consider changing the constructor to take float.
[Serializable]
struct serializableVector2
{
public float x, y;
public serializableVector2(float x, float y)
{
this.x = x;
this.y = y;
}
}
You have two issues:
Your dictionary key serializableVector2 is a class relying on the default equality and hashing methods. The defaults use reference equality such that only variables pointing to the same object will be equal and return the same hash.
If that were not the case you would still be relying on floating point equality. Unless your serialised can guarantee precise storage and retrieval of floating point values the deserialised serializableVector2 may NOT be equal to the original.
Suggested solution:
Override GetHashCode and Equals for your serializableVector2 class. When performing comparisons and hashing round your floats to within 32-bit floating point precision of your expected range of values. You can rely on 6+ significant digits of precision (within the same range) so if your world is += 1000 units I believe you can safely round to 3 decimal points.
Example for GetHashCode (without testing):
public override int GetHashCode() {
return Math.Round(x,3).GetHashCode() ^ Math.Round(y,3).GetHashCode();
}
Consider the TranslateAllCoords static function:
static class CoordinateTransformation
{
public static void TranslateAllCoords(ref int x, ref int y, ref int z,
int amount)
{
x+=amount;
y+=amount;
z+=amount;
}
}
Then, later in code, you have:
int x=0, y=0, z=0;
...
CoordinateTransformation.TranslateAllCoords(ref x, ref y, ref z, 5);
...
But, by calling TranslateAllCoords you are in effect modifying value types (i.e., the integer coords) and generally values types should be immutable. Are some rules broken here or is this a perfectly valid construct that gets around the "value types should be immutable" construct by modifying only built in value types?
The values are immutable. The variables that contain the value types are mutable. Variables vary, that's why they're called "variables".
The design guidance that value types should be immutable is essentially saying that you should not try to change only part of a variable. When you say
struct Point { public int X; public int Y; public int Z; }
...
Point p = new Point();
p.X = 123;
then what you are saying is "mutate only part of the variable p". That is confusing. The variable p should logically represent a point, and a point is a value. If you want to vary p, then logically vary the whole thing by assigning a new point to it. Don't mutate one point into another.
But even if we made point immutable:
struct Point { public int X { get; private set; } ... etc }
then a variable of that type can still vary!
Point p = new Point(123, 456, 789);
p = new Point(100, 200, 300);
But now it is clear that the entire variable is changing to a new point, rather than us trying to mutate a particular portion of the variable.
With an immutable value type you can then do your translation more sensibly:
static Point Translate(Point p, int offset)
{
return new Point(p.X + offset, p.Y + offset, p.Z + offset);
}
...
Point p = new Point(100, 200, 300);
p = Translate(p, 5);
See, again, p mutates, but it mutates all at once, not in little bits at a time.
No rules are broken there. You're simply creating a new integer value and reassigning the variable containing them.