Why is my IEnumerable <String> using the return income slower for iteration then List <String>

I tested the instruction yield return

with some code I was writing. I have two methods:

public static IEnumerable<String> MyYieldCollection {
        get 
        {
            wrapper.RunCommand("Fetch First From Water_Mains");
            for (int row = 0; row < tabinfo.GetNumberOfRows() ; row++) //GetNumberOfRows
                                                                      //will return 1000+ most of the time.
            {
                yield return wrapper.Evaluate("Water_Mains.col1");
                wrapper.RunCommand("Fetch Next From Water_Mains");
             }
        }
    }

      

and

public static List<String> MyListCollection
    {
        get
        {
            List<String> innerlist = new List<String>();

            wrapper.RunCommand("Fetch First From Water_Mains");
            for (int row = 0; row < tabinfo.GetNumberOfRows(); row++)
            {
                innerlist.Add(wrapper.Evaluate("Water_Mains.col1"));
                wrapper.RunCommand("Fetch Next From Water_Mains");
            }
            return innerlist;
        }
    }

      

then I use a loop foreach

for each collection:

        foreach (var item in MyYieldCollection) //Same thing for MyListCollection.
        {
            Console.WriteLine(item);
        }

      

It's funny that for some reason I feel like I can loop and print the full one MyListCollection

faster than MyYieldCollection.

Results:

  • MyYieldCollection → 2062
  • MyListCollection → 1847

I cannot understand the reason for this, am I missing something or is this normal?

0


source to share


4 answers


How did you make your timings? Are you in the debugger? In debug mode? It looks like you are using DataTable

, so I used your code as a template for a test setup (generated 1000 lines each time) and used the wiring as shown below in release mode on the command line ; the results were as follows (the number in brackets is a check to make sure they both do the same job):

Yield: 2000 (5000000)
List: 2100 (5000000)

      

Wire harness:

static  void Main()
{
    GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
    int count1 = 0;
    var watch1 = Stopwatch.StartNew();        
    for(int i = 0 ; i < 5000 ; i++) {
        foreach (var row in MyYieldCollection)
        {
            count1++;
        }
    }
    watch1.Stop();

    GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
    int count2 = 0;
    var watch2 = Stopwatch.StartNew();
    for (int i = 0; i < 5000; i++)
    {
        foreach (var row in MyListCollection)
        {
            count2++;
        }
    }
    watch1.Stop();

    Console.WriteLine("Yield: {0} ({1})", watch1.ElapsedMilliseconds, count1);
    Console.WriteLine("List: {0} ({1})", watch2.ElapsedMilliseconds, count2);
}

      



(note that shouldn't normally be used GC.Collect

, but it does use to align fields for performance tests)

The only other change I made was to loop for

to avoid repetition:

int rows = tabinfo.Rows.Count;
for (int row = 0; row < rows; row++) {...}

      

Therefore, I do not reproduce your numbers ...

+4


source


What happens if one iteration of your loop is expensive and you only need to iterate over multiple items in your collection?

With yield, you only need to pay for what you get;)



public IEnumerable<int> YieldInts()
{
    for (int i = 0; i < 1000; i++)
    {
        Thread.Sleep(1000) // or do some other work
        yield return i;
    }
}

public void Main()
{
    foreach(int i in YieldInts())
    {
        Console.WriteLine(i);
        if(i == 42)
        {
            break;
        }
    }
}

      

+1


source


My guess is that JIT can better optimize the for loop in the version that returns the list. In the version that returns IEnumerable, the string variable used in the for loop is now actually a member of the generated class, instead of a variable that is local only to the method.

The difference in speed is only about 10%, so unless this is a performance critical code I wouldn't bother with it.

0


source


As I understand it, "yield return" will keep looping until it runs our stuff and the function / property exits, returning a filled IEnumarable. In other words, instead of a function being called on each item in the foreach loop, it is called once and before execution is executed inside the foreach loop.

This can be the type of the collections returned. Perhaps List List can be iterated over faster than any IEnumerable data structure.

-1


source







All Articles