[英]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 ![]() |
Allocated![]() |
---|---|---|---|---|---|
Log_WithInfra_ExtensionMethodDirect_NoParameters ![]() |
8.441 ns ![]() |
18.732 ns ![]() |
1.027 ns ![]() |
0.0023 ![]() |
12 B ![]() |
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 ![]() |
Allocated![]() |
---|---|---|---|---|---|
Log_WithInfra_ExtensionMethodDirect_NoParameters ![]() |
11.83 ns ![]() |
4.657 ns ![]() |
0.255 ns ![]() |
0.0084 ![]() |
44 B ![]() |
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.