【发布时间】:2020-07-09 02:59:43
【问题描述】:
假设我有一个类型 Vector3 带有一个重载的运算符 * 允许乘以双精度:
public readonly struct Vector3
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public Vector3f(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public static Vector3f operator *(in Vector3f v, in double d) => new Vector3f(d * v.X, d * v.Y, d * v.Z);
}
只有一个重载,类似于new Vector3(1,2,3) * 1.5 的表达式将编译但1.5 * new Vector3(1,2,3) 不会。由于向量标量乘法是可交换的,我希望任一顺序都可以工作,因此我添加了另一个重载,其参数反转,只会调用原始重载:
public static Vector3f operator *(in double d, in Vector3f v) => v * d;
这是正确的做事方式吗?是否应该将第二个重载实现为
public static Vector3f operator *(in double d, in Vector3f v) => new Vector3f(d * v.X, d * v.Y, d * v.Z);
相反?天真地我希望编译器优化“额外”调用,并尽可能使用第一个重载(或者可能用长重载的主体替换短重载的主体),但我不知道的行为无论哪种方式,C# 编译器都足够好。
我意识到,在许多情况下,这是一种与算法选择相形见绌的性能问题,但在某些情况下,压榨每一滴性能是至关重要的。 在性能关键的情况下,是否应将交换运算符重载实现为两个除了参数顺序之外都相同的重载,还是将一个委托给另一个委托同样有效?
【问题讨论】:
-
您可以尝试使用
Multiply(Vector3f v, double d)方法,用[MethodImpl(MethodImplOptions.AggressiveInlining)]装饰它,然后从两个运算符中调用此方法,并希望最好。它不会在 IL 中,但 JIT 可能会内联 Multiply() 代码。 -
调用一个方法(从 *(d,v) 调用 *(v,d) )有开销,如果你试图“挤压每一滴性能”,内联是至关重要的。
标签: c# performance operator-overloading operators commutativity