通过将状态存储到有作用域的局部变量中来避免在快速路径上关闭 [英] Avoid closure on fast path by storing state into scoped locals

查看:30
本文介绍了通过将状态存储到有作用域的局部变量中来避免在快速路径上关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在分析 OptionsManager.Get(字符串名称) :

I'm analysing OptionsManager.Get(string name):

/// <summary>
/// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
/// </summary>
public virtual TOptions Get(string name)
{
    name = name ?? Options.DefaultName;

    if (!_cache.TryGetValue(name, out TOptions options))
    {
        // Store the options in our instance cache. Avoid closure on fast path by storing state into scoped locals.
        IOptionsFactory<TOptions> localFactory = _factory;
        string localName = name;
        options = _cache.GetOrAdd(name, () => localFactory.Create(localName));
    }

    return options;
}

以下内容实际上是什么意思?

What does the following actually mean?

通过将状态存储到作用域内的局部变量中,避免在快速路径上闭合

Avoid closure on fast path by storing state into scoped locals

什么是快速路径"?

与仅使用 _factory 相比,创建指向 _factory 的局部变量有什么好处?

What is the benefit of creating a local variable pointing to _factory, compared to just using _factory?

推荐答案

@madreflections提要基本上是正确的.

@madreflections synopsis is basically correct.

较长的故事

lambda只是一个匿名方法(反过来就是一个没有名称的方法),C#引入了 lamdas 作为创建 anonymous的一种简洁方法方法.但是,如果匿名方法需要访问自由变量(不属于匿名方法参数或局部范围的变量),则需要一种捕获它们的方法.这是通过 closure 完成的.

A lambda is just an anonymous method (which in turn is just a method without a name), C# introduced lamdas as a concise way of creating anonymous methods. However, if the anonymous method needs to access free variables (variables which are not part of the anonymous methods parameters or local scope) it needs a way to capture them. This is done via a closure.

闭包基本上只是编译器生成的类,用于获取自由变量的副本以通过匿名方法进行访问.

A closure is basically just a class the compiler generates to take a copy of free variables to access in the anonymous method.

注意:即使使用自由变量,编译器也可能不会始终生成闭包,有时,在引用实例字段或属性的情况下,编译器可能仅生成实例方法.但是,如果确实需要捕获变量,则会生成一个闭包.

Note : The compiler may not always generate a closure even with free variables, sometimes it can just generate an instance method in the case of referencing instance fields or properties. However, if it does need to capture a variable a closure is generated.

生成 closure 时,编译器不仅会为Closure类创建代码,还需要实例化该类并分配任何变量.根据这些变量的范围,将确定在代码中的哪个位置初始化类并探查相关性.

When a closure is generated the compiler not only creates the code for the closure class, it needs to instantiate that class and assign any variables. Depending on the scope of those variables a decision will be made to determine where in the code to initialize the class and plumb up the dependencies.

由于 closures 只是普通的旧类,因此需要实例化它们,这又是 allocation ,因此开销很小.在提供源代码的情况下,开发人员权衡了这些开销的成本,并决定提高效率.

Since closures are just plain old classes, they need to be instantiated which in turn is an allocation and as such comes with a small overhead. In the case of the source code supplied, the developers weighed up the cost of that overhead and decided to make a small efficiency.

知道闭包将在其需要" 以捕获自由变量的最外部实例化的事实而实现了这种效率..编译器根据范围使用 fall-back 方法,并且有点复杂.但是,通过以直接内部作用域中所示的方式分配 locals ,编译器知道在该作用域中创建闭包是安全的.这又意味着创建的少量开销仅限于 if 语句的代码块范围的那个分支.

This efficiency was made knowing the fact the closure will be instantiated at the outer most scope it "needs" to capture the free variables. The compiler uses fall-back approach based on scope and is a bit more complicated. However, by assigning locals in the way shown in the immediate inner scope, the compiler knows it's safe to create the closure in that scope. Which in turn means the small overhead of creation is limited to that branch of the code-block scope of the if statement.

一些荒谬的例子

// instance field
readonly Version _something = new Version();

public virtual void Test1(int someValue)
{ 
   // some weird branch
   if (DateTime.Now == DateTime.MaxValue)
   {
      Method(() => _something.Build + someValue);
   }
}

public virtual void Test2(int someValue)
{
   // some weird branch
   if (DateTime.Now == DateTime.MaxValue)
   {
      // some locals to let the compiler create the closure in the immediate scope
      var localSomething = _something;
      var localValue = someValue;
      Method(() => localSomething.Build + localValue);
   }
}

public virtual void Test3(int someValue)
{
   // some weird branch
   if (DateTime.Now == DateTime.MaxValue)
   {
      // closure needed, it can just create an anonymous method in the class
      // and reference _something directly
      Method(() => _something.Build);
   }
}

// something with a delegate parameter.
private int Method(Func<int> func) => func();

编译后

注意:这是编译器所做操作的粗略互操作.对于所有的血淋淋的细节外观这个例子

Note : This is rough interoperation of what the compiler has done. For all the gory details look at this Example

public class CallingClass
{
   [CompilerGenerated]
   private sealed class Closure1
   {
      public CallingClass copyOfCallingClass;

      public int someValue;

      internal int Method()
      {
         return copyOfCallingClass._something.Build + someValue;
      }
   }

   [CompilerGenerated]
   private sealed class Closure2
   {
      public Version localSomething;

      public int localValue;

      internal int Method()
      {
         return localSomething.Build + localValue;
      }
   }

   [CompilerGenerated]
   private int NewMethod()
   {
      return _something.Build;
   }

   private readonly Version _something = new Version();

   public virtual void Test1(int someValue)
   {
      // generated closure plumbing
      Closure1 Closure1 = new Closure1();
      Closure1.copyOfCallingClass = this;
      Closure1.someValue = someValue;
      if (DateTime.Now == DateTime.MaxValue)
      {
         Method(new Func<int>(Closure1.Method));
      }
   }

   public virtual void Test2(int someValue)
   {
      if (DateTime.Now == DateTime.MaxValue)
      {
         // generated closure plumbing
         Closure2 closure2 = new Closure2();
         closure2.localSomething = _something;
         closure2.localValue = someValue;
         Method(new Func<int>(closure2.Method));
      }
   }

   public virtual void Test3(int someValue)
   {
      if (DateTime.Now == DateTime.MaxValue)
      {
         // pointer to the new generated method
         Method(new Func<int>(NewMethod));
      }
   }

   private int Method(Func<int> func)
   {
      return func();
   }
}

免责声明:尽管闭包是一个比较常见的话题,但是编译器如何选择实现闭包及其规则本身是细微的,因此需要使用-深入说明.这只是作为高级摘要.

Disclaimer : Although closures are a fairly pedestrian topic, how the compiler chooses to achieve this and the rules thereof is in itself nuanced, and as such would require an in-depth dive into the specifications. This was just intended as a high level summary.

这篇关于通过将状态存储到有作用域的局部变量中来避免在快速路径上关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆