通过lambda与单独方法的性能差异

时间:2016-05-24 19:58:52

标签: c# performance lambda

以下缓存助手采用密钥名称和创建者函数。如果缓存已经包含命名对象,则返回它,否则调用创建者来创建对象,将其粘贴在缓存中并返回新对象。

public static T GetObject<T>(String name, Func<T> creator) { ... }

显然,创建者函数将被调用一次(第一次),而其他999次将被忽略。

性能影响是什么?如果这只是一个方法指针,那么我认为这将是非常简单的 - 每次只是在堆栈上按指针 - 但是如果lambda很昂贵,可能是状态的闭包 - 每次调用时实际完成了多少工作在对象已经创建之后(在栈上推送lambda只是为了被忽略)。

对此有另一种策略吗?

1 个答案:

答案 0 :(得分:2)

让我们先给你的方法一个实现,然后我们就可以看看幕后发生了什么。

private static Dictionary<string, object> _LUT;

public static T GetObject<T>(String name, Func<T> creator)
{
    object obj;
    if (_LUT.TryGetValue(name, out obj))
    {
        return (T)obj;
    }
    T ret = creator();
    _LUT.Add(name, ret);
    return ret;
}

public string GetString(string name)
{
    return GetObject<string>(name, () => "Foo");
}

GetObject(...)的说明:

IL_0000: ldsfld System.Collections.Generic.Dictionary`2[System.String,System.Object] _LUT
IL_0005: ldarg.0
IL_0006: ldloca.s System.Object (0)
IL_0008: callvirt Boolean TryGetValue(System.String, System.Object ByRef)
IL_000d: brfalse.s IL_0016
IL_000f: ldloc.0
IL_0010: unbox.any T
IL_0015: ret
IL_0016: ldarg.1
IL_0017: callvirt T Invoke()
IL_001c: stloc.1
IL_001d: ldsfld System.Collections.Generic.Dictionary`2[System.String,System.Object] _LUT
IL_0022: ldarg.0
IL_0023: ldloc.1
IL_0024: box T
IL_0029: callvirt Void Add(System.String, System.Object)
IL_002e: ldloc.1
IL_002f: ret

GetString(...)的说明:

IL_0000: ldarg.1
IL_0001: ldsfld System.Func`1[System.String] CS$<>9__CachedAnonymousMethodDelegate1
IL_0006: brtrue.s IL_0019
IL_0008: ldnull
IL_0009: ldftn System.String <GetString>b__0()
IL_000f: newobj Void .ctor(System.Object, IntPtr)
IL_0014: stsfld System.Func`1[System.String] CS$<>9__CachedAnonymousMethodDelegate1
IL_0019: ldsfld System.Func`1[System.String] CS$<>9__CachedAnonymousMethodDelegate1
IL_001e: call System.String GetObject[String](System.String, System.Func`1[System.String])
IL_0023: ret

如您所见,您的lambda存储在System.Func1[System.String]

类型的静态字段中

通过引用(Why are delegates reference types?)传递给您的方法,并在需要时调用System.Func1[System.String].Invoke(),因此方法的大小应该没有区别。

关于您是否存在替代方案的问题:

如果这是一次性功能,您可以尝试使用System.Lazy,如果您正在寻找常规缓存支持,我建议您查看以下内容:.NET 4 Caching Support