Я делал некоторые показатели производительности, и я столкнулся с чем-то, что кажется мне довольно странным. Я выполняю следующие две функции:
private static void DoOne()
{
List<int> A = new List<int>();
for (int i = 0; i < 200; i++) A.Add(i);
int s=0;
for (int j = 0; j < 100000; j++)
{
for (int c = 0; c < A.Count; c++) s += A[c];
}
}
private static void DoTwo()
{
List<int> A = new List<int>();
for (int i = 0; i < 200; i++) A.Add(i);
IList<int> L = A;
int s = 0;
for (int j = 0; j < 100000; j++)
{
for (int c = 0; c < L.Count; c++) s += L[c];
}
}
Даже при компиляции в режиме выпуска результаты таймингов последовательно показывали, что DoTwo занимает ~ 100 дольше, чем DoOne:
DoOne took 0.06171706 seconds.
DoTwo took 8.841709 seconds.
Учитывая тот факт, что Список непосредственно реализует IList, я был очень удивлен результатами. Может ли кто-нибудь прояснить это поведение?
Детали gory
Отвечая на вопросы, вот полный код и изображение настроек сборки проекта:
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<int> A = new List<int>();
for (int i = 0; i < 200; i++) A.Add(i);
int s=0;
for (int j = 0; j < 100000; j++)
{
for (int c = 0; c < A.Count; c++) s += A[c];
}
}
private static void DoTwo()
{
List<int> A = new List<int>();
for (int i = 0; i < 200; i++) A.Add(i);
IList<int> L = A;
int s = 0;
for (int j = 0; j < 100000; j++)
{
for (int c = 0; c < L.Count; c++) s += L[c];
}
}
}
}
Спасибо за все хорошие ответы (особенно @kentaromiura). Я бы закрыл вопрос, хотя я чувствую, что мы все еще пропускаем важную часть головоломки. Почему доступ к классу через интерфейс, который он реализует, будет намного медленнее? Единственное отличие, которое я вижу в том, что доступ к функции через интерфейс подразумевает использование виртуальных таблиц, в то время как обычно функции можно вызывать напрямую. Чтобы убедиться, что это так, я сделал пару изменений в вышеуказанном коде. Сначала я ввел два почти одинаковых класса:
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; } }
}
Как вы видите, VC использует виртуальные функции, а C - нет. Теперь DoOne и DoTwo:
private static void DoOne()
{ C a = new C();
int s=0;
for (int j = 0; j < 100000; j++)
{
for (int c = 0; c < a.Count; c++) s += a.f();
}
}
private static void DoTwo()
{
VC a = new VC();
int s = 0;
for (int j = 0; j < 100000; j++)
{
for (int c = 0; c < a.Count; c++) s += a.f();
}
}
И действительно:
DoOne took 0.01287789 seconds.
DoTwo took 8.982396 seconds.
Это еще более страшно - вызовы виртуальных функций в 800 раз медленнее? поэтому пара вопросов сообществу:
- Можете ли вы воспроизвести? (Учитывая факт, что у всех была худшая производительность раньше, но не так плохо, как у меня)
- Вы можете объяснить?
- (это может быть самое главное) - можете ли вы подумать о способ избежать?
Боаз