【问题标题】:Emulating templates with value parameters in C#在 C# 中使用值参数模拟模板
【发布时间】:2019-04-15 19:35:02
【问题描述】:

有什么方法可以在 C# 中模拟 C++ 值模板参数?

template<bool flag>
void Method()
{
     // Do some work
     if constexpr(flag)
     {
          // Do something specific
     }
     // Do some more work
}

这样它会生成两个版本的方法,可以这样调用:

Method<false>();
Method<true>();

这是出于性能原因,因此最好不要在Method 内进行额外调用。 Method 是性能关键部分,它被调用了数十亿次,因此从它挤出每个 CPU 周期非常重要。

另一方面,它有一个相当复杂的逻辑,所以我宁愿不要有两个副本。

我想我可以用泛型做一些事情,但这不是性能的最佳选择。所以现在我能想到的唯一方法就是为它创建某种模板代码生成器。但也许还有其他选择? 或许可以使用 Roslyn 编译该方法的两个版本,以便创建带有特定参数的两个优化版本?

void Method(bool flag)
{
     // Do some work
     if (flag)
     {
          // Do something specific
     }
     // Do some more work
}

这样我就可以编译成:

void Method_false(false)
{
     // Do some work
     // Do some more work
}

到:

void Method_true(true)
{
     // Do some work
     // Do something specific
     // Do some more work
}

有可能吗?

【问题讨论】:

  • 我怀疑这是可能的,即使是这样,您也必须指出您想在某个时候调用这两种方法中的哪一种。在某些时候,您必须以某种方式执行切换。但是,如果客户端代码只被调用一次,那不是什么大问题。

标签: c# c++ performance


【解决方案1】:

元编程在 C# 中是不可能的。在以下 SO 问题中以代码转换工具的形式寻找可能的替代方案:Is metaprogramming possible in C#?

【讨论】:

    【解决方案2】:

    您不能直接从语言中执行此操作,但它是可能,因为 JIT 足够聪明。创建一个接口和两个结构体:

    public interface IBool
    {
        bool IsTrue();
    }
    
    public struct True : IBool
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsTrue()
        {
            return true;
        }
    }
    
    public struct False : IBool
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsTrue()
        {
            return false;
        }
    }
    

    请注意IsTrue 的实现标有MethodImpl 属性。现在将您的Method 重写为通用:

    public static void Method<T>(ref T flag) where T : struct, IBool
    {
        if (flag.IsTrue())
        {
            Console.WriteLine(42);
        }
        else
        {
            Console.WriteLine(24);
        }
    }
    

    有一点很重要:T 具有 struct 约束,因此 JIT 知道没有从 T 继承的类型,并且没有人可以覆盖 IsTrue 的返回值。此外,由于此方法将被内联,因此 JIT 将对 IsTrue 的调用替换为常量。当你有

    if (false)
    {
        // some code
    }
    

    这意味着整个代码块将被删除。结果,JIT 将创建 Method 的两个实现:第一个具有 if 部分的内容,而 seconf 具有 else 部分的内容。您可以在反汇编窗口中查看:

    【讨论】:

    • 太棒了!谢谢你。看起来这是这样做的方法。
    猜你喜欢
    • 1970-01-01
    • 2012-11-02
    • 2011-02-05
    • 2016-10-06
    • 2015-10-01
    • 1970-01-01
    • 2015-01-16
    • 2013-06-19
    • 1970-01-01
    相关资源
    最近更新 更多