是否有可能简化这种基于分支的矢量数学运算?

时间:2010-01-15 00:15:53

标签: c++ math vector readability simplification

我正在尝试在C ++中实现以下内容:

class MyVector; // 3 component vector  class

MyVector const kA = /* ... */;
MyVector const kB = /* ... */;

MyVector const kC = /* ... */;
MyVector const kD = /* ... */;


// I'd like to shorten the remaining lines, ideally making it readable but less code/operations.
MyVector result = kA;

MyVector const kCMinusD = kC - kD;

if(kCMinusD.X <= 0)
{
    result.X = kB.X;
}

if(kCMinusD.Y <= 0)
{
    result.Y = kB.Y;
}

if(kCMinusD.Z <= 0)
{
    result.Z = kB.Z;
}

将代码翻译成英文,我有四个“已知”向量。其中两个向量具有我在结果中可能想要或不想要的值,并且我是否想要它们取决于基于其他两个向量的分支的分支。

我觉得我应该能够通过一些矩阵数学和掩码来简化这段代码,但我无法理解它。

现在我要去分支机构了,但是我很想知道是否有更好的方法可以理解,而且代码冗长。

编辑:

关于马克的评论,我将解释我在这里要做的事情。

此代码摘自我正在研究的一些弹簧物理。组件如下:

kC是当前的弹簧长度,kD是最小弹簧长度。

kA和kB是两组弹簧张力,每个组件的每个组件可以是唯一的(即,沿X,Y或Z的不同弹簧张力)。如果没有完全压缩,kA是弹簧张力,如果被完全压缩,则是弹簧张力。

我想建立一个合成的'矢量',它只是kC和kD的合并,取决于弹簧是否被压缩。

4 个答案:

答案 0 :(得分:2)

根据您所使用的平台,编译器可能能够优化诸如

之类的语句
result.x = (kC.x > kD.x) ? kA.x : kB.x;
result.y = (kC.y > kD.y) ? kA.y : kB.y;
result.z = (kC.z > kD.z) ? kA.z : kB.z;

使用fsel(floating point select)指令或条件移动。就个人而言,我认为代码看起来更好,更简洁,但这是主观的。

如果代码确实对性能至关重要,并且您不介意将矢量类更改为4个浮点数而不是3个浮点数,则可以使用SIMD(例如英特尔平台上的SSE,PowerPC上的VMX)进行比较并选择答案。如果您继续这样做,它会这样:(伪代码)

// Set each component of mask to be either 0x0 or 0xFFFFFFFF depending on the comparison
MyVector4 mask = vec_compareLessThan(kC, kD);

// Sets each component of result to either kA or kB's component, depending on whether the bits are set in mask
result = vec_select(kA, kb, mask);

这需要一段时间习惯,最初可能不太可读,但你最终习惯于在SIMD模式下思考。

通常的注意事项当然适用 - 在分析之前不要进行优化等等。

答案 1 :(得分:1)

如果你的矢量元素是整数,你可以这样做:

MyVector result;
MyVector const kCMinusD = kC - kD;
int mask = kCMinusD.X >> 31;  // either 0 or -1
result.X = (kB.X & mask) | (kCMinusD.X & ~mask)
mask = kCMinusD.Y >> 31;
result.X = (kB.Y & mask) | (kCMinusD.Y & ~mask)
mask = kCMinusD.Z >> 31;
result.X = (kB.Z & mask) | (kCMinusD.Z & ~mask)

(注意这会以不同的方式处理== 0的情况,不确定你是否在意)

如果你的向量元素是双精度而不是整数,你可以做类似的事情,因为符号位在同一个地方,你只需转换为整数,做掩码,然后转换回来。

答案 2 :(得分:1)

如果您在源代码中寻求一个干净的表达式而不是运行时优化,您可以考虑从“工具箱”的角度来解决这个问题。所以我们在MyVector上说你定义了signgt(大于)和le(小于或等于)。然后分两行:

MyVector const kSignCMinusD = (kC - kD).sign();
result = kSignCMinusD.gt(0) * kA + kSignCMinusD.le(0) * kB;

运算符重载:

MyVector const kSignCMinusD = (kC - kD).sign();
result = (kSignCMinusD > 0) * kA + (kSignCMinusD <= 0) * kB;

这里的灵感是MatLab function reference。很明显,有许多C ++矢量库可供选择。

如果分析显示必要,您可以随时进入并进一步优化。但通常最大的性能问题是你能看到大局并重用中间计算的能力。

答案 3 :(得分:0)

由于您只进行减法,因此您将重写如下:

MyVector result;
result.x = kD.x > kC.x ? kB.x : kA.x;
result.y = kD.y > kC.y ? kB.y : kA.y;
result.z = kD.z > kC.z ? kB.z : kA.z;