对于时间要求严格的算法,我有一个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;
答案 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;
最快。它安全而且相当干净。