Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Prefer using Array.Length as upper for loop limit#2513

Merged
jkotas merged 1 commit intodotnet:masterfrom
mikedn:array-length
Jan 15, 2017
Merged

Prefer using Array.Length as upper for loop limit#2513
jkotas merged 1 commit intodotnet:masterfrom
mikedn:array-length

Conversation

@mikedn
Copy link
Contributor

@mikedn mikedn commented Jan 15, 2017

@jkotas
Copy link
Member

jkotas commented Jan 15, 2017

Thanks!

@jkotas jkotas merged commit 63cc027 into dotnet:master Jan 15, 2017
@am11
Copy link
Member

am11 commented Jan 19, 2017

Just for a take-away, I have two questions (apologies if it is an improper place to ask):

Length caching before for loop is a noticeable practice (perhaps for micro-optimization of IL or some devs have this habit as some sort of good practice). Does that mean from JIT's perspective, not doing such optimization in user code is more profitable because it hinders JITs ability to perform range check elimination optimization? Is it not feasible for JIT to detect the collection length, perhaps only to the extent when it is defined/cached explicitly in the local scope of method?

For instance, recently this PR has brought similar caching of length for performance optimization: dotnet/corefxlab#1113.

@jkotas
Copy link
Member

jkotas commented Jan 19, 2017

Does that mean from JIT's perspective, not doing such optimization in user code is more profitable

Correct - for loops over arrays. Caching length may help slightly for loops over collections, but it is not 100% rule. You need to measure the concrete piece code to get the ultimate answer.

Is it not feasible for JIT to detect the collection length, perhaps only to the extent when it is defined/cached explicitly in the local scope of method

It is possible, but the current .NET JITs do not have this optimization.

In general, explicit caching is typically not worth it for state that is known to be immutable to the JIT. It is typically worth it for potentially mutable state, e.g.:

void MyMethod()
{
    // The JIT is unlikely to see that _buffer is not changing between loop iterations, so it 
    // will fetch _buffer.Length from memory in each loop iteration
    for (int i = 0; i < _buffer.Length; i++)
         DoStuff(_buffer[i]);
}

vs.

void MyMethod()
{
    // It is easy for JIT to see that buffer is not changing between loop iterations because
    // of buffer is cached in a local. The JIT will likely cache buffer.Length in a register, not fetch
    // it every iteration, and also eliminate bounds check for buffer[i]
    var buffer = _buffer; 
    for (int i = 0; i < buffer.Length; i++)
         DoStuff(buffer[i]);
}

@MichalStrehovsky
Copy link
Member

For instance, recently this PR has brought similar caching of length for performance optimization: dotnet/corefxlab#1113.

FWIW, the corefxlab change adds hoisting for iteration over Span and ReadOnlySpan (not array types). It's likely that JIT doesn't do range check optimization for those anyway (or at least not on all runtimes) and that's why it helps perf.

@jkotas
Copy link
Member

jkotas commented Jan 19, 2017

RyuJIT has several deficiencies wrt. Span codegen right now. The missing range check optimization is one of them. They will get fixed before Span<T> ships - folks are working on it.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants