Changing a method that has "return" and "yield return",

I know that it is impossible to use return and yield return in the same method.

This is the code I would like to optimize:

 public IEnumerable<TItem> GetItems(int data) { if (this.isSingleSet) { return this.singleSet; // is IEnumerable per-se } else { int index = this.GetSet(data); foreach(TKey key in this.keySets[index]) { yield return this.items[key]; } } } 

Important: I know that this code does not compile . This is the code I need to optimize.

There are two ways that I know for this method to work:

  • convert yield return part:

     ... else { int index = this.GetSet(data); return this.keySets[index].Select(key => this.items[key]); } 
  • convert return part:

     if (this.isSingleSet) { foreach(TItem item in this.singleSet) { yield return item; } } else ... 

But between them there is a big difference in speed. Using only return (in other words, using Select() ) is much slower (e.g., 6 times slower) yield return conversion.

Question

Is there another way that comes to your mind how to write this method? Do you have any other suggestions that will be useful for performance differences?

Additional Information

I measured the speed of two methods using a stopwatch around the for loop.

 Stopwatch s = new Stopwatch(); s.Start(); for(int i = 0; i < 1000000; i++) { GetItems(GetRandomData()).ToList(); } s.Stop(); Console.WriteLine(s.ElapsedMilliseconds); 

Each of these loops was performed in separate processes, so there could be no impact on garbage collection performance or anything else.

  • I ran the program with one version of the method, then
  • Closed
  • I rewrote the method and run it again.

This happened several times to see a reliable difference in performance ...

+6
source share
2 answers

Use two functions. An external function called by clients performs all non-blocking bits (for example, parameter checking) that you do not want to delay. A private worker makes lazy bits:

 public IEnumerable<TItem> GetItems(int data) { if (this.isSingleSet) { return this.singleSet; // is IEnumerable per-se } else { return DoGetItems(data); } } private IEnumerable<TItem> DoGetItems(int data) { int index = this.GetSet(data); foreach(TKey key in this.keySets[index]) { yield return this.items[key]; } } 
+15
source

Implementation of Select (removed with error removal):

 public static IEnumerable<R> Select<A, R>( this IEnumerable<A> sequence, Func<A, R> projection) { foreach(A item in sequence) yield return projection(item); } 

Thus, I hardly believe that your Select is much slower than the almost identical foreach loop you already have. It will be slowed down by performing error checking (once) and creating a delegate (once), and a small indirect overhead through the delegate. But loop equipment should be identical.

However, if I found out about one thing in performance analysis, I believe that my expectations are often erroneous. What does your profiling run mean is the bottleneck in your application? Let the reason be from facts, not from speculation here. What is a hot spot?

+5
source

Source: https://habr.com/ru/post/904765/


All Articles