diff --git a/.gitignore b/.gitignore index 783b72ef..1bec825d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,10 @@ obj/ # Rider cache/options directory .idea/ -# TestResults +# Test Results [Tt]est[Rr]esults/ +# Benchmark Results +BenchmarkDotNet.Artifacts/ # NuGet Packages *.nupkg diff --git a/Benchmarks/SuperLinq.Benchmarks/.editorconfig b/Benchmarks/SuperLinq.Benchmarks/.editorconfig new file mode 100644 index 00000000..2f5d685d --- /dev/null +++ b/Benchmarks/SuperLinq.Benchmarks/.editorconfig @@ -0,0 +1,17 @@ +[*.cs] + +dotnet_diagnostic.CA1062.severity = none +dotnet_diagnostic.CA1515.severity = none +dotnet_diagnostic.CA1707.severity = none # CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1861.severity = none # CA1861: Avoid constant arrays as arguments +dotnet_diagnostic.CA2201.severity = none # CA2201: Do not raise reserved exception types + +dotnet_diagnostic.IDE0022.severity = none # IDE0022: Use expression body for methods + +# XML Documentation +dotnet_diagnostic.CS1573.severity = none # CS1573: Missing XML comment for parameter +dotnet_diagnostic.CS1591.severity = none # CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1712.severity = none # CS1712: Type parameter has no matching typeparam tag in the XML comment (but other type parameters do) + +dotnet_diagnostic.MA0046.severity = none +dotnet_diagnostic.MA0053.severity = none diff --git a/Benchmarks/SuperLinq.Benchmarks/LinqTestData.cs b/Benchmarks/SuperLinq.Benchmarks/LinqTestData.cs new file mode 100644 index 00000000..0e993462 --- /dev/null +++ b/Benchmarks/SuperLinq.Benchmarks/LinqTestData.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace SuperLinq.Benchmarks; + +[SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable")] +public class TestDataBenchmark +{ + public static IEnumerable NoSpecialization() + { + yield return LinqTestData.s_iEnumerable; + } + + public static IEnumerable CollectionSpecialization() + { + yield return LinqTestData.s_iEnumerable; + yield return LinqTestData.s_iCollection; + } + + public static IEnumerable CountSpecialization() + { + yield return LinqTestData.s_iEnumerable; + yield return LinqTestData.s_range; + yield return LinqTestData.s_iCollection; + } + + public static IEnumerable ListSpecialization() + { + yield return LinqTestData.s_iEnumerable; + yield return LinqTestData.s_array; + yield return LinqTestData.s_list; + yield return LinqTestData.s_iList; + } + + public static IEnumerable FullSpecialization() + { + yield return LinqTestData.s_iEnumerable; + yield return LinqTestData.s_iCollection; + yield return LinqTestData.s_array; + yield return LinqTestData.s_list; + yield return LinqTestData.s_iList; + } +} + +public sealed class LinqTestData +{ + // this field is a const (not instance field) to avoid creating closures in tested LINQ + internal const int Size = 100; + + private static readonly int[] s_arrayOf100Integers = Enumerable.Range(0, Size).ToArray(); + + internal static readonly LinqTestData s_array = new(s_arrayOf100Integers); + internal static readonly LinqTestData s_list = new(new List(s_arrayOf100Integers)); + internal static readonly LinqTestData s_range = new(Enumerable.Range(0, Size)); + internal static readonly LinqTestData s_iEnumerable = new(new IEnumerableWrapper(s_arrayOf100Integers)); + internal static readonly LinqTestData s_iList = new(new IListWrapper(s_arrayOf100Integers)); + internal static readonly LinqTestData s_iCollection = new(new ICollectionWrapper(s_arrayOf100Integers)); + internal static readonly LinqTestData s_iOrderedEnumerable = new(s_arrayOf100Integers.OrderBy(i => i)); // OrderBy() returns IOrderedEnumerable (OrderedEnumerable is internal) + + private LinqTestData(IEnumerable collection) => Collection = collection; + + internal IEnumerable Collection { get; } + + public override string ToString() + { + if (ReferenceEquals(this, s_range)) // RangeIterator is a private type + return "Range"; + + return Collection switch + { + int[] => "Array", + List => "List", + IList => "IList", + ICollection => "ICollection", + IOrderedEnumerable => "IOrderedEnumerable", + _ => "IEnumerable", + }; + } + + private sealed class IEnumerableWrapper(T[] array) : IEnumerable + { + public IEnumerator GetEnumerator() => ((IEnumerable)array).GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable)array).GetEnumerator(); + } + + private sealed class ICollectionWrapper(T[] array) : ICollection + { + private readonly T[] _array = array; + + public IEnumerator GetEnumerator() => ((IEnumerable)_array).GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable)_array).GetEnumerator(); + + public int Count => _array.Length; + public bool IsReadOnly => true; + public bool Contains(T item) => Array.IndexOf(_array, item) >= 0; + public void CopyTo(T[] array, int arrayIndex) => _array.CopyTo(array, arrayIndex); + + public void Add(T item) => throw new NotSupportedException(); + public void Clear() => throw new NotSupportedException(); + public bool Remove(T item) => throw new NotSupportedException(); + } + + private sealed class IListWrapper(T[] array) : IList + { + private readonly T[] _array = array; + + public IEnumerator GetEnumerator() => ((IEnumerable)_array).GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable)_array).GetEnumerator(); + + public int Count => _array.Length; + public bool IsReadOnly => true; + public T this[int index] + { + get => _array[index]; + set => throw new NotSupportedException(); + } + public bool Contains(T item) => Array.IndexOf(_array, item) >= 0; + public void CopyTo(T[] array, int arrayIndex) => _array.CopyTo(array, arrayIndex); + public int IndexOf(T item) => Array.IndexOf(_array, item); + + public void Add(T item) => throw new NotSupportedException(); + public void Clear() => throw new NotSupportedException(); + public bool Remove(T item) => throw new NotSupportedException(); + public void Insert(int index, T item) => throw new NotSupportedException(); + public void RemoveAt(int index) => throw new NotSupportedException(); + } +} diff --git a/Benchmarks/SuperLinq.Benchmarks/Program.cs b/Benchmarks/SuperLinq.Benchmarks/Program.cs new file mode 100644 index 00000000..0dc7cb8e --- /dev/null +++ b/Benchmarks/SuperLinq.Benchmarks/Program.cs @@ -0,0 +1,11 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Running; + +BenchmarkSwitcher + .FromAssembly(typeof(Program).Assembly) + .Run( + args, + DefaultConfig.Instance + .AddDiagnoser(MemoryDiagnoser.Default) + ); diff --git a/Benchmarks/SuperLinq.Benchmarks/SuperLinq.Benchmarks.csproj b/Benchmarks/SuperLinq.Benchmarks/SuperLinq.Benchmarks.csproj new file mode 100644 index 00000000..1528550d --- /dev/null +++ b/Benchmarks/SuperLinq.Benchmarks/SuperLinq.Benchmarks.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + + + + + + + + + + + + diff --git a/Benchmarks/SuperLinq.Benchmarks/Sync/ProjectionBenchmarks.cs b/Benchmarks/SuperLinq.Benchmarks/Sync/ProjectionBenchmarks.cs new file mode 100644 index 00000000..6e393df4 --- /dev/null +++ b/Benchmarks/SuperLinq.Benchmarks/Sync/ProjectionBenchmarks.cs @@ -0,0 +1,51 @@ +using BenchmarkDotNet.Attributes; + +namespace SuperLinq.Benchmarks.Sync; + +[BenchmarkCategory("Projection")] +public class ProjectionBenchmarks : TestDataBenchmark +{ + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void EquiZip(LinqTestData testData) => testData.Collection.EquiZip(testData.Collection); + + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void ZipShortest(LinqTestData testData) => testData.Collection.ZipShortest(testData.Collection); + + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void ZipLongest(LinqTestData testData) => testData.Collection.ZipLongest(testData.Collection); + + [Benchmark] + [ArgumentsSource(nameof(CountSpecialization))] + public void CountDown(LinqTestData testData) => testData.Collection.CountDown(5); + + [Benchmark] + [ArgumentsSource(nameof(CountSpecialization))] + public void TagFirstLast(LinqTestData testData) => testData.Collection.TagFirstLast(); + + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void Index(LinqTestData testData) => testData.Collection.Index(); + + [Benchmark] + [ArgumentsSource(nameof(NoSpecialization))] + public void IndexBy(LinqTestData testData) => testData.Collection.IndexBy(x => x % 10); + + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void Lag(LinqTestData testData) => testData.Collection.Lag(1); + + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void Lead(LinqTestData testData) => testData.Collection.Lead(1); + + [Benchmark] + [ArgumentsSource(nameof(NoSpecialization))] + public void Rank(LinqTestData testData) => testData.Collection.Rank(); + + [Benchmark] + [ArgumentsSource(nameof(ListSpecialization))] + public void ZipMap(LinqTestData testData) => testData.Collection.ZipMap(_ => true); +} diff --git a/Benchmarks/SuperLinq.Benchmarks/Sync/SortBenchmarks.cs b/Benchmarks/SuperLinq.Benchmarks/Sync/SortBenchmarks.cs new file mode 100644 index 00000000..48564390 --- /dev/null +++ b/Benchmarks/SuperLinq.Benchmarks/Sync/SortBenchmarks.cs @@ -0,0 +1,168 @@ +// Portions of this file are covered under the following license: +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; + +namespace SuperLinq.Benchmarks.Sync; + +internal sealed class FavourColourComparer + : IComparer +{ + public readonly static IComparer Instance = new FavourColourComparer(); + private static int TurnIntoValue(System.Drawing.Color x) => (x.R + x.G + x.B) * x.A; + public int Compare(System.Drawing.Color lhs, System.Drawing.Color rhs) => TurnIntoValue(lhs).CompareTo(TurnIntoValue(rhs)); +} + +// Bit of a mix of some value type fields and string +public sealed class Person +{ + public required string FirstName { get; set; } + public required string MiddleName { get; set; } + public required string LastName { get; set; } + + public string FullName => $"{LastName}, {FirstName} {MiddleName[0]}."; + + public DateTime DOB { get; set; } + public System.Drawing.Color FavouriteColour { get; set; } + public int IQ { get; set; } +} + +internal static class PersonData +{ + public static Person[] Generate(int count) + { + var r = new Random(42); // Same seed for consistent dataset + + var dataset = new Person[count]; + + for (var i = 0; i < count; ++i) + { + dataset[i] = new Person + { + FirstName = s_firstNames[r.Next(0, s_firstNames.Length)], + MiddleName = s_firstNames[r.Next(0, s_firstNames.Length)], + LastName = s_surnames[r.Next(0, s_surnames.Length)], + + DOB = new DateTime(1919, 1, 1).AddDays(r.Next(0, 100 * 365)), + FavouriteColour = System.Drawing.Color.FromArgb(r.Next(0, 256), r.Next(0, 256), r.Next(0, 256)), + IQ = 100 + r.Next(-30, 30), + }; + } + + return dataset; + } + + private static readonly string[] s_firstNames = + [ +"James", "John", "Robert", "Michael", "William", "David", "Richard", "Charles", "Joseph", "Thomas", "Christopher", "Daniel", "Paul", "Mark", "Donald", "George", "Kenneth", "Steven", "Edward", "Brian", +"Mary", "Patricia", "Linda", "Barbara", "Elizabeth", "Jennifer", "Maria", "Susan", "Margaret", "Dorothy", "Lisa", "Nancy", "Karen", "Betty", "Helen", "Sandra", "Donna", "Carol", "Ruth", "Sharon", "Michelle", +"Ronald", "Anthony", "Kevin", "Jason", "Matthew", "Gary", "Timothy", "Jose", "Larry", "Jeffrey", "Frank", "Scott", "Eric", "Stephen", "Andrew", "Raymond", "Gregory", "Joshua", "Jerry", "Dennis", "Walter", +"Laura", "Sarah", "Kimberly", "Deborah", "Jessica", "Shirley", "Cynthia", "Angela", "Melissa", "Brenda", "Amy", "Anna", "Rebecca", "Virginia", "Kathleen", "Pamela", "Martha", "Debra", "Amanda", "Stephanie", +"Patrick", "Peter", "Harold", "Douglas", "Henry", "Carl", "Arthur", "Ryan", "Roger", "Joe", "Juan", "Jack", "Albert", "Jonathan", "Justin", "Terry", "Gerald", "Keith", "Samuel", "Willie", "Ralph", "Lawrence", +"Carolyn", "Christine", "Marie", "Janet", "Catherine", "Frances", "Ann", "Joyce", "Diane", "Alice", "Julie", "Heather", "Teresa", "Doris", "Gloria", "Evelyn", "Jean", "Cheryl", "Mildred", "Katherine", "Joan", +"Nicholas", "Roy", "Benjamin", "Bruce", "Brandon", "Adam", "Harry", "Fred", "Wayne", "Billy", "Steve", "Louis", "Jeremy", "Aaron", "Randy", "Howard", "Eugene", "Carlos", "Russell", "Bobby", "Victor", "Martin", +"Ashley", "Judith", "Rose", "Janice", "Kelly", "Nicole", "Judy", "Christina", "Kathy", "Theresa", "Beverly", "Denise", "Tammy", "Irene", "Jane", "Lori", "Rachel", "Marilyn", "Andrea", "Kathryn", "Louise", "Sara", +"Ernest", "Phillip", "Todd", "Jesse", "Craig", "Alan", "Shawn", "Clarence", "Sean", "Philip", "Chris", "Johnny", "Earl", "Jimmy", "Antonio", "Danny", "Bryan", "Tony", "Luis", "Mike", "Stanley", "Leonard", +"Anne", "Jacqueline", "Wanda", "Bonnie", "Julia", "Ruby", "Lois", "Tina", "Phyllis", "Norma", "Paula", "Diana", "Annie", "Lillian", "Emily", "Robin", "Peggy", "Crystal", "Gladys", "Rita", "Dawn", "Connie", "Florence", +"Nathan", "Dale", "Manuel", "Rodney", "Curtis", "Norman", "Allen", "Marvin", "Vincent", "Glenn", "Jeffery", "Travis", "Jeff", "Chad", "Jacob", "Lee", "Melvin", "Alfred", "Kyle", "Francis", "Bradley", "Jesus", +"Tracy", "Edna", "Tiffany", "Carmen", "Rosa", "Cindy", "Grace", "Wendy", "Victoria", "Edith", "Kim", "Sherry", "Sylvia", "Josephine", "Thelma", "Shannon", "Sheila", "Ethel", "Ellen", "Elaine", "Marjorie", "Carrie", +"Herbert", "Frederick", "Ray", "Joel", "Edwin", "Don", "Eddie", "Ricky", "Troy", "Randall", "Barry", "Alexander", "Bernard", "Mario", "Leroy", "Francisco", "Marcus", "Micheal", "Theodore", "Clifford", "Miguel", +"Charlotte", "Monica", "Esther", "Pauline", "Emma", "Juanita", "Anita", "Rhonda", "Hazel", "Amber", "Eva", "Debbie", "April", "Leslie", "Clara", "Lucille", "Jamie", "Joanne", "Eleanor", "Valerie", "Danielle", "Megan", +"Oscar", "Jay", "Jim", "Tom", "Calvin", "Alex", "Jon", "Ronnie", "Bill", "Lloyd", "Tommy", "Leon", "Derek", "Warren", "Darrell", "Jerome", "Floyd", "Leo", "Alvin", "Tim", "Wesley", "Gordon", "Dean", "Greg", +"Alicia", "Suzanne", "Michele", "Gail", "Bertha", "Darlene", "Veronica", "Jill", "Erin", "Geraldine", "Lauren", "Cathy", "Joann", "Lorraine", "Lynn", "Sally", "Regina", "Erica", "Beatrice", "Dolores", "Bernice", +"Jorge", "Dustin", "Pedro", "Derrick", "Dan", "Lewis", "Zachary", "Corey", "Herman", "Maurice", "Vernon", "Roberto", "Clyde", "Glen", "Hector", "Shane", "Ricardo", "Sam", "Rick", "Lester", "Brent", "Ramon", +"Audrey", "Yvonne", "Annette", "June", "Samantha", "Marion", "Dana", "Stacy", "Ana", "Renee", "Ida", "Vivian", "Roberta", "Holly", "Brittany", "Melanie", "Loretta", "Yolanda", "Jeanette", "Laurie", "Katie", +"Charlie", "Tyler", "Gilbert", "Gene", "Marc", "Reginald", "Ruben", "Brett", "Angel", "Nathaniel", "Rafael", "Leslie", "Edgar", "Milton", "Raul", "Ben", "Chester", "Cecil", "Duane", "Franklin", "Andre", "Elmer", +"Kristen", "Vanessa", "Alma", "Sue", "Elsie", "Beth", "Jeanne", "Vicki", "Carla", "Tara", "Rosemary", "Eileen", "Terri", "Gertrude", "Lucy", "Tonya", "Ella", "Stacey", "Wilma", "Gina", "Kristin", "Jessie", +"Brad", "Gabriel", "Ron", "Mitchell", "Roland", "Arnold", "Harvey", "Jared", "Adrian", "Karl", "Cory", "Claude", "Erik", "Darryl", "Jamie", "Neil", "Jessie", "Christian", "Javier", "Fernando", "Clinton", "Ted", +"Natalie", "Agnes", "Vera", "Willie", "Charlene", "Bessie", "Delores", "Melinda", "Pearl", "Arlene", "Maureen", "Colleen", "Allison", "Tamara", "Joy", "Georgia", "Constance", "Lillie", "Claudia", "Jackie", "Marcia", +"Mathew", "Tyrone", "Darren", "Lonnie", "Lance", "Cody", "Julio", "Kelly", "Kurt", "Allan", "Nelson", "Guy", "Clayton", "Hugh", "Max", "Dwayne", "Dwight", "Armando", "Felix", "Jimmie", "Everett", "Jordan", +"Tanya", "Nellie", "Minnie", "Marlene", "Heidi", "Glenda", "Lydia", "Viola", "Courtney", "Marian", "Stella", "Caroline", "Dora", "Jo", "Vickie", "Mattie", "Terry", "Maxine", "Irma", "Mabel", "Marsha", "Myrtle", +"Ian", "Wallace", "Ken", "Bob", "Jaime", "Casey", "Alfredo", "Alberto", "Dave", "Ivan", "Johnnie", "Sidney", "Byron", "Julian", "Isaac", "Morris", "Clifton", "Willard", "Daryl", "Ross", "Virgil", "Andy", "Marshall", +"Lena", "Christy", "Deanna", "Patsy", "Hilda", "Gwendolyn", "Jennie", "Nora", "Margie", "Nina", "Cassandra", "Leah", "Penny", "Kay", "Priscilla", "Naomi", "Carole", "Brandy", "Olga", "Billie", "Dianne", "Tracey", "Leona", +"Salvador", "Perry", "Kirk", "Sergio", "Marion", "Tracy", "Seth", "Kent", "Terrance", "Rene", "Eduardo", "Terrence", "Enrique", "Freddie", "Wade", +"Jenny", "Felicia", "Sonia", "Miriam", "Velma", "Becky", "Bobbie", "Violet", "Kristina", "Toni", "Misty", "Mae", "Shelly", "Daisy", "Ramona", "Sherri", "Erika", "Katrina", "Claire", + ]; + + private static readonly string[] s_surnames = + [ +"Smith", "Johnson", "Williams", "Jones", "Brown", "Davis", "Miller", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", "Jackson", "White", "Harris", "Martin", "Thompson", "Garcia", "Martinez", "Robinson", "Clark", +"Rodriguez", "Lewis", "Lee", "Walker", "Hall", "Allen", "Young", "Hernandez", "King", "Wright", "Lopez", "Hill", "Scott", "Green", "Adams", "Baker", "Gonzalez", "Nelson", "Carter", "Mitchell", "Perez", "Roberts", +"Turner", "Phillips", "Campbell", "Parker", "Evans", "Edwards", "Collins", "Stewart", "Sanchez", "Morris", "Rogers", "Reed", "Cook", "Morgan", "Bell", "Murphy", "Bailey", "Rivera", "Cooper", "Richardson", "Cox", +"Howard", "Ward", "Torres", "Peterson", "Gray", "Ramirez", "James", "Watson", "Brooks", "Kelly", "Sanders", "Price", "Bennett", "Wood", "Barnes", "Ross", "Henderson", "Coleman", "Jenkins", "Perry", "Powell", +"Long", "Patterson", "Hughes", "Flores", "Washington", "Butler", "Simmons", "Foster", "Gonzales", "Bryant", "Alexander", "Russell", "Griffin", "Diaz", "Hayes", "Myers", "Ford", "Hamilton", "Graham", "Sullivan", +"Wallace", "Woods", "Cole", "West", "Jordan", "Owens", "Reynolds", "Fisher", "Ellis", "Harrison", "Gibson", "Mcdonald", "Cruz", "Marshall", "Ortiz", "Gomez", "Murray", "Freeman", "Wells", "Webb", "Simpson", "Stevens", +"Tucker", "Porter", "Hunter", "Hicks", "Crawford", "Henry", "Boyd", "Mason", "Morales", "Kennedy", "Warren", "Dixon", "Ramos", "Reyes", "Burns", "Gordon", "Shaw", "Holmes", "Rice", "Robertson", "Hunt", "Black", +"Daniels", "Palmer", "Mills", "Nichols", "Grant", "Knight", "Ferguson", "Rose", "Stone", "Hawkins", "Dunn", "Perkins", "Hudson", "Spencer", "Gardner", "Stephens", "Payne", "Pierce", "Berry", "Matthews", "Arnold", +"Wagner", "Willis", "Ray", "Watkins", "Olson", "Carroll", "Duncan", "Snyder", "Hart", "Cunningham", "Bradley", "Lane", "Andrews", "Ruiz", "Harper", "Fox", "Riley", "Armstrong", "Carpenter", "Weaver", "Greene", +"Lawrence", "Elliott", "Chavez", "Sims", "Austin", "Peters", "Kelley", "Franklin", "Lawson", "Fields", "Gutierrez", "Ryan", "Schmidt", "Carr", "Vasquez", "Castillo", "Wheeler", "Chapman", "Oliver", "Montgomery", +"Richards", "Williamson", "Johnston", "Banks", "Meyer", "Bishop", "Mccoy", "Howell", "Alvarez", "Morrison", "Hansen", "Fernandez", "Garza", "Harvey", "Little", "Burton", "Stanley", "Nguyen", "George", "Jacobs", +"Reid", "Kim", "Fuller", "Lynch", "Dean", "Gilbert", "Garrett", "Romero", "Welch", "Larson", "Frazier", "Burke", "Hanson", "Day", "Mendoza", "Moreno", "Bowman", "Medina", "Fowler", "Brewer", "Hoffman", "Carlson", +"Silva", "Pearson", "Holland", "Douglas", "Fleming", "Jensen", "Vargas", "Byrd", "Davidson", "Hopkins", "May", "Terry", "Herrera", "Wade", "Soto", "Walters", "Curtis", "Neal", "Caldwell", "Lowe", "Jennings", "Barnett", +"Graves", "Jimenez", "Horton", "Shelton", "Barrett", "Obrien", "Castro", "Sutton", "Gregory", "Mckinney", "Lucas", "Miles", "Craig", "Rodriquez", "Chambers", "Holt", "Lambert", "Fletcher", "Watts", "Bates", +"Hale", "Rhodes", "Pena", "Beck", "Newman", "Haynes", "Mcdaniel", "Mendez", "Bush", "Vaughn", "Parks", "Dawson", "Santiago", "Norris", "Hardy", "Love", "Steele", "Curry", "Powers", "Schultz", "Barker", "Guzman", +"Page", "Munoz", "Ball", "Keller", "Chandler", "Weber", "Leonard", "Walsh", "Lyons", "Ramsey", "Wolfe", "Schneider", "Mullins", "Benson", "Sharp", "Bowen", "Daniel", "Barber", "Cummings", "Hines", "Baldwin", +"Griffith", "Valdez", "Hubbard", "Salazar", "Reeves", "Warner", "Stevenson", "Burgess", "Santos", "Tate", "Cross", "Garner", "Mann", "Mack", "Moss", "Thornton", "Dennis", "Mcgee", "Farmer", "Delgado", "Aguilar", +"Vega", "Glover", "Manning", "Cohen", "Harmon", "Rodgers", "Robbins", "Newton", "Todd", "Blair", "Higgins", "Ingram", "Reese", "Cannon", "Strickland", "Townsend", "Potter", "Goodwin", "Walton", "Rowe", "Hampton", +"Ortega", "Patton", "Swanson", "Joseph", "Francis", "Goodman", "Maldonado", "Yates", "Becker", "Erickson", "Hodges", "Rios", "Conner", "Adkins", "Webster", "Norman", "Malone", "Hammond", "Flowers", "Cobb", "Moody", +"Quinn", "Blake", "Maxwell", "Pope", "Floyd", "Osborne", "Paul", "Mccarthy", "Guerrero", "Lindsey", "Estrada", "Sandoval", "Gibbs", "Tyler", "Gross", "Fitzgerald", "Stokes", "Doyle", "Sherman", "Saunders", "Wise", +"Colon", "Gill", "Alvarado", "Greer", "Padilla", "Simon", "Waters", "Nunez", "Ballard", "Schwartz", "Mcbride", "Houston", "Christensen", "Klein", "Pratt", "Briggs", "Parsons", "Mclaughlin", "Zimmerman", "French", +"Buchanan", "Moran", "Copeland", "Roy", "Pittman", "Brady", "Mccormick", "Holloway", "Brock", "Poole", "Frank", "Logan", "Owen", "Bass", "Marsh", "Drake", "Wong", "Jefferson", "Park", "Morton", "Abbott", "Sparks", +"Patrick", "Norton", "Huff", "Clayton", "Massey", "Lloyd", "Figueroa", "Carson", "Bowers", "Roberson", "Barton", "Tran", "Lamb", "Harrington", "Casey", "Boone", "Cortez", "Clarke", "Mathis", "Singleton", "Wilkins", +"Cain", "Bryan", "Underwood", "Hogan", "Mckenzie", "Collier", "Luna", "Phelps", "Mcguire", "Allison", "Bridges", "Wilkerson", "Nash", "Summers", "Atkins", "Wilcox", "Pitts", "Conley", "Marquez", "Burnett", "Richard", +"Cochran", "Chase", "Davenport", "Hood", "Gates", "Clay", "Ayala", "Sawyer", "Roman", "Vazquez", "Dickerson", "Hodge", "Acosta", "Flynn", "Espinoza", "Nicholson", "Monroe", "Wolf", "Morrow", "Kirk", "Randall", +"Anthony", "Whitaker", "Oconnor", "Skinner", "Ware", "Molina", "Kirby", "Huffman", "Bradford", "Charles", "Gilmore", "Dominguez", "Oneal", "Bruce", "Lang", "Combs", "Kramer", "Heath", "Hancock", "Gallagher", "Gaines", +"Shaffer", "Short", "Wiggins", "Mathews", "Mcclain", "Fischer", "Wall", "Small", "Melton", "Hensley", "Bond", "Dyer", "Cameron", "Grimes", "Contreras", "Christian", "Wyatt", "Baxter", "Snow", "Mosley", "Shepherd", +"Larsen", "Hoover", "Beasley", "Glenn", "Petersen", "Whitehead", "Meyers", "Keith", "Garrison", "Vincent", "Shields", "Horn", "Savage", "Olsen", "Schroeder", "Hartman", "Woodard", "Mueller", "Kemp", "Deleon", + ]; +} + +[BenchmarkCategory("Sort")] +public class TopNBenchmarks : TestDataBenchmark +{ + private readonly Consumer _consumer = new(); + + public const int NumberOfPeople = 512; + + [Params(1, 2, 5)] + public int TopN { get; set; } + + private Person[] _people = default!; + + [GlobalSetup] + public void GlobalSetup() => _people = PersonData.Generate(NumberOfPeople); + + [Benchmark] + public void PartialSortByString() => _people.PartialSortBy(TopN, p => p.FirstName).Consume(_consumer); + + [Benchmark] + public void PartialSortByValueType() => _people.PartialSortBy(TopN, p => p.DOB).Consume(_consumer); + + [Benchmark] + public void PartialSortByCustomComparer() => _people.PartialSortBy(TopN, p => p.FavouriteColour, FavourColourComparer.Instance).Consume(_consumer); + + [Benchmark] + [ArgumentsSource(nameof(NoSpecialization))] + public void RandomSubset(LinqTestData testData) + { + var rand = new Random(12345); + testData.Collection.RandomSubset(TopN, rand).Consume(_consumer); + } +} + +[BenchmarkCategory] +public class ShuffleBenchmark : TestDataBenchmark +{ + private readonly Consumer _consumer = new(); + + [Benchmark] + [ArgumentsSource(nameof(NoSpecialization))] + public void Shuffle(LinqTestData testData) + { + var rand = new Random(12345); + testData.Collection.Shuffle(rand).Consume(_consumer); + } +} diff --git a/Directory.Packages.props b/Directory.Packages.props index 2cdcf2f1..6effd273 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,6 +4,7 @@ + diff --git a/SuperLinq.sln b/SuperLinq.sln index a02f6b0e..3880fba1 100644 --- a/SuperLinq.sln +++ b/SuperLinq.sln @@ -43,6 +43,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{08F3AAFB-0876-4BB3-8BA8-935F1AFA1702}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperLinq.Benchmarks", "Benchmarks\SuperLinq.Benchmarks\SuperLinq.Benchmarks.csproj", "{383C2F63-E6D0-4EF6-A53F-017BBBE41701}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +81,10 @@ Global {33BCC44C-84B3-4C0A-9643-1530B5B65C11}.Debug|Any CPU.Build.0 = Debug|Any CPU {33BCC44C-84B3-4C0A-9643-1530B5B65C11}.Release|Any CPU.ActiveCfg = Release|Any CPU {33BCC44C-84B3-4C0A-9643-1530B5B65C11}.Release|Any CPU.Build.0 = Release|Any CPU + {383C2F63-E6D0-4EF6-A53F-017BBBE41701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {383C2F63-E6D0-4EF6-A53F-017BBBE41701}.Debug|Any CPU.Build.0 = Debug|Any CPU + {383C2F63-E6D0-4EF6-A53F-017BBBE41701}.Release|Any CPU.ActiveCfg = Release|Any CPU + {383C2F63-E6D0-4EF6-A53F-017BBBE41701}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -90,6 +98,7 @@ Global {B6503F7F-B3DE-47E5-922E-3A6108E75970} = {7805222E-6CD5-41FE-9945-0D9FD7DFA24C} {33BCC44C-84B3-4C0A-9643-1530B5B65C11} = {210A4922-6BBE-47D6-901E-38127E15C4C4} {30FDB542-4F2B-40E0-9FD9-2BCC8CA05009} = {835F8FFA-471F-4322-B721-A897F27872FA} + {383C2F63-E6D0-4EF6-A53F-017BBBE41701} = {08F3AAFB-0876-4BB3-8BA8-935F1AFA1702} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6A8EBCB0-B3EB-4D85-AD02-D349122C1D8E}