简体   繁体   中英

How does linq concat work under the hood?

I am considering replacing code looking like this:

        foreach (var meshCut in meshCuts0)
        {
            ComputePolygons(meshCut, polygons);
        }
        foreach (var meshCut in meshCuts1)
        {
            ComputePolygons(meshCut, polygons);
        }

With linq looking like this:

        meshCuts0.Concat(meshCuts1).ForEach(m => ComputePolygons(m, polygons));

I do not know how linq is implemented so I am not sure about the performance consequences. I would appreciate some help please.

1)

Will Concat create a list copy or is it just an enumerator doing something like this:

    public static IEnumerable<T> Concat<T>(IEnumerable<T> a, IEnumerable<T> b)
    {
        foreach (var t in a)
        {
            yield return t;
        }
        foreach (var t in b)
        {
            yield return t;
        }
    }

2)

Will this be the same behaviour on Mono?

3)

Is there any reference explaining how linq api functions are implemented for the performance conscious?

thanks!

Edit

Ok there is no ForEach so just assume I also define something like this:

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach(T item in source)
        action(item);
}

The real question is whether Concat would be a costly overhead just for slimming down the code which thanks to the comments, I now understand it is not.

Edit 2

Ah, Jon Skeet suggests not adding ForEach... so I won't!

http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx

Firstly, your code would only work if you also introduce a ForEach extension method on IEnumerable<T> . I suggest you don't do that - see Eric Lippert's blog post for the reasons I agree with.

I suggest you write it as:

foreach (var meshCut in meshCuts0.Concat(meshCuts1))
{
    ComputePolygons(meshCut, polygons);
}

Concat performs fine - it just iterates over the first sequence then the second, yielding items as it goes. It doesn't buffer all the elements. There's a very slight performance penalty for the extra level of indirection you're adding, but that's about all.

I'd expect Mono to behave the same way - Concat is very simple. In fact, as Mono is open source, you can check it for yourself . (It may move over time, of course...)

A while ago I blogged about LINQ to Objects in detail, reimplementing the whole thing from scratch and documenting various aspects of its performance, including where there's room for improvement. See my Edulinq blog series for more details.

This is how linq concat works under the hood.

Microsoft .net framework 4.0:

public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) {
    if (first == null) throw Error.ArgumentNull("first");
    if (second == null) throw Error.ArgumentNull("second");
    return ConcatIterator<TSource>(first, second); 
}

static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second) { 
    foreach (TSource element in first) yield return element;
    foreach (TSource element in second) yield return element; 
}

Mono (source: https://github.com/mono/mono/blob/master/mcs/class/System.Core/System.Linq/Enumerable.cs#L584 )

public static IEnumerable<TSource> Concat<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
    Check.FirstAndSecond (first, second);

    return CreateConcatIterator (first, second);
}

static IEnumerable<TSource> CreateConcatIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second)
{
    foreach (TSource element in first)
        yield return element;
    foreach (TSource element in second)
        yield return element;
}

Specific answers:

  1. It's not creating any copy, it's just enumerating.

  2. Yes.

  3. For the performance conscious, the source code is the best.

  1. It works just like you have described it. You can download nice free tool named JetBrains dotPeek - .net decompiler. You can load every assembly, even standart assemblies to decompiler and get source code. If you look to assembly System.Core, namespace System.Linq, class Enumerable you can see this:

     public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) { if (first == null) throw Error.ArgumentNull("first"); if (second == null) throw Error.ArgumentNull("second"); return ConcatIterator<TSource>(first, second); } static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second) { foreach (TSource element in first) yield return element; foreach (TSource element in second) yield return element; } 

    Talking about LINQ - it's lazy and every linq extension method uses defered calculations. There is nice guy Jon Skeet, he have a blog. There many articles named "Reimplementing LINQ to Objects: Part " and all of them can be found by tag 'linq': link

  2. Mono is opensource project. You can found sources at the github . Class Enumerable can be founded here: link . And source code of Concat:

     public static IEnumerable<TSource> Concat<TSource> (this IEnumerable<TSource> first, IEnumerable<TSource> second) { Check.FirstAndSecond (first, second); return CreateConcatIterator (first, second); } static IEnumerable<TSource> CreateConcatIterator<TSource> (IEnumerable<TSource> first, IEnumerable<TSource> second) { foreach (TSource element in first) yield return element; foreach (TSource element in second) yield return element; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM