代码之家  ›  专栏  ›  技术社区  ›  Sergio0694

IL方法将参考T值固定为void*(从并行代码处理Span<T>)

  •  0
  • Sergio0694  · 技术社区  · 6 年前

    在实际问题之前,有一个小小的免责声明:

    这是一个相关/后续问题 this one ,但从这里开始,我谈论的是一个更普遍的问题(如何固定 ref T 变量来执行指针操作),我提出了一个可能的(可能是错误的)解决方案,我提出了一个单独的问题。

    所以,问题是: 给定 参考T 变量(假设它是数组的第一项),如何固定它,以便GC在使用不安全指针处理底层数组时不会造成问题?

    我不确定这在C#中是否可行(但我希望我错了),但请查看IL代码中的一个方法,该方法只修复 ref int 变量,我试图想出一个可以处理泛型类型的变量。以下是我的想法:

    假设我有这个 delegate 在类“MyTestClass”中:

    public unsafe delegate void UnsafeAction(void* p);
    

    然后,在IL中:

    .method public hidebysig static void
        Foo<valuetype .ctor (class [netstandard]System.ValueType) T>(
            !!0/*T*/& r, 
            class MyTestClass/UnsafeAction action
        ) cil managed
    {
        .maxstack 2
        .locals init (
          [0] void* p,
          [1] !!0/*T*/& pinned V_1
        )
    
        // Load the ref T argument into the pinned variable (as if fixed were used)
        IL_0000: nop          
        IL_0001: ldarg.0      // r
        IL_0002: stloc.1      // V_1
    
        // Cast the T* pointer to void*
        IL_0003: ldloc.1      // V_1
        IL_0004: conv.u       
        IL_0005: stloc.0      // p
    
        // Invoke the action passing the p pointer
        IL_0006: ldarg.1      // action
        IL_0007: ldloc.0      // p
        IL_0008: callvirt     instance void MyTestClass/UnsafeAction::Invoke(void*)
        IL_000d: nop          
    
        // Set the pinned variable V_1 to NULL
        IL_000e: ldc.i4.0     
        IL_000f: conv.u       
        IL_0010: stloc.1      // V_1
        IL_0011: ret
    }
    

    我们的想法是能够像这样使用这种方法:

    public static void Test<T>(this Span<T> span, Func<T> provider)
    {
        void Func(void* p)
        {
            // Do stuff with p, possibly in parallel
            // eg. Unsafe.Write(p, provider());
        }
        Foo(ref span.DangerousGetPinnableReference(), Func);
    }
    

    你喜欢这个工作吗?如果是这样的话,将这样一个用IL编写的方法包含到现有方法中的最佳方法是什么。NET标准2.0项目?

    谢谢

    奖金问题 :我见过CoreFX repo使用“.ilproj”文件来支持具有IL类的项目,但VS实际上不支持这些文件。这是某种扩展,还是他们使用自己的自定义脚本来支持该项目格式?我知道有一个 ILProj extension 可用,但它既不是官方的,也与VS2017不兼容。

    编辑 :我可能刚刚对如何解决此问题有了想法,请考虑以下事项:

    public static void Foo<T>(ref T value)
    {
        fixed (void* p = &Unsafe.As<T, byte>(ref value))
        {
            // Shouldn't the ref T be correctly fixed here, since
            // the "dummy" byte ref had the same address?
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   IS4    4 年前

    是的,将引用存储在固定的本地中就足够了。在方法执行期间,引用的内存将不会移动,因此指针将保持有效。

    您还可以删除 nop 从您的方法(分析在调试下编译的代码的结果)中删除,并且之后将变量设置为零也是不必要的,因为该方法正好在它之后退出。

    至于如何在C#项目中分发CIL代码,我将使用 动态方法 ,但我不知道是否在中提供。净标准。如果不是,则将IL编译为 .dll .netmodule 引用它或自动将其链接到主项目并不是什么大问题。