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

时间:2018-06-08 14:53:51

标签: c++ performance machine-learning statistics c++17

我有类允许复合协方差函数(也称为内核参见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;
};

3 个答案:

答案 0 :(得分:7)

通过模板化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;
}

Demo

答案 1 :(得分:1)

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

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

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

但是,如果您不需要所有权或该功能或功能对象是无状态的(例如免费功能),并且您在问题中说明我可以为您提供替代选项。

答案 2 :(得分:1)

如你所说,你喜欢 "files": [ "App/Helpers/AppHelper.php", "App/Helpers/CoinHiveApi.php", "App/Helpers/CloudflareAPI.php" ] 的清晰度,你可以试试这个非拥有的函数引用类:

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优化。这是我修改过的Vittorio Romeo在talk中讨论过的类的实现。您仍然需要观察传递给构造函数的函数的生命周期,但是它很适合使用函数参数。

使用示例:

-O3