【问题标题】:How do I implement a high-performance piecewise linear transfer function in Eigen?如何在 Eigen 中实现高性能分段线性传递函数?
【发布时间】:2013-11-05 07:41:24
【问题描述】:

我需要一些帮助来优化分段线性传递函数的基于特征的实现(输出值等于输入,但限制在一个范围内,在本例中为 [-0.5,0.5])。下面是我分析的函数:

typedef float SignalT;
typdedef Eigen::Array<SignalT, Eigen::Dynamic, Eigen::Dynamic> Signal2D;
void ActivateSum(unsigned char const idx, Signal2D::ColXpr& outputSum)
{
    switch (idx)
    {
    case 0U:
        //Threshold
        outputSum = (outputSum >= (SignalT) 0.0).cast<SignalT>();
        break;
    case 1U:
        //Piecewise linear
        outputSum = outputSum.unaryExpr([](SignalT const elem)
        {
            if (elem >(SignalT) 0.5)
                return (SignalT) 0.5;
            else if (elem < (SignalT)-0.5)
                return (SignalT)-0.5;
            else
                return elem;
        }
        );
        break;
    case 2U:
        //Fast Sigmoid
        outputSum *= ((SignalT) 1.0 + outputSum.abs()).inverse();
        break;
    default:
        assert(0);
        throw;
    }
}

我的整个程序在每个 switch 案例中都使用了以下部分样本:

Threshold: 3.3%
Piecewise Linear: 18%
Fast Sigmoid: < 0.1%

快速 sigmoid 很少使用,但分段线性情况应该与阈值情况一样频繁出现(尽管我不知道如何使用 Visual Studio 来衡量这一点)。所以在我看来,我在分段线性一元表达式上花费了很多时间,并且想知道是否有另一种方法来实现 Eigen 中的功能,也许是通过使用一些内置方法来提高速度.这是一个非常简单的传递函数,因此它在计算上确实应该很便宜 - 我的猜测是,由于我的自定义 lambda,成本与优化不佳有关。

想法?

编辑:到目前为止,由于 Leeor 的回答,我想出了这个:

case 1U:
    //Piecewise linear
    outputSum = outputSum.max((SignalT)-0.5).min((SignalT)0.5);
    break;

【问题讨论】:

  • 你想要饱和算术。 “分段线性传递函数”太笼统了,建议 1. 您有任意数量的任意斜率线,而不仅仅是一条斜率 = 1 的线,以及 2. 您处于您的频域中不是。
  • @Potatoswatter 好吧我不知道它叫什么,但你可以看看代码,看看我在追求什么(我以为它被称为分段线性传递函数)。关于开关/外壳;无论您使用 if/else、查找表还是 switch/case,它都会对性能产生绝对的影响。原因是 outputSum 的长度 > 20,000,因此操作的核心在那些 case 子句中。

标签: c++ performance eigen


【解决方案1】:

如果您现有的代码还没有以这种方式编译,请使用 FPU 最大和最小指令。

    outputSum = outputSum.unaryExpr( [] (SignalT elem)
    {
        return std::fmax( -0.5f, std::fmin( 0.5f, elem ) );
    }

Eigen 可能已经内置了此类操作,但查看文档并没有发现任何问题。

【讨论】:

  • 是的,确实如此! outputSum = outputSum.max((SignalT)-0.5).min((SignalT)0.5);
  • @ausairman 酷,这提高了性能吗?一定要对这两种方式进行分析;我认为 Eigen 足够聪明,可以交错链接操作,但你不能太确定。
  • 是的,基本上,该操作现在占用与阈值函数相同的 CPU 时间。
【解决方案2】:

可能是分支预测,您的 if 条件会创建复杂的数据相关控制流,多个返回站点可能难以优化。

也许像这样的三元运算符会消除分支:

        return (elem>0? 1 : -1) * (std::min(std::abs(elem),0.5));

(请确保您使用的库支持浮点上的 abs,我认为 cmath 应该没问题)。

看看你的编译器是否通过条件移动来以这种方式发出更少分支的代码。

【讨论】:

  • 三元运算符生成与if 相同的分支。编译器的优化器看到的控制流图会完全一样。
  • @Potatoswatter,不完全是,原始代码从 if 代码中的多个位置从函数返回。如果编译器可以用条件操作交换它,我会感到非常惊讶。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-09
  • 1970-01-01
  • 2020-09-27
  • 2021-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多