浏览代码

Merge pull request #752 from akarnokd/MinMaxImprovements

Ix: Improve Min(), MinBy(), Max(), MaxBy() performance
Oren Novotny 7 年之前
父节点
当前提交
eb8bb89b9c

+ 47 - 0
Ix.NET/Source/Benchmarks.System.Interactive/MinMaxBenchmark.cs

@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using BenchmarkDotNet.Attributes;
+
+namespace Benchmarks.System.Interactive
+{
+    [MemoryDiagnoser]
+    public class MinMaxBenchmark
+    {
+        [Params(1, 10, 100, 1000, 10000, 100000, 1000000)]
+        public int N;
+        private int _store;
+        private IList<int> _listStore;
+
+        private readonly IComparer<int> _comparer = Comparer<int>.Default;
+
+        [Benchmark]
+        public void Min()
+        {
+            Volatile.Write(ref _store, Enumerable.Range(1, N).Min(_comparer));
+        }
+
+        [Benchmark]
+        public void MinBy()
+        {
+            Volatile.Write(ref _listStore, Enumerable.Range(1, N).MinBy(v => -v, _comparer));
+        }
+
+        [Benchmark]
+        public void Max()
+        {
+            Volatile.Write(ref _store, Enumerable.Range(1, N).Max(_comparer));
+        }
+
+        [Benchmark]
+        public void MaxBy()
+        {
+            Volatile.Write(ref _listStore, Enumerable.Range(1, N).MaxBy(v => -v, _comparer));
+        }
+    }
+}

+ 1 - 0
Ix.NET/Source/Benchmarks.System.Interactive/Program.cs

@@ -20,6 +20,7 @@ namespace Benchmarks.System.Interactive
                 typeof(BufferCountBenchmark),
                 typeof(IgnoreElementsBenchmark),
                 typeof(DeferBenchmark),
+                typeof(MinMaxBenchmark),
             });
 
             switcher.Run();

+ 41 - 6
Ix.NET/Source/System.Interactive/Max.cs

@@ -27,8 +27,7 @@ namespace System.Linq
                 throw new ArgumentNullException(nameof(comparer));
             }
 
-            return MaxBy(source, x => x, comparer)
-                .First();
+            return Extrema(source, x => x, comparer, 1);
         }
 
         /// <summary>
@@ -80,10 +79,46 @@ namespace System.Linq
                 throw new ArgumentNullException(nameof(comparer));
             }
 
-            return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
+            return ExtremaBy(source, keySelector, comparer, 1);
         }
 
-        private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
+        private static TSource Extrema<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> compare, int direction)
+        {
+            var result = default(TSource);
+
+            using (var e = source.GetEnumerator())
+            {
+                if (!e.MoveNext())
+                {
+                    throw new InvalidOperationException("Source sequence doesn't contain any elements.");
+                }
+
+                var current = e.Current;
+                var resKey = keySelector(current);
+                result = current;
+
+                while (e.MoveNext())
+                {
+                    var cur = e.Current;
+                    var key = keySelector(cur);
+
+                    var cmp = compare.Compare(key, resKey) * direction;
+                    if (cmp == 0)
+                    {
+                        result = cur;
+                    }
+                    else if (cmp > 0)
+                    {
+                        result = cur;
+                        resKey = key;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> compare, int direction)
         {
             var result = new List<TSource>();
 
@@ -103,7 +138,7 @@ namespace System.Linq
                     var cur = e.Current;
                     var key = keySelector(cur);
 
-                    var cmp = compare(key, resKey);
+                    var cmp = compare.Compare(key, resKey) * direction;
                     if (cmp == 0)
                     {
                         result.Add(cur);
@@ -122,4 +157,4 @@ namespace System.Linq
             return result;
         }
     }
-}
+}

+ 3 - 4
Ix.NET/Source/System.Interactive/Min.cs

@@ -27,8 +27,7 @@ namespace System.Linq
                 throw new ArgumentNullException(nameof(comparer));
             }
 
-            return MinBy(source, x => x, comparer)
-                .First();
+            return Extrema(source, x => x, comparer, -1);
         }
 
         /// <summary>
@@ -80,7 +79,7 @@ namespace System.Linq
                 throw new ArgumentNullException(nameof(comparer));
             }
 
-            return ExtremaBy(source, keySelector, (key, minValue) => -comparer.Compare(key, minValue));
+            return ExtremaBy(source, keySelector, comparer, -1);
         }
     }
-}
+}