foreach modifications #1404
Replies: 27 comments
-
This has already been proposed, see #461 and #1046.
This has also already been proposed, see #185. Though the syntax is different.
The compiler can do no such thing, at least in general, because it doesn't know the size of the array.
I would find it useful to be able to get the index when iterating an
Similarly, I think this use case is better served by using a |
Beta Was this translation helpful? Give feedback.
-
Sure it does. I tried the code that I suggest to be generated and i had exception when the length is out of bounds. By the way I ment the length not the end but I was sleapy then. Sorry 😉 About for loops: the IL optimization can't realize many cases where bound checks are not necessary. Using foreach is our way to tell the complliler to mark this code as fixed. It can convert it to for loop in unsafe fixed block. My goal is optimization not compact syntax this time. It can be used with larg arrays in criticl situations. I also asked many time to provide a class for array operations like vectoe class does. Its methods can be written with pointers to aggregate one array or do math operations on two arrays, so it will be faster. |
Beta Was this translation helpful? Give feedback.
-
@MohammadHamdyGhanem
That wasn't the compiler, that was the runtime. I think you meant to say, "Note that the compiler will generate a check that the given start and end are valid for the array." I don't see any need for it to do that, though. Slicing an array will already do a check in library code, and if it didn't, the runtime would step in. |
Beta Was this translation helpful? Give feedback.
-
@jnm2 |
Beta Was this translation helpful? Give feedback.
-
@MohammadHamdyGhanem
Except the compiler does no such thing. If you have an array, the IL for iterating it using I don't think it's a good idea to add features to the language that only make sense if the compiler does some thing, as long as no compiler does that thing. Maybe you should open an issue in the Roslyn repo asking for the compiler to behave this way? Though I'm not sure if you would have success with that issue, especially considering that using |
Beta Was this translation helpful? Give feedback.
-
It behaves like this, and eliminates the bounds checks in foreeach if c# was compiled to IL with the /optimize flag. We can ask for make this the default later. I read this info here, and then this proposal came ti me: |
Beta Was this translation helpful? Give feedback.
-
But, I also mentioned another way: |
Beta Was this translation helpful? Give feedback.
-
@MohammadHamdyGhanem You'll notice that the article talks about eliminating bounds checks both in |
Beta Was this translation helpful? Give feedback.
-
I read many articles about this. The Compiler leave out many obvious cases just to be careful. I.e, if you use any variable that carries the length of the array, the loop will not be optimized! This is the case also when using a sub range of the array! |
Beta Was this translation helpful? Give feedback.
-
The jit has no way of knowing which compiler generated the code. Nor can it just "trust" that the compiler did things correctly. What if the compiler did not? The jit must do things correctly first, looking for and exercising performance optimizations it can prove are sound to make. |
Beta Was this translation helpful? Give feedback.
-
The jit doesn't need the compiler to tell it this. The Jit can see this from its own analysis of the IL presented to it. Or, if it cannot tell, no amount of assertion from teh C# compiler will change that as the C# compiler itself cannot do things like see into the implementations of ref-assemblies and the like. |
Beta Was this translation helpful? Give feedback.
-
This (and a bunch of stuff that follows from it) is a bunch of nonsense.
Yeah. You can do that already: foreach (var x in new Span<int>(a, 4, 2))
{
…
}
Not sure what that means. If it means what I think it means then it's not an accurate description of what happens. |
Beta Was this translation helpful? Give feedback.
-
1- Is it difficult to mark IL code with some attribute, or put it in some block, to say to JIT: "don't optimize this"? Once again: the implementation is not the issue here. If we agreed on the concept, anything can be done. |
Beta Was this translation helpful? Give feedback.
-
None of this make any sense. The JIT compiler already eliminates range checks from loops produced by |
Beta Was this translation helpful? Give feedback.
-
I know about foreach, and said this in the first line of this proposal.
But foreach usage is limited, even with the use of span or ArraySegment. I Suggested to add new syntaxes to foreach to wide its use, so we can write more coplex for loops with it. I expect you can suggest more syntaxes to inculde more cases. |
Beta Was this translation helpful? Give feedback.
-
You suggested many things. Some things don't make sense, some things have already been suggested, some thing might be new/useful. You're also mixing convenience language features with optimization that might be better discussed in the context of JIT/IL optimizations. There were plenty of such discussion in the past and they always get shot down because ultimately it's not the job of the C# language compiler proper to make such optimizations. In short, it's difficult to make head or tail of this discussion. |
Beta Was this translation helpful? Give feedback.
-
Why you should throw all the load on the JIT optimization, while you can tell it from the beginnig that this is a safe for loop? We can writ a safe foreach code, C# compiler trnslates it to an already optimum IL, and JIT lives haply ever after :) |
Beta Was this translation helpful? Give feedback.
-
What load? Do you have any numbers to show that the cost of removing such trivial range checks is significant to the JIT? And why should anyone spend time implementing something that already works? Or why should the C# compiler suddenly start producing unsafe/unverifiable IL for code that's obviously safe/verifiable? Why should we ask the same thing from 3 languages when we could ask this from an IL optimizer that doesn't care about the language the code was originally written to? |
Beta Was this translation helpful? Give feedback.
-
Will JIT optimize this:
If not, I suggest this:
|
Beta Was this translation helpful? Give feedback.
-
@MohammadHamdyGhanem Randomly suggesting C# syntax doesn't help. The question is the IL and what the JIT does with it. Having to use a completely different syntax to somehow suggest to the JIT what to actually do seems pretty inappropriate. If there are common patterns that could be optimized by the JIT then that is (and should be) a CoreCLR request. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour |
Beta Was this translation helpful? Give feedback.
-
@MohammadHamdyGhanem Which is fine, but the criticism is part of that discussion. Enumeration is unidirectional. An |
Beta Was this translation helpful? Give feedback.
-
The JIT optimizes this using loop cloning. It's not great but there's little else that can be done for such code.
You can achieve the same effect (no range checks inside the loop) using this code: Span<int> sa = new Span<int>(a, 1, 3);
Span<int> sb = new Span<int>(b, 3, 3);
for (int i = 0; i < sa.Length; i++)
{
sa[i] = sb[i];
} Sure, it's kind of verbose. Some special language syntax for dealing with the case of iterating over "parallel" arrays might be worth considering. But mixing that with optimization issues is not a good idea. To be more precise:
|
Beta Was this translation helpful? Give feedback.
-
My honest reaction to this: foreach (ref int item1 in a<x, foo()-x>; int item2 in b<x+2> ) is that it's cryptic and visually complex, doesn't look like it tries to fit the style of anything in C#, and doesn't appeal to me. The |
Beta Was this translation helpful? Give feedback.
-
I'd say that this syntax is not very readable. Maybe it should be built on tuple (and slice) syntax: foreach ((ref int dst, int src) in (a[1..3], b[3..5]))
dst = src; |
Beta Was this translation helpful? Give feedback.
-
Thanks all for this useful discussion. |
Beta Was this translation helpful? Give feedback.
-
@mikedn A foreach loop enumerating two separate IEnumerables at the same time? I could get behind that! @MohammadHamdyGhanem foreach works on anything that has a specific method signature - IEnumerator GetEnumerator(). It does not care about the specific implementations of how that enumerator gets its items. The underlying functionality may not have a notion of "backwards;" for example, an enumerator that just generates random numbers. Making a foreachbackwards keyword would not be possible without fundamentally changing the meaning of foreach. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Using foreach grantees that the compiler will not add the array bounds check. But foreach used only to read array values, making it unsuitable for code that modifies thaw array.
So, I suggest to allow us to use ref with the foreach variable, to use it to modify the array like this:
also, Foreach will cover more ground if we can control the start and length.
which can be done like this:
Note that there is no risk if the start and length are not valid for the array, becuase they will be checked when the compiler defines the ArraySegment. I mean it ls safe becuase it will throw an exception if they are out of bound.
I also want a way to get the corresponding index for each item, without having to declare a variable and increase it.. This is useful in many cases such as a nested loop that started from the outer loop counter. Maybe:
which is the shortcut for:
It is important also to "for each" to iterate on two arrays:
This b array iteration can be done by using the MoveNext method inside the foreach loop of a array.
The compiler can generate additional check to ensure that b has 4 or more items staring from index 3.
Conclusion:
This way we can use foreach to handle most for loop cases, so we have a high level safe way to tell the compiler not to add bound checks.
IL optimization can't realize many cases of for ststments where bound checks are not necessary. Using foreach is our way to tell the complliler to mark this code as fixed. It can convert it to for loop in unsafe fixed block. My goal is optimization not compact syntax this time. It can be used with larg arrays in criticl situations. I also asked many time to provide a class for array operations like vectoe class does. Its methods can be written with pointers to aggregate one array or do math operations on two arrays, so it will be faster.
Beta Was this translation helpful? Give feedback.
All reactions