我有一个静态硬编码的测试代码。之后我将其转换为动态绑定来测试性能,虚拟调用似乎慢了大约两倍。

#include <vector>

class vUnit {
  public:
    virtual ~vUnit() {;}
    virtual float process(float f) = 0;
};

class vRack {
  public:
    vRack() {;}
    ~vRack() {;}
    
    inline void addUnit(vUnit* unit_ptr) {
      _units.push_back(unit_ptr);
    }
    
    inline float process_all(float  f) { 
      float  res = 0.;
      for (int j = 0 ; j< 60; j++) {
        for (auto u : _units) {
          f = u->process(f);
        }
      }
      res += f;
      return res;
    }

  private:
    std::vector<vUnit*>  _units ; 
    int _i = 0;
};


class vMultiplyF : public vUnit {
  public:
    vMultiplyF(float v) {_f = v;}
    inline float process(float f) final {
      return f * _f;
    }
  private:
    float  _f;
};

class vDivideF : public vUnit {
  public:
    vDivideF(float v) {_f = v;}
    inline float process(float f) final {
      if (f > 0) {
        return _f / f;
      } else return 0.0f;
    }
  private:
    float  _f;
};

int main () {
  float f = 1.5f, r = 0.0f;
  vRack       vR;
  vMultiplyF  vM(5.1f);
  vDivideF    vD(5.5f);

  vR.addUnit(&vM);
  vR.addUnit(&vD);

  for( int i = 0 ; i < 1000 ; i++) {
    f = vR.process_all(f);
    r += f;
  }
  return 0;
}

我认为 CRTP 可能是一个合理的折衷方案,但我不太明白如何重写vRack.addUnit()以便可以采用不同的 vUnit 实现。有人可以帮忙吗?

15

  • 擅自编辑您的代码来初始化变量(如果您的编译器允许您float f;这样做,我会觉得很奇怪vR.process_all(f);)。


    – 

  • 您如何衡量 2 倍的性能差异?


    – 

  • 1
    @AhmedAEK 我希望访问一个变体,其执行效果类似于虚拟函数调用


    – 

  • 1
    @pptaszni ESP32S3 是目标


    – 

  • 1
    @Caleth 大多数现代计算机对于变体和虚拟调用具有相同的性能,因为它们可以预测事物并预取和重新排序指令,而 MCU 则不会,访问变体比虚拟调用更快。


    – 



最佳答案
1

我认为 CRTP 可能是一个合理的折衷方案。

CRTP 在这里帮不了你。你需要一些间接方法来区分不同的操作。

如果配置是静态的,那么您可以从迭代vUnits 的容器切换到使用折叠表达式来组合特定对象的元组。

#include <tuple>

template <typename T>
concept Unit = requires(T unit, float f) { f = unit.process(f); };

class vMultiplyF { // No base class needed
  public:
    vMultiplyF(float v) {_f = v;}
    float process(float f) {
      return f * _f;
    }
  private:
    float  _f;
};

class vDivideF { // No base class needed
  public:
    vDivideF(float v) {_f = v;}
    float process(float f) {
      if (f > 0) {
        return _f / f;
      } else return 0.0f;
    }
  private:
    float  _f;
};

template <Unit... Units>
class vRack {
  public:
    vRack(Units... units) : _units(std::move(units)...) {}
    float process_all(float  f) { 
      for (int j = 0 ; j< 60; j++) {
        // evaluates (f = unit.process(f)) for each unit in sequence
        std::apply([&](Units&... unit){ ((f = unit.process(f)), ...); }, _units);
      }
      return f;
    }

  private:
    std::tuple<Units...>  _units; 
};

int main () {
  float f = 1.5f, r = 0.0f;
  vMultiplyF  vM(5.1f);
  vDivideF    vD(5.5f);
  vRack       vR(vM, vD); // deduces vRack<vMultiplyF, vDivideF>

  for( int i = 0 ; i < 1000 ; i++) {
    f = vR.process_all(f);
    r += f;
  }
  return 0;
}

2

  • 谢谢,我会用我已有的两个来测试它


    – 

  • vRack通过添加以下方法可以减少“静态”配置: template <Unit... U> auto add (U...units) { return vRack<Units...,U...> (std::tuple_cat (_units, std::make_tuple (units...))); },然后可以通过向现有机架添加一些单元来获得新机架,例如auto vR2 = vR.add (vD);。但请注意,两个机架不会具有相同的类型。


    –