.Max() vs OrderByDescending().First()

只谈情不闲聊 提交于 2019-12-04 02:17:28

If you are talking about straight LINQ to Objects, then no, it doesn't optimize for that.

Presumably another LINQ provider could do, but that's up to the particulars of the implementation.

For Enumerable, the implementations that Reflector gives me are:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    return new OrderedEnumerable<TSource, TKey>(source, keySelector, null, false);
}

and for First()

public static TSource First<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    IList<TSource> list = source as IList<TSource>;
    if (list != null)
    {
        if (list.Count > 0)
        {
            return list[0];
        }
    }
    else
    {
        using (IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                return enumerator.Current;
            }
        }
    }
    throw Error.NoElements();
}

Is LINQ and/or the compiler smart enough to figure out that it doesn't need to sort the entire enumerable and boils the code down to essentially the same as .Max()?

No.

Is there a quantifiable way to find out?

A simple benchmark with Stopwatch:

    var numbers = Enumerable.Range(1, 10000000);
    var sw = Stopwatch.StartNew();
    int max = numbers.Max();
    Console.WriteLine(sw.ElapsedMilliseconds);
    sw.Restart();
    int max2 = numbers.OrderByDescending(x => x).First();
    Console.WriteLine(sw.ElapsedMilliseconds);

Max() : 70ms

OrderBy() : 2066ms

Also, OrderBy() fails with an OutOfMemoryException if you increase the count too much beyond that, Max() doesn't.

.Max() is O(n), while your OrderByDescending solution isn't - depending on the sort, it's probably O(nlog(n)).

I obviously haven't dug inside the compiler to know, but what you're asking for (an optimization that realizes sort then grab only one item is the same as .max) is rather a lot from a compiler.

It appears that OrderedEnumerable is now smart enough to figure out that it doesn't need to sort the list for First and Last().

Note the code at around line 223, TryGetFirst() https://github.com/dotnet/corefx/blob/ed0ee133ac49cee86f10ca4692b1d72e337bc012/src/System.Linq/src/System/Linq/OrderedEnumerable.cs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!