5 ms·
I'm not a C# developer, but this kind of thing seems to permeate almost all languages in one form or another. Maybe it is just a style and readability thing; o
by cr0sh 7y ago
I'm not a C# developer, but this kind of thing seems to permeate almost all languages in one form or another.
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...
- Someone 7y ago”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 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-count-down-than-it-is-to-count-up https://stackoverflow.com/questions/2823043/is-it-faster-to-..., which also shows how times have changed, with many answers calling this premature optimization.
- masklinn 7y agoWas also common in javascript a few years ago when the runtimes were more simplistic than they are now. 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.
- Sahhaese 7y agoIterating from the tail has other advantages, such as if you want to remove items from the list. With negative iteration you don't need to do any special handling. But that is an optimisation for development time rather than run-time, I'd imagine multiple in-place removals is not so efficient.
- mtVessel 7y agoThe difference is that the article is investigating using a property in the boundary check. The boundary check is evaluated with every iteration. Your example is only using it in the initialization expression which, by definition, is evaluated only once. This holds true for virtually any language with a for construct.
- duncanawoods 7y agoI wouldn't naturally think about summing lists in reverse so it becomes more cognitive effort to understand the code. I've seen plenty of termination condition bugs on reverse iterations that might back that up. 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.
- whoisthemachine 7y agoI personally find early returns to be easier to understand (I think of them as exit conditions) than nested if statements but to each their own.
- duncanawoods 7y agoThere are definitely cases like pre-conditions and validation that are natural early exists. There are other situations where it would sound really off and confusing to phrase instructions for a human that way. Not an easy task for an automatic code-clean-up tool to discern!
- usefulcat 7y agoIn the above example I tend to agree with you, but I'd definitely prefer early return over multiple levels of nested if statements unless all of the nested statements are quite small.
- darrenkopp 7y agoI feel like I saw something about how the JIT only checks the bounds once when going in reverse, but I can't find anything concrete to back that up, so we'll have to go with the 10 year old write-up for now saying: no, it is not optimized compared to the Length optimization. https://blogs.msdn.microsoft.com/clrcodegeneration/2009/08/13/array-bounds-check-elimination-in-the-clr/ https://blogs.msdn.microsoft.com/clrcodegeneration/2009/08/1...
- davemp 7y ago`i > -1` isn't quite right. I'm not sure about how C# does implicit casting, but arrays should be indexed by `usize` and comparing signed w/ unsigned is a bug waiting to happen or UB in other languages.
- louthy 7y agoWhilst I would personally use i >= 0 and find the use of -1 here slightly jarring, it is correct. .NET libraries usually use int even in places where uint would be best (like Array.Length) and indexers like this[int i] for Array. So, this will work fine. Well, it'd work fine anyway, because the negative value is never used, but yeah...
- davemp 7y agoYeah I meant it more as a bad habit, because when you move to other languages you'll have problems. In rust I don't think the style would even compile, and in C you're just playing with fire.