检查范围内容差是不是相等的最快方法?
Posted
技术标签:
【中文标题】检查范围内容差是不是相等的最快方法?【英文标题】:Fastest way to check equality with tolerance within a range?检查范围内容差是否相等的最快方法? 【发布时间】:2012-12-04 04:15:45 【问题描述】:以下函数比较两个数组,如果考虑到容差,所有元素都相等,则返回 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;
有没有办法超越这个功能的表现?
【问题讨论】:
它是否会导致您的程序出现速度问题? 至少,将std::abs(tolerance)
拉出循环。并不是说编译器已经不能为你做到这一点......
这种任务可以从战略性手动矢量化中受益匪浅。但我非常怀疑编译器是否能够在这里完成它,因为行程计数是未知的。
@Vincent 在这种情况下,您很可能需要为一些冷金属推测性手动矢量化牺牲模板。不过这并不容易。 :D
通过更好地了解特定用例可能会进行优化。例如,如果两个数组的最后一个对象几乎总是非常不同,但第一个对象几乎总是相同,那么以其他顺序进行比较可能是一种优化。如果值几乎总是完全相同,那么在减法之前检查相等性可能是一种避免大量减法和绝对值运算的优化。
【参考方案1】:
在循环外计算 abs(tolerance)。
您可以尝试将循环展开为“主要”循环和“次要”循环,其中“次要”循环的唯一跳转是到其开头,“主要”循环具有“if”和“break”内容。在次要循环中执行ok &= (x[i]-y[i] < abstol) & (y[i]-x[i] < abstol);
之类的操作以避免分支——注意&
而不是&&
。
然后部分展开并矢量化次要循环。然后专门针对您实际使用的任何浮点类型,并使用您平台的 SIMD 指令来执行次要循环。
当然,在这样做之前请三思,因为它会增加代码大小,从而对可维护性产生不良影响,有时还会影响系统其他部分的性能。
【讨论】:
【参考方案2】:您可以避免那些返回变量赋值,并预先计算公差的绝对值:
// 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;
此外,如果您知道容差始终为正,则无需计算其绝对值。如果没有,你可以把它作为前提。
【讨论】:
任何体面的编译器都应该自己想出这个,如果这有帮助,我会感到惊讶。【参考方案3】:我会这样做,您也可以使用类函子滚动 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 引入了额外的运算符要求,如果运算符 很慢,它可能会比 abs 慢(测量一下)。
另外,std::equal
是一种标准算法,可以为您完成所有循环和提前中断,为此使用标准库总是一个好主意。它通常更好地维护(至少在 C++11 中)并且可以得到更好的优化,因为您清楚地表明了意图。
【讨论】:
我认为您在其中缺少-
,因为它不能同时小于和大于tolerance
。以上是关于检查范围内容差是不是相等的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 中检查 2 个固定大小的数组是不是相等的最快方法是啥?