简体   繁体   English

即使此 Func<> 变量为空,是什么导致内存分配?

[英]What causes a memory allocation even when this Func<> variable is null?

Using BenchmarkDotNet's memory diagnoser, this code seems to allocate 12B even when called with parameters = null :使用 BenchmarkDotNet 的内存诊断程序,即使使用parameters = null调用此代码,它似乎也分配了 12B:

public static void Usage(this ILogger logger, LogLevel logLevel, string area, string operation, Dictionary<string, string> parameters)
{
    Func<Dictionary<string, string>> function = null;
    if (parameters != null)
    {
        function = new Func<Dictionary<string, string>>(() =>
        {
            return parameters;
        });
    }
    logger.Usage(logLevel, area, operation, function);
}

If I remove the assignment to function , the allocation drops to 0. When I look at the IL code I can see the following lines:如果我删除对function的分配,分配会下降到 0。当我查看 IL 代码时,我可以看到以下几行:

IL_0000: newobj instance void MIT.Logging.Infrastructure.LoggerExtensions/'<>c__DisplayClass0_0'::.ctor() IL_0000: newobj 实例无效 MIT.Logging.Infrastructure.LoggerExtensions/'<>c__DisplayClass0_0'::.ctor()

IL_001f: newobj instance void class [mscorlib]System.Func`1<class [mscorlib]System.Collections.Generic.Dictionary`2<string, string>>::.ctor(object, native int) IL_001f: newobj instance void class [mscorlib]System.Func`1<class [mscorlib]System.Collections.Generic.Dictionary`2<string, string>>::.ctor(object, native int)

The second line makes sense, it suppose to be conditional and in my specific case, the condition is NOT met.第二行是有道理的,它假设是有条件的,在我的具体情况下,条件不满足。 But the first one I can't explain.但第一个我无法解释。

This is the benchmark method:这是基准方法:

[Benchmark]
public void Log_WithInfra_ExtensionMethodDirect_NoParameters()
{
    LoggerExtensions.Usage(_logger, LogLevel.Information, LogAreas.MainApplication.AreaName, LogAreas.MainApplication.Operations.Operation0, null);
}

This is the benchmark results:这是基准测试结果:

Method方法 Mean意思是 Error错误 StdDev标准差 Gen 0 0代 Allocated已分配
Log_WithInfra_ExtensionMethodDirect_NoParameters Log_WithInfra_ExtensionMethodDirect_NoParameters 8.441 ns 8.441 纳秒 18.732 ns 18.732 纳秒 1.027 ns 1.027 纳秒 0.0023 0.0023 12 B 12 乙

It is stupid, it is not that important to my use case, but it drives me crazy.这很愚蠢,对我的用例来说并不重要,但它让我发疯。

The problem is that the scope of parameters extends beyond the scope of your conditional, so the closure that captures it is created at the top level.问题是parameters的范围超出了条件的范围,因此捕获它的闭包是在顶层创建的。

Simplifying your original code to this, for example:将您的原始代码简化为此,例如:

public static void Usage(string area, string operation, Dictionary<string, string> parameters)
{
    Func<Dictionary<string, string>> function = null;

    if (parameters != null)
    {
        function = new Func<Dictionary<string, string>>(() =>
        {
            return parameters;
        });
    }
}

... the C# 1.0 equivalent looks like this: ... C# 1.0 等效项如下所示:

   <>c__DisplayClass5_0 <>c__DisplayClass5_ = new <>c__DisplayClass5_0 ();
   <>c__DisplayClass5_.parameters = parameters;
   Func<Dictionary<string, string>> function = null;
   if (<>c__DisplayClass5_.parameters != null)
   {
        function = new Func<Dictionary<string, string>> (<>c__DisplayClass5_.<Usage>b__0);
   }

You can see that the closure is created, and its property is set, so that it can be used if needed anywhere in the function.您可以看到创建了闭包,并设置了它的属性,因此如果需要,可以在函数中的任何位置使用它。 To avoid this happening when your condition is not met, create a separate variable scoped locally to your if condition.为避免在您的条件不满足时发生这种情况,请创建一个单独的变量,其范围为您的if条件。

public static void Usage(string area, string operation, Dictionary<string, string> parameters)
{
    Func<Dictionary<string, string>> function = null;

    if (parameters != null)
    {
        var capturable = parameters;
        function = new Func<Dictionary<string, string>>(() =>
        {
            return capturable;
        });
    }
}

That changes your C# 1.0-equivalent code to:这会将您的 C# 1.0 等效代码更改为:

   Func<Dictionary<string, string>> function = null;
   if (parameters != null)
   {
        <>c__DisplayClass5_0 <>c__DisplayClass5_ = new <>c__DisplayClass5_0 ();
        <>c__DisplayClass5_.capturable = parameters;
        function = new Func<Dictionary<string, string>> (<>c__DisplayClass5_.<Usage>b__0);
   }

I've tried this code:我试过这段代码:

    public static void Usage(this ILogger logger, LogLevel logLevel, string area, string operation, Dictionary<string, string> parameters)
    {
        Func<Dictionary<string, string>> function = null;

        if (parameters == null)
        {
            var capturable = parameters;
            function = () => capturable;
        }

        logger.Usage(logLevel, area, operation, function);
    }

And it results in 44B of allocation.它会导致 44B 的分配。

What am i missing?我错过了什么?

Method方法 Mean意思是 Error错误 StdDev标准差 Gen 0 0代 Allocated已分配
Log_WithInfra_ExtensionMethodDirect_NoParameters Log_WithInfra_ExtensionMethodDirect_NoParameters 11.83 ns 11.83 纳秒 4.657 ns 4.657 纳秒 0.255 ns 0.255 纳秒 0.0084 0.0084 44 B 44乙

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM