在特征值以下将数字四舍五入为零

时间:2019-02-03 14:22:18

标签: c++ matrix eigen

我在一段时间以来一直在科学应用中广泛使用Eigen。由于我正在实施一种数值方法,因此低于某个阈值(例如1e-15)的数字不是我们的关注点,它会减慢计算速度并增加错误率。

因此,我想将低于该阈值的数字四舍五入为0。我可以使用for循环来完成此操作,但是使用for-if循环锤击多个相对较大的矩阵(每个矩阵需要2M单元,每个矩阵最多)是昂贵的,并且由于需要而减慢了速度做多次。

使用Eigen库是否有更有效的方法?

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

2 个答案:

答案 0 :(得分:3)

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

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

但是,比较和select方法都不会被Eigen(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?

答案 1 :(得分:0)

本征有一种称为UnaryExpr的方法,该方法将给定的函数指针应用于矩阵中的每个系数(它也具有稀疏和数组变体)。

将测试其性能并相应地更新此答案。