-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Yield Performance Improvement suggestion #112183
Comments
Although |
@huoyaoyuan I understand, but in the previous dotnet version there was a lot of work done in linq to hand-write iterators to improve performance of the different combinations. Maybe removing the state machine at the compiler level, and moving it to the IL / runtime could make that type of work obsolete since the Compiler / JIT could inline the logic of the chain between each yield to the caller (and maybe the loop body of the caller as well). |
I would expect: public IEnumerable<int> M() {
int cumSum = 0;
foreach (var n in Enumerable.Range(0, 100).Where(x => x % 3 != 0).Select(x => x * 2)) {
cumSum += n;
yield return cumSum;
}
} To generate the equivalent of: public IEnumerable<int> M() {
int cumSum = 0;
for (int i = 0; i < 100; i++) {
if (i %3 != 0) {
cumSum += i * 2;
yield return cumSum;
}
}
} |
For runtime-async we are optimizing based on the assumption that suspensions are infrequent. The trade off is making suspension/resumption cost higher in favor of making the cost of other cases smaller. |
I get it now, thank you. @jakobbotsch . Ignoring the specifics of the async machinery, moving the yield machinery to the IL level wouldn't allow the JIT to optimize iterator chains so effectively there would be only one Enumerator running with all the logic inlined? The above code I provided runs between 2 times and 3 times as fast for different sizes on dotnet 9: using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<Benchmarks>();
[MemoryDiagnoser]
public class Benchmarks
{
[Params(10, 1000, 1_000_000)] public int Size { get; set; }
[Benchmark]
public int[] CumSumLinq()
{
return CumSumLinqImp(Size).ToArray();
}
[Benchmark]
public int[] CumSumHandWritten()
{
return CumSumHandWrittenImp(Size).ToArray();
}
private static IEnumerable<int> CumSumLinqImp(int size)
{
int cumSum = 0;
foreach (int n in Enumerable.Range(0, size).Where(x => x % 3 != 0).Select(x => x * 2)) {
cumSum += n;
yield return cumSum;
}
}
private static IEnumerable<int> CumSumHandWrittenImp(int size)
{
int cumSum = 0;
for (int i = 0; i < size; i++)
{
if (i % 3 == 0) continue;
cumSum += i * 2;
yield return cumSum;
}
}
} BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5371/22H2/2022Update)
|
This falls under the topic about cost-free linq (dotnet/csharplang#2482). |
Is it possible to improve
yield
based methods using the same type of optimizations as async v2?Methods like:
generates a state machine that could make the compiler and JIT have a harder time to optimize the generated code (and maybe stored fields etc.).
This could make Linq based APIs automatically inline and remove the need for manually writing IEnumerables to make it more performant.
The text was updated successfully, but these errors were encountered: