代码之家  ›  专栏  ›  技术社区  ›  Eddie Parker

是否可以简化这种基于分支的向量数学运算?

  •  1
  • Eddie Parker  · 技术社区  · 15 年前

    我正在努力实现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 回复  |  直到 15 年前
        1
  •  2
  •   celion    15 年前

    根据您所处的平台,编译器可能能够优化如下语句

    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(如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模式下思考。

    当然,通常的注意事项是适用的——在分析之前不要优化,等等。

        2
  •  1
  •   Keith Randall    15 年前

    如果向量元素是int,则可以执行以下操作:

    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大小写的方式不同,不确定您是否在意)

    如果你的向量元素是双精度的而不是整数,你可以做一些类似的事情,比如符号位在同一个位置,你只需要转换成整数,做蒙版,然后再转换回来。

        3
  •  1
  •   HostileFork says dont trust SE    15 年前

    如果您在源代码中寻找的是一个干净的表达式,而不是运行时优化,那么您可以考虑从“工具箱”的角度来解决这个问题。那么假设在myvector上你定义了 sign , gt (大于),以及 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++向量库可以从这些函数中选择。

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

        4
  •  0
  •   Ketan    15 年前

    由于您只进行减法运算,因此将按以下方式重写:

    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;