Where are static variables stored inside a non static method call? I.e. Inside CalculateTotalStatic()
, we are incrementing the static member MyStaticVariableHolder.Total
and are also comparing the variable i
with MyStaticVariableHolder.TotalArray.Length
within the for loop.
On the other hand, in another version of this method CalculateTotalLocal()
, we are using local variables declared within the method to perform both the above actions.
During CalculateTotalLocal
, there will be two additional variables placed on the stack which will hold their values on the stack itself (localTotal
and localLength
). What happens in the case of CalculateTotalStatic
? Does it access the static variables from the heap every time? Also, CalculateTotalLocal
is 10% faster than CalculateTotalStatic
. What is the correct reason for this performance improvement?
EDIT - I guess I'm not being very clear - apologies for that.
What I'm trying to say is:
can (according to C# spec) static variable access be optimized the same way as local variable by compliant C# compiler/JIT?
class MyStaticVariableHolder
{
public static int[] TotalArray = new int[100];
public static int Total = 1;
}
class Trial
{
public void CalculateTotalStatic()
{
for (int i = 0; i < MyStaticVariableHolder.TotalArray.Length; i++)
{
MyStaticVariableHolder.Total += i;
}
}
public void CalculateTotalLocal()
{
int localTotal = MyStaticVariableHolder.Total;
int[] localTotalArray = MyStaticVariableHolder.TotalArray;
int localLength = MyStaticVariableHolder.TotalArray.Length;
for (int i = 0; i < localLength; i++)
{
localTotal += i;
}
MyStaticVariableHolder.Total = localTotal;
}
}
I was also looking at this link - http://www.dotnetperls.com/local-variable-field-optimization for reference but I didnt achieve as much performance improvement as they were getting.
This is the boilerplate MSDN annotation about the thread-safety of a framework class:
Thread Safety
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Which in general is a load of horse-feathers and should not be assumed to be accurate, it is copy/paste documentation. There is however one guarantee you get for static variables, and the only reason this blurb is included, the .NET memory model does promise that assignments to such a variable can be visible to other threads.
This means that the jitter cannot optimize a read or write from such a variable. The underlying load or store instruction cannot be delayed and cannot be reordered. This does not come for free.
No such issue with a local variable, the jitter has a hard guarantee that this variable can never be visible to another thread. This permits the jitter to enregister the variable, storing it in a CPU register. The fastest kind of memory available on a processor.
Do keep in mind that the mental model is not correct, "there will be two additional variables placed on the stack" is not accurate. Both variables will in fact be stored in a CPU register when you run the Release build of your program without a debugger attached. The perf improvement you get is measurable in a test like this.
Storage for the static variable is normally allocated from the loader heap, an AppDomain implementation detail. This does not otherwise affect perf of the program, the jitter knows the exact address of the variable since it is the one that allocated the storage.
Where are static variables stored inside a non static method call?
I take it you mean: a value is fetched from a static variable in order to perform some computation on it. Therefore a copy is made of the value. To what storage is the copy made?
At the level of IL, it's made to the evaluation stack. The runtime has broad latitude to reify the evaluation stack however it likes. The stack of the current thread, or registers are both likely.
I note that the copy can be made to a heap location in some scenarios.
What happens in the case of CalculateTotalStatic? Does it access the static variables from the heap every time?
Wait, who said they were on the heap in the first place? There's no requirement that static variables be stored on the garbage collected heap. Their memory is not going to be collected, so why should they be on the heap? (The array that the variable refers to is on the heap of course.)
Let's rephrase.
What happens in the case of CalculateTotalStatic? Does it do a fresh access to the static variable every time?
The question still isn't answerable. Rephrase again.
Is the runtime required to do a fresh fetch from the variable every time?
No. The runtime is permitted to do any optimization whatsoever that would be invisible in a single-threaded program. (This is a slight over-statement; there are some optimizations it will not perform. I'm not going to list what they are.) Internalize that fact. In a single threaded program everything is stable unless made to move. In a multithreaded program, everything is moving unless made to stay still. The runtime is permitted to assume the former even in a world where the latter is true.
Also, CalculateTotalLocal is 10% faster than CalculateTotalStatic. What is the correct reason for this performance improvement?
I have no idea. Examine the generated code and see what the difference is.
can (according to C# spec) static variable access be optimized the same way as local variable by compliant C# compiler/JIT?
Absolutely yes. That falls squarely under the "any optimization that would be invisible in a single-threaded program".
Moreover: the runtime is not just permitted to reorder reads and writes as it sees fit. It is also permitted to allow different threads to observe an inconsistent world. If one thread observes a particular time sequence of variables being read and written, another thread is permitted to observe a completely different interleaving of the reads and writes.
Moreover: do not ever forget that by the runtime I mean also anything the runtime depends upon like, say, the CPU. Remember, CPUs are permitted broad latitude to reorder reads and writes as they see fit. Just because you are looking at some x86 code that plainly reads a location out of memory into a register, that has absolutely nothing whatsoever to do with the time at which the read actually goes to memory. That memory location might already be in the cache, and main memory might already have been written on another thread, effectively moving the read backwards in time.
Moreover: volatile does not necessarily help. For people who believe they can correct predict the behaviour of a program that has only volatile accesses to static variables on a strong memory model, I encourage you to read http://blog.coverity.com/2014/03/26/reordering-optimizations/ and see if you can correctly deduce the permitted sequences of reads and writes. Remember, this is on a strong memory model; now think about how much harder things might get on weak memory models!
You are in deep waters the moment you abandon standard patterns and practices for sharing memory across threads. Don't go there.
Unless your static
fields are declared as volatile
, the .NET JIT is free to do the same optimization you did, and it's becoming better at this with each major version, which would explain the difference from the somehow dated "Dot Net Perls" article.
My best guess at the static version 10% difference would be the static int[] TotalArray = new int[100]
initializer, which can and most probably does impose some overhead on the MyStaticVariableHolder static
member access to ensure it's run only once and only once before it's actually needed.