OfType(x)vs Where(_ => _是x)vs Where with枚举

时间:2018-07-01 11:55:56

标签: c#

我对这里的结果有些困惑,也许有人可以给我一些见解?

基本上,我正在尝试测试两次使用之间的性能

  • OfType(x)
  • Where(_ = _ is x).Select((X)x)
  • Where(_ = _.Type = type).Select((X)x)

以下是课程:

public enum AnimalTypes { Alligator, Bear, Cat, Dog, Elephant }

public interface IAnimal
{
    AnimalTypes Type { get; }
}

public class Bear : IAnimal
{
    public AnimalTypes Type => AnimalTypes.Bear;
}

public class Cat : IAnimal
{
    public AnimalTypes Type => AnimalTypes.Cat;
}

编辑:此代码已根据注释修复!对不起,这个错误 这是测试方法

void Main()
{
    List<IAnimal> animals = new List<IAnimal>();

    for (int i = 0; i < 100000; i++)
    {
        animals.Add(new Bear());
        animals.Add(new Cat());
    }


    // tests
    IEnumerable<Cat> test1 = animals.OfType<Cat>();
    IEnumerable<Cat> test2 = animals.Where(_ => _ is Cat).Select(_ => (Cat)_);
    IEnumerable<Cat> test3 = animals.Where(_ => _.Type == AnimalTypes.Cat).Select(_ => (Cat)_);

    Stopwatch sw = new Stopwatch();

    // OfType       
    sw.Start();
    test1.ToArray();
    sw.Stop();
    Console.WriteLine($"OfType = {sw.ElapsedTicks} ticks");
    sw.Reset();

    // Where (is) + Select
    sw.Start();
    test2.ToArray();
    sw.Stop();
    Console.WriteLine($"Where (is) + Select = {sw.ElapsedTicks} ticks");
    sw.Reset();

    // Where (enum) + Select
    sw.Start();
    test3.ToArray();
    sw.Stop();
    Console.WriteLine($"Where (type) + Select = {sw.ElapsedTicks} ticks");
    sw.Reset();
}

奇怪的是,结果始终确保最后一次测试获得最佳结果...

1 个答案:

答案 0 :(得分:4)

您的测试代码存在三个大问题:

  1. 您不是在测试实际的查询执行时间,而是在测量创建查询所花费的时间。
  2. 第一次测试是不公平的,因为您要增加程序集加载的开销。
  3. 您正在运行一次测试,这在测试性能时没有多大意义。

改为查看类似的内容:

var animals = new List<IAnimal>();

for (int i = 0; i < 1000000; i++)
{
    animals.Add(new Bear());
    animals.Add(new Cat());
}

// remove overhead of the first query
int catsCount = animals.Where(x => x == x).Count();

var whereIsTicks = new List<long>();
var whereTypeTicks = new List<long>();
var ofTypeTicks = new List<long>();

var sw = Stopwatch.StartNew();

// a performance test with a single pass doesn't make a lot of sense
for (int i = 0; i < 100; i++)
{
    sw.Restart();

    // Where (is) + Select
    catsCount = animals.Where(_ => _ is Cat).Select(_ => (Cat)_).Count();
    whereIsTicks.Add(sw.ElapsedTicks);

    // Where (enum) + Select
    sw.Restart();
    catsCount = animals.Where(_ => _.Type == AnimalTypes.Cat).Select(_ => (Cat)_).Count();
    whereTypeTicks.Add(sw.ElapsedTicks);

    // OfType
    sw.Restart();
    catsCount = animals.OfType<Cat>().Count();
    ofTypeTicks.Add(sw.ElapsedTicks);
}

sw.Stop();

// get the average run time for each test in an easy-to-print format
var results = new List<Tuple<string, double>>
{
    Tuple.Create("Where (is) + Select", whereIsTicks.Average()),
    Tuple.Create("Where (type) + Select", whereTypeTicks.Average()),
    Tuple.Create("OfType", ofTypeTicks.Average()),
};

// print results orderer by time taken
foreach (var result in results.OrderBy(x => x.Item2))
{
    Console.WriteLine($"{result.Item1} => {result.Item2}");
}

多次运行Where (is)可能比Where (type)快一点或慢一点,但是OfType始终是最慢的,而且有一定优势:

  1. i < 10

    Where (type) + Select => 111428.9
    Where (is) + Select => 132695.8
    OfType => 158220.7
    
  2. i < 100

    Where (is) + Select => 110541.8
    Where (type) + Select => 119822.74
    OfType => 150087.22
    
  3. i < 1000

    Where (type) + Select => 113196.381
    Where (is) + Select => 115656.695
    OfType => 160461.465
    

当您查看源代码for the OfType method时,OfType总是变慢的原因很明显:

static IEnumerable<TResult> OfTypeIterator<TResult>(IEnumerable source) 
{
    foreach (object obj in source) 
    {
        if (obj is TResult) 
        {
            yield return (TResult)obj;
        }
    }
}

如您所见,源项目使用is进行了类型检查,然后被投射回TResult。由于装箱,值类型的差异会更大。