繁体   English   中英

std :: function有性能问题,如何避免呢?

[英]std::function has performances issues, how to avoid it?

我有一些类允许复合协方差函数 (也称为内核参见https://stats.stackexchange.com/questions/228552/covariance-functions-or-kernels-what-exactly-are-they ),然后计算给出的协方差以新内核为例:

auto C = GaussianKernel(50,60) + GaussianKernel(100,200);
auto result = C.covarianceFunction(30.0,40.0);

但问题是,当我想计算协方差时,我调用了一个std::function有一种简单的方法可以避免它吗?
请注意,我想计算一个大的协方差矩阵(大约50K * 50K),这意味着性能很重要。

这是代码

class Kernel {
public: 
    /*
    Covariance function : return the covariance between two R.V. for the entire kernel's domain definition. 
    */
    virtual double covarianceFunction(
        double   X,
        double   Y
    )const = 0 ;
    ~Kernel() = default;
};

class FooKernel : public Kernel {
public:
    FooKernel(std::function<double(double, double)> fun) : fun_(fun) {}
    double covarianceFunction(
        double   X,
        double   Y
    ) const {
        return fun_(X, Y);
    }
    template<class T>
    auto operator+(const T b) const {
        return FooKernel([b, this](double X, double Y) -> double {
            return this->covarianceFunction(X, Y) + b.covarianceFunction(X, Y);
        });
    }
    FooKernel operator=(const FooKernel other) const {
        return other;
    }
private:
    std::function<double(double, double)> fun_;
};

class GaussianKernel : public Kernel {
public:
    GaussianKernel(double sigma, double scale) : m_sigma(sigma), m_scale(scale) {}
    GaussianKernel(double sigma) : m_sigma(sigma), m_scale(1) {}
    /*
    A well known covariance function that enforces smooth deformations
    Ref : Shape modeling using Gaussian process Morphable Models, Luethi et al.
    */
    double covarianceFunction(
        double   X,
        double   Y
    ) const 
    {
        //use diagonal matrix
    doulbe result;
    result = m_scale  *  exp(-std::norm(X - Y) / (m_sigma*m_sigma));
    return result;      
    }
    template<class T>
    auto operator+(const T b) const {
        return FooKernel([b, this](double X, double Y) -> double {
            auto debugBval = b.covarianceFunction(X, Y);
            auto debugAval = this->covarianceFunction(X, Y);
            auto test = debugBval + debugAval;
            return test;
        });
    }
private:
    double m_sigma;
    double m_scale;
};

通过模板化FooKernel,您可以避免使用std :: function。

#include <iostream>
#include <complex>
#include <functional>


class Kernel {
public: 
    /*
    Covariance function : return the covariance between two R.V. for the entire kernel's domain definition. 
    */
    virtual double covarianceFunction(
        double   X,
        double   Y
    )const = 0 ;
    ~Kernel() = default;
};


template <typename Func>
class FooKernel : public Kernel {
public:

    FooKernel(Func&& fun) : fun_(std::forward<Func>(fun)) {}
    double covarianceFunction(
        double   X,
        double   Y
    ) const {
        return fun_(X, Y);
    }
    template<class T>
    auto operator+(const T b) const {
        return make_foo_kernel([b, this](double X, double Y) -> double {
            return this->covarianceFunction(X, Y) + b.covarianceFunction(X, Y);
        });
    }
    FooKernel operator=(const FooKernel other) const {
        return other;
    }
private:
   Func fun_;
};

template <typename Func>
auto make_foo_kernel(Func&& fun)
{
    return FooKernel<Func>(std::forward<Func>(fun));
}


class GaussianKernel : public Kernel {
public:
    GaussianKernel(double sigma, double scale) : m_sigma(sigma), m_scale(scale) {}
    GaussianKernel(double sigma) : m_sigma(sigma), m_scale(1) {}
    /*
    A well known covariance function that enforces smooth deformations
    Ref : Shape modeling using Gaussian process Morphable Models, Luethi et al.
    */
    double covarianceFunction(
        double   X,
        double   Y
    ) const 
    {
        //use diagonal matrix
    double result;
    result = m_scale  *  exp(-std::norm(X - Y) / (m_sigma*m_sigma));
    return result;      
    }
    template<class T>
    auto operator+(const T b) const {
        return make_foo_kernel([b, this](double X, double Y) -> double {
            auto debugBval = b.covarianceFunction(X, Y);
            auto debugAval = this->covarianceFunction(X, Y);
            auto test = debugBval + debugAval;
            return test;
        });
    }
private:
    double m_sigma;
    double m_scale;
};

int main()
{
    auto C = GaussianKernel(50,60) + GaussianKernel(100,200);
    auto result = C.covarianceFunction(30.0,40.0);

    return 0;
}

演示

使用这种设计,使用std::function的唯一改进是对类进行模板参数化,这可能会产生其他不需要的问题。

template<class Fun>
class FooKernel : public Kernel {
public:
    FooKernel(Fun&& fun) : fun_(std::forward<Fun>(fun)) {}
...
private:
    Fun fun_;
};

如果你不想模拟你的类,如果你需要你的类拥有一个有状态的函数对象,那么std::function几乎是唯一的方法。

但是,如果您不需要所有权或函数或函数对象是无状态的(例如自由函数),并且您在问题中声明我可以为您提供替代选项。

如你所说,你喜欢std::function的清晰度,你可以尝试这个非拥有的函数引用类:

#include <utility>

template<typename TSignature> class function_ref;

template<typename TRet, typename ...TParams>
class function_ref<TRet(TParams...)> final
{
    using refptr_t = void*;
    using callback_t = TRet (*)(refptr_t, TParams&&...);

    callback_t m_callback = nullptr;
    refptr_t m_callable = nullptr;

public:
    constexpr function_ref() noexcept = default;
    constexpr function_ref(const function_ref&) noexcept = default;
    constexpr function_ref& operator=(const function_ref&) noexcept = default;
    constexpr function_ref(function_ref&&) noexcept = default;
    constexpr function_ref& operator=(function_ref&&) noexcept = default;
    ~function_ref() noexcept = default;

    template <
        typename T,
        typename = typename std::enable_if_t<
            std::is_invocable_r_v<TRet, T(TParams...), TParams...> &&
            !std::is_convertible_v<std::decay_t<T>, function_ref>
        >
    >
    constexpr function_ref(T &&_callable) noexcept :
        m_callback(
            [](refptr_t callable, TParams&& ...params)
            {return (*reinterpret_cast<std::remove_reference_t<T>*>(callable))(std::forward<TParams>(params)...);}
            ),
        m_callable(reinterpret_cast<refptr_t>(std::addressof(_callable))) 
    {}

    constexpr decltype(auto) operator()(TParams&& ...params) noexcept 
    {
        return m_callback(m_callable, std::forward<TParams>(params)...);
    }

    constexpr operator bool() noexcept { return m_callback; }
};

这没有std::function的开销,因为它不需要拥有可调用的,并且通过我的测试,它通常完全内联-O3优化。 这是我在本次演讲中对 Vittorio Romeo所讨论的课程的修改实现。 您仍然需要观察传递给构造函数的函数的生命周期,但是获取函数参数是完美的。

用法示例:

void func(int x)
{
    std::cout<<x<< " I'm a free func!\n";
}

class Obj 
{
    public: 
    void member(int x) { std::cout<<x<< " I'm a member func!\n";}
};

int main()
{
    // Define the signature
    using func_ref_t = function_ref<void(int)>;

    // Can be used with stateful lambdas
    int bar = 1;
    auto lambda = [&bar](int x){std::cout<<x<< " I'm a lambda!\n"; ++bar;};

    // Copy and move
    func_ref_t lref(lambda);
    auto cpy = lref;
    auto mv = std::move(lref);
    cpy(1);
    mv(2);

    // See the modified var from the lambda
    std::cout<<bar<<'\n';

    // Use with free functions
    auto fref = func_ref_t{func};
    fref(4);

    // We can wrap member functions with stateful lamdas
    Obj obj;
    auto mem = [&obj](int x) { obj.member(x); };

    auto mref = func_ref_t{mem};
    mref(5);

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM