Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ Why is "Enumerable = Enumerable.Skip(N)" slow?

I am having an issue with the performance of a LINQ query and so I created a small simplified example to demonstrate the issue below. The code takes a random list of small integers and returns the list partitioned into several smaller lists each which totals 10 or less.

The problem is that (as I've written this) the code takes exponentially longer with N. This is only an O(N) problem. With N=2500, the code takes over 10 seconds to run on my pc.

I would appriciate greatly if someone could explain what is going on. Thanks, Mark.

int N = 250;
Random r = new Random();
var work = Enumerable.Range(1,N).Select(x => r.Next(0, 6)).ToList();
var chunks = new List<List<int>>();

// work.Dump("All the work.");  // LINQPad Print
var workEnumerable = work.AsEnumerable();

Stopwatch sw = Stopwatch.StartNew();
while(workEnumerable.Any())  // or .FirstorDefault() != null
{
    int soFar = 0;
    var chunk = workEnumerable.TakeWhile( x => 
                          {
                              soFar += x;               
                              return  (soFar <= 10);
                          }).ToList();
    chunks.Add(chunk);          // Commented out makes no difference.
    workEnumerable = workEnumerable.Skip(chunk.Count); // <== SUSPECT
}
sw.Stop();

// chunks.Dump("Work Chunks.");   // LINQPad Print
sw.Elapsed.Dump("Time elapsed.");
like image 203
Mark Avatar asked Nov 20 '12 22:11

Mark


People also ask

Is LINQ fast or slow?

LINQ is absolutely 100% slower You are going to essentially "stall-out" if you are performing any complex queries, joins etc...

Is LINQ slower than for loop?

Yes, it's slower.

What is LINQ enumerable?

Min(IEnumerable<Single>) Returns the minimum value in a sequence of Single values. Min<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) Invokes a transform function on each element of a generic sequence and returns the minimum resulting value. Min<TSource>(IEnumerable<TSource>)

What is skip in LINQ?

The Skip operator bypasses a specified number of contiguous rows from a sequence/table and returns the remaining table. It can skip rows from the top or can be for a certain criteria, in other words it can also skip rows depending on a certain criteria.


2 Answers

What .Skip() does is create a new IEnumerable that loops over the source, and only begins yielding results after the first N elements. You chain who knows how many of these after each other. Everytime you call .Any(), you need to loop over all the previously skipped elements again.

Generally speaking, it's a bad idea to set up very complicated operator chains in LINQ and enumerating them repeatedly. Also, since LINQ is a querying API, methods like Skip() are a bad choice when what you're trying to achieve amounts to modifying a data structure.

like image 138
millimoose Avatar answered Oct 14 '22 15:10

millimoose


You effectively keep chaining Skip() onto the same enumerable. In a list of 250, the last chunk will be created from a lazy enumerable with ~25 'Skip' enumerator classes on the front.

You would find things become a lot faster, already if you did

workEnumerable = workEnumerable.Skip(chunk.Count).ToList();

However, I think the whole approach could be altered.

How about using standard LINQ to achieve the same:

See it live on http://ideone.com/JIzpml

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    private readonly static Random r = new Random();

    public static void Main(string[] args)
    {
        int N = 250;
        var work = Enumerable.Range(1,N).Select(x => r.Next(0, 6)).ToList();

        var chunks = work.Select((o,i) => new { Index=i, Obj=o })
            .GroupBy(e => e.Index / 10)
            .Select(group => group.Select(e => e.Obj).ToList())
            .ToList();

        foreach(var chunk in chunks)
            Console.WriteLine("Chunk: {0}", string.Join(", ", chunk.Select(i => i.ToString()).ToArray()));
    }
}
like image 29
sehe Avatar answered Oct 14 '22 16:10

sehe