首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在IL中实现C#前端优化

如何在IL中实现C#前端优化
EN

Stack Overflow用户
提问于 2017-02-15 14:38:12
回答 1查看 756关注 0票数 2

在这个回答和这个GitHub问题 (顶级项)中,有一个关于C#编译器使用的foreach优化的描述。

基本上,生成的代码不是分配IEnumerable<T>,而是对返回的对象调用GetEnumerator(),然后调用MoveNext(),始终使用直接call,从而避免装箱和虚拟调用。

是否可以用中介语言编写同样的逻辑?我是一个相当初学者的IL,但熟悉的不安全包装和它的工作方式。我想知道是否有可能用IL编写一个不安全的方法,它接受某个对象并直接调用它的方法和属性?

(另外,有人能给出一个链接到罗斯林回购中发生foreach优化的那一行吗?回购是如此庞大和复杂,到目前为止我在那里迷路了。)

更新:

下面是一个方法模板

代码语言:javascript
复制
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ILSub(@"
    .. IL code here to be replaced by ilasm.exe
    .. Is there a way to do the same without boxing and virtual calls?
    ")]
public T CallIEnumerableMoveNextViaIL<T>(IEnumerable<T> enumerable)
{
    // I know that the `enumerable` returns an enumerator that is a struct, but its type could be custom
    // Next two calls are virtual via an interface, and enumerator is boxed
    var enumerator = enumerable.GetEnumerator();
    enumerator.MoveNext();
    return enumerator.Current;
}

在这里,IL用于这种方法:

代码语言:javascript
复制
IL_0000: ldarg.1
IL_0001: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!T>::GetEnumerator()
IL_0006: dup
IL_0007: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_000c: pop
IL_000d: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<!!T>::get_Current()
IL_0012: ret

从注释中可以看出,在不知道类型的情况下调用get_Current()之类的方法是不可能的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-15 15:56:18

让我们举几个例子来说明如何编译foreach

首先,在一个常规的IEnumerator返回的GetEnumerator上

代码语言:javascript
复制
public class A : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

foreach(object o in new A())
{

}
代码语言:javascript
复制
  .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
           class [mscorlib]System.IDisposable V_1)
  IL_0000:  newobj     instance void Testing.Program/A::.ctor()
  IL_0005:  call       instance class [mscorlib]System.Collections.IEnumerator Testing.Program/A::GetEnumerator()
  IL_000a:  stloc.0
  .try
  {
    IL_000b:  br.s       IL_0014
    IL_000d:  ldloc.0
    IL_000e:  callvirt   instance object [mscorlib]System.Collections.IEnumerator::get_Current()
    IL_0013:  pop
    IL_0014:  ldloc.0
    IL_0015:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    IL_001a:  brtrue.s   IL_000d
    IL_001c:  leave.s    IL_002f
  }  // end .try
  finally
  {
    IL_001e:  ldloc.0
    IL_001f:  isinst     [mscorlib]System.IDisposable
    IL_0024:  stloc.1
    IL_0025:  ldloc.1
    IL_0026:  brfalse.s  IL_002e
    IL_0028:  ldloc.1
    IL_0029:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_002e:  endfinally
  }  // end handler

这里没有什么奇怪的,只是在IEnumerator上调用这些方法。注意,它还实现了IDisposable,因此它使用它的模式。

接下来,让我们有一个返回值类型的GetEnumerator。

代码语言:javascript
复制
public class B
{
    public BE GetEnumerator()
    {
        return new BE();
    }

    public struct BE
    {
        public object Current {
            get {
                throw new NotImplementedException();
            }
        }

        public bool MoveNext()
        {
            throw new NotImplementedException();
        }

        public void Reset()
        {
            throw new NotImplementedException();
        }
    }
}
代码语言:javascript
复制
  .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
           class [mscorlib]System.IDisposable V_1,
           valuetype Testing.Program/B/BE V_2,
           object[] V_3,
           int32 V_4)
  IL_002f:  newobj     instance void Testing.Program/B::.ctor()
  IL_0034:  call       instance valuetype Testing.Program/B/BE Testing.Program/B::GetEnumerator()
  IL_0039:  stloc.2
  IL_003a:  br.s       IL_0044
  IL_003c:  ldloca.s   V_2
  IL_003e:  call       instance object Testing.Program/B/BE::get_Current()
  IL_0043:  pop
  IL_0044:  ldloca.s   V_2
  IL_0046:  call       instance bool Testing.Program/B/BE::MoveNext()
  IL_004b:  brtrue.s   IL_003c

请注意,这里不需要实现IEnumerable/IEnumerator接口。这种方法有时被称为鸭子类型。此实现在内部将枚举数存储在变量中,然后调用其地址(ldloca)上的方法。当枚举数从GetEnumerator返回时,将在这里进行一次值类型复制。

第三个例子几乎是完全不同的,数组上的foreach

代码语言:javascript
复制
foreach(object o in new object[0])
{

}

  .locals init (class [mscorlib]System.Collections.IEnumerator V_0,
           class [mscorlib]System.IDisposable V_1,
           valuetype Testing.Program/B/BE V_2,
           object[] V_3,
           int32 V_4)  IL_004d:  ldc.i4.0
  IL_004e:  newarr     [mscorlib]System.Object
  IL_0053:  stloc.3
  IL_0054:  ldc.i4.0
  IL_0055:  stloc.s    V_4
  IL_0057:  br.s       IL_0064
  IL_0059:  ldloc.3
  IL_005a:  ldloc.s    V_4
  IL_005c:  ldelem.ref
  IL_005d:  pop
  IL_005e:  ldloc.s    V_4
  IL_0060:  ldc.i4.1
  IL_0061:  add
  IL_0062:  stloc.s    V_4
  IL_0064:  ldloc.s    V_4
  IL_0066:  ldloc.3
  IL_0067:  ldlen
  IL_0068:  conv.i4
  IL_0069:  blt.s      IL_0059

这不使用GetEnumerator,只需使用老式的索引方式(更高的性能)遍历数组。

您不能在CIL中使用这种精确的优化,因为CIL没有鸭类型;您必须自己编写所有签名和方法调用。

但是,如果需要对任何类型进行此优化,并且可以修改要使用的类型,则可以在类似于此的代码中使用泛型接口:

代码语言:javascript
复制
public class B : IStructEnumerable<object, BE>
{
    public BE GetEnumerator()
    {
        return new BE();
    }
}

public struct BE : IStructEnumerator<object>
{
    public object Current {
        get {
            throw new NotImplementedException();
        }
    }

    public bool MoveNext()
    {
        throw new NotImplementedException();
    }

    public void Reset()
    {
        throw new NotImplementedException();
    }
}

public interface IStructEnumerable<TItem, TEnumerator> where TEnumerator : struct, IStructEnumerator<TItem>
{
    TEnumerator GetEnumerator();
}

public interface IStructEnumerator<TItem>
{
    TItem Current {get;}
    bool MoveNext();
    void Reset();
}

public static void TestEnumerator<TEnumerable, TEnumerator>(TEnumerable b) where TEnumerable : IStructEnumerable<object, TEnumerator> where TEnumerator : struct, IStructEnumerator<object>
{
    foreach(object obj in b)
    {

    }
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42252341

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档