如何最好地支持双接口CPU与GPU?

时间:2014-01-24 11:13:59

标签: c++ cuda eigen

我有一个使用Eigen开发的项目,该项目实现了多种HPC方法,例如优化,最初是针对CPU。随着代码的增长,我将只有CPU的特征类型MatrixXd和VectorXd作为公共接口的一部分,例如

/// Base abstract definition for a System Solver strategy.
/**
 * Base abstract definition for a System Solver strategy.
 */
class SolverInterface {
private:
    DISALLOW_COPY_AND_ASSIGN(SolverInterface);

public:
    /// Default constructor
    /**
     * Default empty constructor
     */
    SolverInterface() { /* empty */ }

    /// Solves the given system either determined or over-determined.
    /**
     * Solves the given system either determined or over-determined and returns
     * the solution.
     *
     * \param A squared or overdetermined matrix
     * \param b vector of responses
     */
    virtual const VectorXd& solve(const MatrixXd& A, const VectorXd& b) = 0;

    /// Destructor
    /**
     * Destructor
     */
    virtual ~SolverInterface() { /* empty */ }
};

此时我还希望为许多算法提供原生GPU实现,因此需要以某种方式更改抽象接口。然而,最佳设计策略是什么并不是非常清楚,这是对可能性的总结:

  1. 重载:使用支持GPU实现的其他方法重载不同的接口,例如: virtual double* solve(double* d_A, int m, int n, double* d_b) = 0;
    • 优点:简单的解决方案,在客户端,代码看起来更清晰
    • 缺点:不优雅,推广if-then-else梯形反模式if-CPU-call-CPU-else-call-GPU。
  2. 模板:将SolverInterface更改为模板类,将Matrix和Vector类型作为模板参数。
    • 优点:求解器层次结构和具体实现将是干净和简洁的。
    • 缺点:模板类的协变分配不起作用。我不知道如何优雅地克服类型协方差限制,每个都需要不同的输入才能工作。这种选择感觉就像模板一直升级到客户端并导致更多的最终用户代码复杂性。
  3. DeviceMatrixXd:扩展了MatrixXd(我最喜欢的替代方案),设备备选方案可以例如附加属性double * d_data包含设备存储器和方法,例如, updateHost()updateDevice()来回同步:
    • 优点:接口并不需要改变。
    • 缺点:创建MatrixXd的DeviceMatrixXd子类会破坏Eigen中的表达式模板设计原则。它也会产生双重存储,这可能并不总是必要的,例如使用或传递不需要转换为主机内存的矩阵时。
  4. 任何其他建议,也许是一些使用特征的聪明设计?

    对于可能性#2,将以以下协变分配问题结束:

      // this will not compile!
      SolverInterface<?,?>* solver = NULL;
      if (CPU) {
          // extends SolverInterface<MatrixXd,VectorXd>
          solver = new CPULeastSquaresSolver(); 
      } else {
          // extends SolverInterface<double*,double*>
          solver = new GPULeastSquaresSolver();
      }
    

0 个答案:

没有答案