以下函数比较两个数组,如果考虑到容差,所有元素都相等,则返回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;
}
有没有办法打败这个功能的表现?
答案 0 :(得分:2)
我会这样做,你也可以使用类函数滚动一个C ++ 03版本,它会更加冗长但应该同样有效:
std::equal(x, x+n, y, [&tolerance](Type a, Type b) -> bool { return ((a-b) < tolerance) && ((a-b) > -tolerance); }
主要区别在于放弃了abs:取决于Type
以及如何实现abs
,您可能会获得额外的条件执行路径,并且有很多分支错误预测,这当然应该避免这种情况。 a-b的重复计算可能会被编译器优化掉(如果认为必要的话)。
当然,它引入了对Type的额外运算符要求以及运算符&lt;或者&gt;很慢,它可能比abs慢(测量它)。
此外,std::equal
是一个标准算法,为您完成所有循环和早期破坏,为此使用标准库总是一个好主意。它通常更好(至少在C ++ 11中)并且可以更好地进行优化,因为你清楚地显示了意图。
答案 1 :(得分:2)
在循环外计算abs(容差)。
您可以尝试将循环展开为“主要”循环和“次要”循环,其中“次要”循环仅跳转到其开头,“主要”循环具有“if”和“break”内容。在次要循环中执行ok &= (x[i]-y[i] < abstol) & (y[i]-x[i] < abstol);
之类的操作以避免分支 - 请注意&
而不是&&
。
然后部分展开并矢量化次循环。然后专门针对您实际使用的任何浮点类型,并使用平台的SIMD指令进行次循环。
在这样做之前,请考虑一下,因为它会增加代码大小,从而对可维护性产生不良影响,有时会影响系统其他部分的性能。
答案 2 :(得分: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;
}
另外,如果您知道公差总是可行的,则无需计算其绝对值。如果没有,你可以把它当作先决条件。