Should array length be stored into a local variable in C#?
habr.com
habr.com
No such guarantee exists.
The only guarantee there is is that enumeration will throw on next iteration after a modification.
The compiler can assume nothing.
That's an overstatement. That's currently impossible in C#, but Microsoft is working on it in F*: https://www.fstar-lang.org I hope some of that will trickle down to F# and C# some day.
The compiler can explore the call graph of the loop body and find out which methods might be called on the list, and then check that none of those methods write to the list's internal length variable.
How deeply and precisely the compiler (or JIT) analyzes the code in an attept to prove that the length won't change involves a tradeoff in compilation speed, but I'd expect .NET to have found a pretty good sweet spot by now.
For a single thread that should be doable. But that’s not mathematically covering all bases is it?
And how can the compiler know if the relevant list is not accessed from multiple threads? I’m pretty sure that’s next to impossible and definitely not worth the complexity for such a small optimisation.
Doing some quick googling, this article seems to explain things well from the perspective of a C# developer: https://afana.me/archive/2015/07/10/memory-barriers-in-dot-n...
For example, see https://shipilev.net/jvm/anatomy-quarks/19-lock-elision/.
This also can help when using java’s StringBuffer, which is thread-safe, but can often be ‘converted’ into a non-thread-safe variant by the compiler (the existence of StringBuilder shows that ‘often’ at least in some version of Java wasn’t often enough, though)
This is not true. Here's three examples:
A positive result from escape analysis and then scalar replacement of aggregates.
A positive result from partial escape analysis until after this point in the program.
Speculation that list is not modified, and deoptimisation if it is.
One nitpick about the title: C# runs on more runtimes than Microsoft .Net CLR, and those may behave very differently. For example: Mono CLR, or Unity's IL2CPP which is an ahead-of-time compiler.
Specifically, I'd expect IL2CPP would not hoist length out, because it would not recognize it as an invariant. (Some great examples of IL2CPP cross compilation are here: https://jacksondunstan.com/articles/4749 )
TLDR: the Microsoft JIT compiler makes the local variable unnecessary, but this is a property of the JIT, not of C#. Developers on non-MS platforms shouldn't assume this.
They shouldn’t behave any differently should they? There’s a single language spec.
Even better is to just avoid accessing the array's length. I almost always use foreach or Linq.
From the article:
> It also turned out that Foreach often walks through the array faster than For
Maybe it is just a style and readability thing; or maybe (as suggested elsewhere as well) it is meant to be reused elsewhere in the system, so it is cached in a variable for later use.
Or, it's possible that at one time - maybe early in the early days of .NET - doing it this way was more optimized, and the habit stuck with developers (perhaps they all read the same article in the knowledge base about it?). If that's the case, it's a bit of "premature optimization", but one that doesn't apparently harm anything.
What I do wonder is if certain other changes could change the speed?
At least it might be interesting to see in these trivial cases; I admit that in more complex loops it might not be advisable.
But - for instance, what if rather than iterating thru the array from the 0th element to the length of the array, you instead started from the last element and iterated backwards, until you hit zero? That way, you wouldn't be checking the length of the array, but rather for zero?
The code for such a test might look like:
public int WithoutVariable() {
int sum = 0;
for (int i = array.Length - 1; i > -1; i--) {
sum += array[i];
}
return sum;
}
I'm not sure that a "with variable" version would make much difference (or sense), but here it is for completeness sake: public int WithVariable() {
int sum = 0;
int length = array.Length - 1;
for (int i = length; i > -1; i--) {
sum += array[i];
}
return sum;
}
Again - I'm not a C# developer - maybe my code is wrong above, but hopefully it gets the idea across.Would this work better? Would it be faster? What would the JIT compiler create? Maybe it wouldn't be any faster or better than the ForEach examples?
I honestly don't know - but if anybody wants to give it a shot, I'd be curious as to the results...
EDIT: I noticed that I said "checking for zero" - but I modified my code to check for -1 as the boundary; I suppose the check in the loops could be modified to be "i == 0;" instead. I'm not sure if whether doing an "i >= 0;" vs "i == 0;" vs "i > -1;" which is faster - another thing to check, I suppose...
That used to be common in assembly, as it leads to smaller and faster code on many systems.
See https://stackoverflow.com/questions/2823043/is-it-faster-to-..., which also shows how times have changed, with many answers calling this premature optimization.
Iterating from the tail also made the terminal condition a comparison to 0, which IIRC was faster in at least one browser.
The gains were mostly masturbatory of course, the overhead of the iteration loop is not going to matter much when you have 5 method calls in the body.
A related thought is how modern code clean-up tools are doing things like reducing if-nesting e.g. turning this:
if (open)
{
stuff();
close();
}
into this, with early returns: if (!open) return;
stuff();
close();
My feeling is that the first more naturally represents the idea I have of the behaviour and the second, like your reverse iteration, is an encoded version of that idea. I feel I have to make an extra cognitive step decode and reassemble it to create an idea of the behaviour.I suspect the further you stray from the natural idea, the harder the code is to read, validate by eye and the more likely errors are to crop in. I'm don't know how subjective this is. I generally don't have a strong feeling about early returns it's just I have been noticing the slightly greater cognitive effort they are causing me compared to the logical chunking that nested-ifs provide.
It's an interesting analysis and all, but why bother when the language has such an elegant collections API.
Whenever I tried to find actual data for this Linq seemed to be slightly slower, but not too much.
Examples: https://codereview.stackexchange.com/questions/14197/is-the-... https://wheresmykeyboard.com/2015/06/linq-lambda-loop-perfor...
But something like:
var sum = values.Sum(x => x * x);
will take ~10x longer than an for or foreach loop equivalent.(260ms vs 36ms on 32 million float32s on my machine, measured by benchmarkdotnet)
plus a small allocation
I made some benchmarks myself: https://pastebin.com/5vQNpbPC
And esp. the Sum is a lot slower in linq. Not quite an order of magnitude, but pretty bad.
Even worse:
float sum = 0;
arr.Select(x => (sum += x * x)).ToList();
return sum;
This is somehow still a lot faster than the normal linq Sum. What does .Sum() do to be this slow?Edit: I just noticed you also wrote this blog post on the topic: https://jackmott.github.io/programming/2016/07/22/making-obv... I should have read that earlier!
var sum = arr.Aggregate(0, (t, x) => t + (x * x));
On the whole though it's better to use Linq until it's not. It's more declarative which will lead to more reliable code. Optimise when you find performance issues, don't write bad code just because you may gain a few nanoseconds here and there.For anyone that needs maximum performance then they should clearly be wary of anything that requires invocation of lambdas, but the usefulness of Linq shouldn't be written off because of that - the declarative style leads to fewer bugs, more stable, maintainable and easier to read code - that in itself is a performance boost.
If things are slow, instrument and investigate. Only bother fiddling with Array.length if profiling with production workloads exposes that as the bottleneck. More likely the problem will be elsewhere.
var widgetLength = widget.Length;
?Java supports this optimization for a wide range of loop types... since Java 7, I think. Normally I would argue, that Java is just ahead of curve, but Android has also gained supports for bounds check elimination in 2014-2015.
Either article does not tell us whole truth or Microsoft JIT is subpar by modern standards.