2

以下函数比较两个数组,如果考虑到容差,所有元素都相等,则返回 true。

// Equal
template<typename Type>
bool eq(const unsigned int n, const Type* x, const Type* y, const Type tolerance)
{
    bool ok = true;
    for(unsigned int i = 0; i < n; ++i) {
        if (std::abs(x[i]-y[i]) > std::abs(tolerance)) {
            ok = false;
            break;
        }
    }
    return ok;
}

有没有办法打败这个功能的表现?

4

3 回答 3

2

在循环外计算 abs(tolerance)。

您可以尝试将循环展开为“主要”循环和“次要”循环,其中“次要”循环的唯一跳转是到其开头,“主要”循环具有“if”和“break”内容。在次要循环中执行类似ok &= (x[i]-y[i] < abstol) & (y[i]-x[i] < abstol);操作以避免分支 - note&而不是&&.

然后部分展开并矢量化次循环。然后专门针对您实际使用的任何浮点类型,并使用您平台的 SIMD 指令来执行次要循环。

当然,在这样做之前要三思,因为它会增加代码大小,从而对可维护性产生不良影响,有时还会对系统其他部分的性能产生不良影响。

于 2012-12-05T03:26:45.077 回答
1

您可以避免那些返回变量分配,并预先计算公差的绝对值:

// Equal
template<typename Type>
bool eq(const unsigned int n, const Type* x, const Type* y, const Type tolerance) {
    const Type absTolerance = std::abs(tolerance);
    for(unsigned int i = 0; i < n; ++i) {
        if (std::abs(x[i]-y[i]) > absTolerance) {
            return false;
        }
    }
    return true;
}

此外,如果您知道容差始终为正,则无需计算其绝对值。如果没有,您可以将其作为前提条件。

于 2012-12-04T05:07:18.777 回答
1

我会这样做,您也可以使用类函子滚动 C++03 版本,它会更冗长但应该同样有效:

std::equal(x, x+n, y, [&tolerance](Type a, Type b) -> bool { return ((a-b) < tolerance) && ((a-b) > -tolerance); }

主要区别在于删除 abs:取决于实现方式Typeabs实现方式,您可能会获得额外的条件执行路径,并且有很多分支错误预测,这当然应该避免这种情况。ab 的重复计算可能会被编译器优化掉(如果它认为有必要的话)。

当然,它为 Type 引入了额外的运算符要求,如果运算符 < 或 > 很慢,它可能会比 abs 慢(测量它)。

此外,std::equal是一种标准算法为您完成所有循环和早期中断,为此使用标准库总是一个好主意。它通常更好维护(至少在 C++11 中)并且可以得到更好的优化,因为您清楚地表明了意图。

于 2012-12-04T10:45:17.963 回答