Skip to content
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

[Enhancement] Can these API's also work on IReadOnlyList<T> #15

Open
Smurf-IV opened this issue Jul 12, 2019 · 20 comments
Open

[Enhancement] Can these API's also work on IReadOnlyList<T> #15

Smurf-IV opened this issue Jul 12, 2019 · 20 comments

Comments

@Smurf-IV
Copy link

No description provided.

@jackmott
Copy link
Owner

I don't think there would be any performance difference since IEnumerable would still be happening behind the scenes.

@Smurf-IV
Copy link
Author

IList has a this[] accessor ?

@jackmott
Copy link
Owner

If you want, set up a little microbenchmark, doing sum or max or something with an Ilist with linq for using a for loop and the accessor.

If there is a substantial difference I could think about supporting IList.

@ndrwrbgs
Copy link

I took this up because I was interested in the profile. Of course the results will depend on the underlying implementation, but I was curious if the single interface call to this[] could outperform the double interface calls to MoveNext() and Current.

LegacyJIT Results:

BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.765 (1803/April2018Update/Redstone4)
Intel Core i7-6820HQ CPU 2.70GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
  [Host]   : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3416.0
  ShortRun : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3416.0

Job=ShortRun  IterationCount=3  LaunchCount=1
WarmupCount=3
Method Mean Error StdDev
EnumerateEnumerable 6.574 us 2.8566 us 0.1566 us
EnumerateList 2.528 us 0.8032 us 0.0440 us
SumEnumerable 6.758 us 0.6564 us 0.0360 us
SumList 3.221 us 1.0518 us 0.0577 us

RyuJIT x64 Results:

BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.765 (1803/April2018Update/Redstone4)
Intel Core i7-6820HQ CPU 2.70GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
  [Host]   : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3416.0
  ShortRun : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3416.0

Job=ShortRun  Jit=RyuJit  Platform=X64
IterationCount=3  LaunchCount=1  WarmupCount=3
Method Mean Error StdDev
EnumerateEnumerable 9.898 us 9.115 us 0.4996 us
EnumerateList 3.695 us 2.075 us 0.1137 us
SumEnumerable 7.720 us 2.943 us 0.1613 us
SumList 4.062 us 2.356 us 0.1291 us

Code, for validity testing:

(Requires benchmarkdotnet nuget package to be added)

namespace ListVsEnumerable
{
    using System.Collections.Generic;
    using System.Linq;

    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Configs;
    using BenchmarkDotNet.Jobs;
    using BenchmarkDotNet.Running;

    public class Program
    {
        private static void Main(string[] args)
        {
            BenchmarkRunner.Run<Program>(
                DefaultConfig.Instance
                    .With(Job.ShortRun));
//                    .With(Job.ShortRun.With(Jit.RyuJit).With(Platform.X64)));
        }

        private const int ListLength = 1000;

        private static readonly IList<int> list = Enumerable.Range(0, ListLength).ToList();

        [Benchmark]
        public void EnumerateEnumerable()
        {
            foreach (var item in list) { }
        }

        [Benchmark]
        public void EnumerateList()
        {
            int count = list.Count;
            for (int i = 0; i < count; i++)
            {
                var item = list[i];
            }
        }

        [Benchmark]
        public long SumEnumerable()
        {
            long sum = 0;
            foreach (var item in list)
            {
                sum += item;
            }

            return sum;
        }

        [Benchmark]
        public long SumList()
        {
            int count = list.Count;
            long sum = 0;
            for (int i = 0; i < count; i++)
            {
                var item = list[i];
                sum += item;
            }

            return sum;
        }
    }
}

Do note that this has to take advantage of the optimization of pulling .Count OUT of the loop to really be worth it; if you inline those int count's to the for, the results look like this

Method Mean Error StdDev
EnumerateEnumerable 7.223 us 4.011 us 0.2199 us
EnumerateList 5.485 us 7.374 us 0.4042 us
SumEnumerable 7.175 us 8.143 us 0.4464 us
SumList 5.707 us 6.125 us 0.3357 us

This slightly suggests that perhaps IEnumerable should have returned some sort of FP style Maybe<T> in one call to optimize :)

@Smurf-IV
Copy link
Author

Smurf-IV commented Jul 14, 2019

Do note that this has to take advantage of the optimization of pulling .Count OUT of the loop to really be worth it;

Note: this is what I have found in the past as well.
BUT: This contradicts it "https://blogs.msdn.microsoft.com/clrcodegeneration/2009/08/13/array-bounds-check-elimination-in-the-clr/"

@jackmott
Copy link
Owner

jackmott commented Jul 14, 2019

yeah the bounds check probably isn’t getting eliminated when working with an enunerable plus count has function call overhead here probably. its a misoptimization with arrays and lists but good with IList. kinda silly!

anyway this would be valuable to add support for, its easy work but a lot of it. if people want to call a file they want to tackle here to help out that would be great.

@ndrwrbgs
Copy link

BUT: This contradicts...

It doesn't contradict, since that's not on IList. The compiler only does this when the type can be KNOWN to be an array (this is why providing overrides for Arrays themselves can be insanely beneficial)

@Smurf-IV
Copy link
Author

Currently doing something for the SumF's

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Jul 31, 2019
- Add SumF API's to redirect IList's to their base types
@Smurf-IV
Copy link
Author

Currently doing AverageF's

@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 1, 2019

Currently doing AggregateF's
image

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 1, 2019
- Done for Aggregate
- Add All Span types
- Add ReadOnlySpan
- Reduce code in SumList
Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 1, 2019
- Fix copy paste error
- Add better intellisense text
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 2, 2019

First's then found this: #22

@Smurf-IV Smurf-IV changed the title [Enhancement] Can these API's also work on IList<T> [Enhancement] Can these API's also work on IReadOnlyList<T> Aug 5, 2019
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 5, 2019

Changed to IReadOnlyList, so that the caller has confidence that the members will "probably" not be changed using these calls.

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 5, 2019
…ReadOnlyList<T>

- Change over to IReadOnlyList
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 5, 2019

Doing LastF's

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 5, 2019
…dOnlyList<T>

- Implement LastF's the same way as FirstF's
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

Doing MaxF's

@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

Spans are still NOT Faster or fast enough!
image

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 6, 2019
…ReadOnlyList<T>

- Use types instead of vars
- Breakout Max Benchmarks
- Breakout MaxF API's for different types into seperate files
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

Doing MinF's

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 6, 2019
…ReadOnlyList<T>

- Split MinF's out into seperate files
- Change to NameOf for Selector parameter
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

MinF's Done:
image

@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

Doing WhereSumF's

@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

WhereSumF's Done.. Span is STILL a problem!
image

Smurf-IV pushed a commit to Smurf-IV/LinqFaster that referenced this issue Aug 6, 2019
… on IReadOnlyList<T>

- Replacement to use nameof for source
- Split more benchmarks out into seperate relevant files
@Smurf-IV
Copy link
Author

Smurf-IV commented Aug 6, 2019

yeah the bounds check probably isn’t getting eliminated when working with an enunerable plus count has function call overhead here probably. its a misoptimization with arrays and lists but good with IList. kinda silly!

    // Note: Lists can have items added and removed whilst these API's are in use
    // The IReadOnlyList<T> represents a list in which the _number_ and _order_ of list elements is read-only.
    //

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants