Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy does casting List<T> into IList<T> result in reduced performance?
    primarykey
    data
    text
    <p>I was doing some performance metrics and I ran into something that seems quite odd to me. I time the following two functions:</p> <pre><code> private static void DoOne() { List&lt;int&gt; A = new List&lt;int&gt;(); for (int i = 0; i &lt; 200; i++) A.Add(i); int s=0; for (int j = 0; j &lt; 100000; j++) { for (int c = 0; c &lt; A.Count; c++) s += A[c]; } } private static void DoTwo() { List&lt;int&gt; A = new List&lt;int&gt;(); for (int i = 0; i &lt; 200; i++) A.Add(i); IList&lt;int&gt; L = A; int s = 0; for (int j = 0; j &lt; 100000; j++) { for (int c = 0; c &lt; L.Count; c++) s += L[c]; } } </code></pre> <p>Even when compiling in release mode, the timings results were consistently showing that DoTwo takes ~100 longer then DoOne:</p> <pre><code> DoOne took 0.06171706 seconds. DoTwo took 8.841709 seconds. </code></pre> <p>Given the fact the List directly implements IList I was very surprised by the results. Can anyone clarify this behavior?</p> <h2>The gory details</h2> <p>Responding to questions, here is the full code and an image of the project build preferences:</p> <blockquote class="spoiler"> <p> <a href="http://img178.imageshack.us/img178/9456/projpref.tif" rel="nofollow noreferrer">Dead Image Link</a></p> </blockquote> <pre><code>using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Collections; namespace TimingTests { class Program { static void Main(string[] args) { Stopwatch SW = new Stopwatch(); SW.Start(); DoOne(); SW.Stop(); Console.WriteLine(" DoOne took {0} seconds.", ((float)SW.ElapsedTicks) / Stopwatch.Frequency); SW.Reset(); SW.Start(); DoTwo(); SW.Stop(); Console.WriteLine(" DoTwo took {0} seconds.", ((float)SW.ElapsedTicks) / Stopwatch.Frequency); } private static void DoOne() { List&lt;int&gt; A = new List&lt;int&gt;(); for (int i = 0; i &lt; 200; i++) A.Add(i); int s=0; for (int j = 0; j &lt; 100000; j++) { for (int c = 0; c &lt; A.Count; c++) s += A[c]; } } private static void DoTwo() { List&lt;int&gt; A = new List&lt;int&gt;(); for (int i = 0; i &lt; 200; i++) A.Add(i); IList&lt;int&gt; L = A; int s = 0; for (int j = 0; j &lt; 100000; j++) { for (int c = 0; c &lt; L.Count; c++) s += L[c]; } } } } </code></pre> <p>Thanks for all the good answers (especially @kentaromiura). I would have closed the question, though I feel we still miss an important part of the puzzle. Why would accessing a class via an interface it implements be so much slower? The only difference I can see is that accessing a function via an Interface implies using virtual tables while the normally the functions can be called directly. To see whether this is the case I made a couple of changes to the above code. First I introduced two almost identical classes:</p> <pre><code> public class VC { virtual public int f() { return 2; } virtual public int Count { get { return 200; } } } public class C { public int f() { return 2; } public int Count { get { return 200; } } } </code></pre> <p>As you can see VC is using virtual functions and C doesn't. Now to DoOne and DoTwo:</p> <pre><code> private static void DoOne() { C a = new C(); int s=0; for (int j = 0; j &lt; 100000; j++) { for (int c = 0; c &lt; a.Count; c++) s += a.f(); } } private static void DoTwo() { VC a = new VC(); int s = 0; for (int j = 0; j &lt; 100000; j++) { for (int c = 0; c &lt; a.Count; c++) s += a.f(); } } </code></pre> <p>And indeed:</p> <pre><code>DoOne took 0.01287789 seconds. DoTwo took 8.982396 seconds. </code></pre> <p>This is even more scary - virtual function calls 800 times slower?? so a couple of question to the community:</p> <ol> <li>Can you reproduce? (given the fact that all had worse performance before, but not as bad as mine)</li> <li>Can you explain? </li> <li>(this may be the most important) - can you think of a way to avoid?</li> </ol> <p>Boaz</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload