如何多次使用一个数组项以获得高性能?

时间:2016-04-23 20:27:45

标签: c# arrays performance struct compiler-optimization

对于时间要求严格的算法,我有一个struct数组,需要访问一个数组项的多个字段,如下所示:

    struct MyStruct
    {
        public int a, b, c;
    }

    MyStruct[] MyArray = new MyStruct[5];

    int Test(int id)
    {
        return MyArray[id].a + MyArray[id].b + MyArray[id].c;
    }

这是否已经由编译器优化,因此不能更快?

即使它是,它也不是非常易读。

它会变得更加可读:

    int Test(int id)
    {
        var item = MyArray[id];
        return item.a + item.b + item.c;
    }

但是,因为它是一个结构,它会被复制,对吗?那很糟糕。

我可以这样做并希望它被内联:

    int Test(ref MyStruct item)
    {
        return item.a + item.b + item.c;
    }

    int Test(int id)
    {
        return Test(ref MyArray[id]);
    }

接下来的想法是使用不安全的指针,然后我可以做

    //fixed statement, forgot syntax
    return item->a + item->b + item->c;

有什么建议吗?该怎么做呢?

其他语言有一个with块,据我记得 - 这样的事情 - 不是很好(在这种情况下 - 最好不讨论这个)?

    with MyArray[id] do
        return a + b + c;

3 个答案:

答案 0 :(得分:2)

" 对于时间要求严格的算法"您的所有代码样式变体都无关紧要 - 唯一重要考虑因素是时间。如果您可以先优化逻辑并减少大O时间复杂度(平均值或最差值),然后优化实现性能,并且只有 ,如果您在多个实现之间存在僵局,那么您会做得很好选择具有最干净代码的那个。

为了获得最优化的实现,请考虑使用低级代码("不安全的代码")并描述您的所有想法,直到您知道哪种方法最有效。

答案 1 :(得分:1)

OOP的一个原则是将纯粹依赖于对象状态的操作封装为实例方法。绝对没有理由不将此规则应用于struct

以下是可读和最佳的:

struct MyStruct
{
    public int a, b, c;
    public int SumABC() { return a + b + c; }
}

int Test(int id)
{
    return MyArray[id].SumABC();
}

答案 2 :(得分:1)

了解这一点的最佳方法是查看反汇编。我建议您为所有可以想到的变体做到这一点。通过这种方式,您将获得JIT倾向于正确和不具备的经验。

为了帮助您入门:

CLI知道一个名为托管指针的概念。当你说array[i].x时,这通常被编译为通过托管指针的访问。这就是array[i].x经常在评估堆栈上而不是加载array[i]的原因!它只加载和复制x

C#语言可以很好地保护您免受许多安全的托管代码在启用GC的指针上运行的影响。

    var item = MyArray[id];
    return item.a + item.b + item.c;

这将在实践中复制结构。这是JIT质量问题。它可以进行优化,但不是.NET 4.6.2。

int Test(ref MyStruct item)

ref是一个托管指针。这相当于其他托管指针版本。也许JIT没有内联这个,我不知道。

使用不安全的指针理论上也可以实现非复制语义,但JIT(根据我的有限测试)将某些优化应用于此类代码时遇到了麻烦。我已经看到安全代码版本更快了。

我的预测是return MyArray[id].a + MyArray[id].b + MyArray[id].c;最快。它安全而且相当干净。