c# - LINQ to Objects 缓慢。现在我完全糊涂了

标签 c# linq generics

好的,以我之前的问题为基础: Generically checking for null that won't box nullables on a non-constrained type.

一位用户建议为类和结构设置约束,我还实现了 UnboxT专门针对这三种类型并将该逻辑存储在委托(delegate)中的模式。我还被告知尝试使用 OfType<T> .

这是我的方法:

public static class Check<T>
{
    public static readonly Func<T,bool> IfNull = CreateIfNullDelegate();
    private static bool AlwaysFalse(T obj)
    {
        return false;
    }

    private static bool ForRefType(T obj)
    {
        return object.ReferenceEquals(obj, null);
    }

    private static bool ForNullable<Tu>(Tu? obj) where Tu:struct
    {
        return !obj.HasValue;
    }

    private static Func<T,bool> CreateIfNullDelegate()
    {
        if (!typeof(T).IsValueType)
            return ForRefType;
        else
        {
            Type underlying;
            if ((underlying = Nullable.GetUnderlyingType(typeof(T))) != null)
            {
                return Delegate.CreateDelegate(
                    typeof(Func<T,bool>),
                    typeof(Check<T>)
                     .GetMethod("ForNullable",BindingFlags.NonPublic | BindingFlags.Static)
                      .MakeGenericMethod(underlying)
                ) as Func<T,bool>;
            }
            else
            {
                return AlwaysFalse;
            }
        }
    }
}
public static int CountNonNull<T>(this IEnumerable<T> enumerable) where T:class
{
    return enumerable.Count(x=>Object.ReferenceEquals(x,null));
}

public static int CountNonNull<T>(this IEnumerable<T?> enumerable) where T : struct
{
    return enumerable.Count(x=>x!=null);
}

public static int CountNonNull3<T>(this IEnumerable<T> enumerable)
{
    return enumerable.OfType<T>().Count();
}

public static int CountNonNull2<T>(this IEnumerable<T> enumerable)
{
    return enumerable.Count(Check<T>.IfNull);
}
public static void Profile(this Action action, string name, int times = 1 * 1000 * 1000, bool display = true)
{
    for (int i = 0; i < 100; i++)
    {
        action();
    }
    var _stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < times; i++)
    {
        action();
    }
    _stopwatch.Stop();
    if (display)
    {
        Console.WriteLine("{0}: did {1} iterations in {2} ms", name, times, _stopwatch.ElapsedMilliseconds);
    }
}

这是我的测试集:

var ints = Enumerable.Range(0,10).ToArray();
var nullableInts = Array.ConvertAll(ints,x=>x as int?);
var strings = Array.ConvertAll(ints,x => x.ToString());
Profile(() => nullableInts.CountNonNull(), "int?");
Profile(() => strings.CountNonNull(), "strings");
Profile(() => nullableInts.CountNonNull2(), "int? 2");
Profile(() => strings.CountNonNull2(), "strings 2");
Profile(() => nullableInts.CountNonNull3(), "int? 3");
Profile(() => strings.CountNonNull3(), "strings 3");

这是我的结果:

int?: did 1000000 iterations in 188 ms
strings: did 1000000 iterations in 2346 ms
int? 2: did 1000000 iterations in 180 ms
strings 2: did 1000000 iterations in 147 ms
int? 3: did 1000000 iterations in 4120 ms
strings 3: did 1000000 iterations in 859 ms

OfType<T>慢是有道理的必须做一个 is然后是类型转换。这意味着它必须对集合进行两次循环才能确定其结果(尽管 int? 时间很难相信)。

但是第一种和第二种方法都执行相同的谓词。为什么第一个在弦乐上表现如此缓慢,而第二个却表现得像冠军?

编辑:额外的疯狂: 将另一种方法添加到试验中:

public static int CountNonNull4(this System.Collections.IEnumerable enumerable)
{
    return enumerable.Cast<object>().Count(x => object.ReferenceEquals(x, null));
}

这个版本产生:

strings 4: did 1000000 iterations in 677 ms

这几乎没有意义。这是怎么回事?

最佳答案

您知道 StopWatch 没有考虑实际的线程事件,对吗?这相当于你早上上下类的时间;有很多事情可能会阻碍你的进步,每天的数量会有所不同(你今天看到的一盏灯第二天就会让你停下来,交通堵塞等)。

这个类比在计算机中非常适用;操作系统可能会中断您的线程来执行其他操作,您的线程可能不得不等待页面文件操作(扩展、交换)等。尝试将每个算法运行 2 或 3 次并取平均次数。此外,请确保您的应用程序在 FullTrust 中运行,这会绕过所有安全(但不是运行时完整性)权限检查。最后,如果您能以某种方式将此探查器多线程化,您可以获得有关算法所需的 CPU 实际周期数的指标,这将独立于线程调度延迟。

关于c# - LINQ to Objects 缓慢。现在我完全糊涂了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4000178/

相关文章:

c# - 从 linq select 查询中读取结果

.net - LINQ - 根据列表中的索引返回平均值列表

具有多种类型的一个参数的C#泛型方法

c# - 输入保存受约束泛型类实例的变量

c# - 使用配置文件中的字符串来初始化泛型

c# - Asp.Net Owin 授权请求在隐身模式下工作,但不是 "normal"Chrome

c# - 带有 BackgroundTask 的 UWP 应用程序将不再构建错误 80080204

c# - 使用共享托管创建定时事件?

c# - 标签透明底色

c# - LINQ 的迭代器 block