我在我开发了一段时间的科学应用程序中广泛使用 Eigen。由于我正在实现数值方法,因此低于某个阈值(例如 1e-15 )的数字不是兴趣点,并且会减慢计算速度并增加错误率。

因此,我想将低于该阈值的数字四舍五入到 0 。我可以用 for 循环来完成,但是用 for 敲击多个相对较大的矩阵(每个矩阵 200 万个单元和以上) - if 循环很昂贵并且会减慢我的速度,因为我需要多次执行它。
Eigen 库有没有更有效的方法来做到这一点?

换句话说,我试图在我的计算管道中消除低于某个阈值的数字。

最佳答案

写你想要的东西的最短方法是

void foo(Eigen::VectorXf& inout, float threshold)
{
    inout = (threshold < inout.array().abs()).select(inout, 0.0f);
}

但是,比较和 select 方法都不会被特征 ( as of now ) 向量化。

如果速度很重要,您需要编写一些手动 SIMD 代码,或者编写一个支持 packet 方法的自定义仿函数(这使用了 Eigen 的内部功能,因此不能保证稳定!):
template<typename Scalar> struct threshold_op {
  Scalar threshold;
  threshold_op(const Scalar& value) : threshold(value) {}
  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator() (const Scalar& a) const{
    return threshold < std::abs(a) ? a : Scalar(0); }
  template<typename Packet>
  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Packet packetOp(const Packet& a) const {
    using namespace Eigen::internal;
    return pand(pcmp_lt(pset1<Packet>(threshold),pabs(a)), a);
  }
};
namespace Eigen { namespace internal {

template<typename Scalar>
struct functor_traits<threshold_op<Scalar> >
{ enum {
    Cost = 3*NumTraits<Scalar>::AddCost,
    PacketAccess = packet_traits<Scalar>::HasAbs };
};

}}

然后可以将其传递给 unaryExpr :
inout = inout.unaryExpr(threshold_op<float>(threshold));

Godbolt-Demo(应该适用于 SSE/AVX/AVX512/NEON/...):https://godbolt.org/z/bslATI

实际上,您放缓的唯一原因可能是低于正常的数字。在这种情况下,一个简单的
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);

应该可以解决问题(参见:Why does changing 0.1f to 0 slow down performance by 10x?)

关于c++ - 在 Eigen 中将低于某个阈值的数字四舍五入为零,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54503795/

10-11 04:53