1

我正在尝试在 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 是弹簧未完全压缩时的张力,kB 是弹簧完全压缩时的张力。

我想建立一个结果“向量”,它只是 kC 和 kD 的合并,取决于弹簧是否被压缩。

4

4 回答 4

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(浮点选择)指令或条件移动。就个人而言,我认为这样的代码看起来也更好更简洁,但这是主观的。

如果代码确实对性能至关重要,并且您不介意将矢量类更改为 4 个浮点数而不是 3 个浮点数,则可以使用 SIMD(例如 Intel 平台上的 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 模式下思考。

当然,通常的警告适用 - 在配置文件之前不要优化,等等。

于 2010-01-15T01:13:16.537 回答
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 的情况不同,不确定你是否关心)

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

于 2010-01-15T00:24:45.430 回答
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 函数参考。显然,有许多 C++ 向量库可供选择这些函数。

如果分析表明有必要,您可以随时进行进一步优化。但通常最大的性能问题是您如何看待全局并重用中间计算。

于 2010-01-15T01:56:05.120 回答
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;
于 2010-01-15T01:15:30.263 回答