我有一个相当复杂的函数,该功能采用了几个双重值,该值代表两个形式的三个空间(大小,纬度,经度)的两个矢量,其中纬度和经度在弧度为弧度和一个角度。该函数的目的是按指定的角度将第一个向量旋转在第二个矢量并返回结果向量。我已经验证了代码在逻辑上是正确的并且有效。

该函数的预期目的是用于图形,因此不需要双重精度。但是,在目标平台上,trig(和sqrt)功能具有浮力(SINF,COSF,ATAN2F,ASINF,ASINF,ACOSF和SQRTF)的功能更快地在双打上工作速度(可能是因为计算此类值的说明实际上可能需要一个指令,实际上可能需要一个double;如果通过浮子,则必须将值铸成双重,这需要将其复制到具有更多内存的区域 - 即高架)。结果,该函数中涉及的所有变量均为双重精度。

这是问题:我正在尝试优化我的功能,以便每秒都可以称呼更多次。因此,我用呼叫呼叫,用呼叫来代替对这些功能的浮点版本的呼叫,因为它们总体上增加了3-4倍的速度。这几乎适用于所有输入;但是,如果输入向量与标准单位向量(I,J或K)平行,则各种功能的圆形错误构建足够,以引起稍后对SQRTF或逆Trig函数的调用(ASINF,ACOSF,ACOSF,ACOSF, atan2f)通过 只是勉强 在这些功能的领域之外。

因此,我遇到了这个困境:要么我只能调用双重精度功能并避免问题(最终每秒的限制约为1,300,000个矢量操作),要么我可以尝试提出其他内容。最终,我想一种方法来消毒反向触发器功能的输入以照顾边缘案例(对于SQRT来说,这是微不足道的:只需使用ABS)。分支不是一个选择,因为即使是单个条件语句也增加了太多的高架,以至于丢失了任何性能。

那么,有什么想法吗?

编辑:有人对我的使用双打与浮点操作表示困惑。如果我实际将所有值存储在双尺寸的容器(即双型变量)中,则该功能要快得多。但是,出于明显的原因,浮点精度触发器操作比双精度TRIG操作要快。

有帮助吗?

解决方案

基本上,您需要找到一个 数值稳定 解决您的问题的算法。没有对这种事情的通用解决方案,需要使用诸如诸如 条件编号 如果单个步骤。实际上,如果潜在的问题本身不足,则可能是不可能的。

其他提示

单精度浮点本质上引入了错误。因此,您需要构建数学,以便所有比较都通过使用Epsilon因子具有一定程度的“ SLOP”,并且您需要对具有有限域的功能进行消毒。

分支时,前者很容易

bool IsAlmostEqual( float a, float b ) { return fabs(a-b) < 0.001f; } // or
bool IsAlmostEqual( float a, float b ) { return fabs(a-b) < (a * 0.0001f); } // for relative error

但这很混乱。夹紧域输入有点棘手,但更好。关键是使用 有条件的移动操作员, ,通常会做类似的事情

float ExampleOfConditionalMoveIntrinsic( float comparand, float a, float b ) 
{ return comparand >= 0.0f ? a : b ; }

在单个OP中,不产生分支。

这些因建筑而异。在X87浮点单元上,您可以使用 FCMOV有条件移动, ,但这很笨拙,因为它取决于以前设置的条件标志,所以它很慢。同样,CMOV也没有一致的编译器固有的。这就是为什么我们避免使用X87浮点而支持SSE2标量数学的原因之一。

通过配对,有条件的移动可以更好地支持SSE 比较操作员 有一个位和。对于标量数学来说,这是可取的:

// assuming you've already used _mm_load_ss to load your floats onto registers 
__m128 fsel( __m128 comparand, __m128 a, __m128 b ) 
{
    __m128 zero = {0,0,0,0};
    // set low word of mask to all 1s if comparand > 0
    __m128 mask = _mm_cmpgt_ss( comparand, zero );  
    a = _mm_and_ss( a, mask );    // a = a & mask 
    b = _mm_andnot_ss( mask, b ); // b = ~mask & b
    return _mm_or_ss( a, b );     // return a | b
    }
}

当启用SSE2标量数学时,编译器更好,但不是很棒的,即为Ternaries发出这种模式。您可以使用编译器标志来做到这一点 /arch:sse2 在MSVC或 -mfpmath=sse 在GCC上。

在PowerPC和许多其他RISC架构上, fsel() 是一个硬件操作码,因此通常也是编译器的内在。

你看过 图形编程黑皮书 还是将计算交给您的GPU?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top