最有效的以未知顺序执行函数的方法

时间:2011-09-05 23:55:20

标签: c++ performance algorithm

假设我有一个50到200之间的大型单个函数池,它们的工作就是对单个对象进行操作并对其进行修改。函数池有选择地放入一个数组中,并按任意顺序排列。

函数本身不会在它正在修改的对象中出现的值之外使用任何参数,并且以这种方式,对象的行为仅由执行哪些函数以及以什么顺序确定。

到目前为止,我暂时使用的一种方法是,这可以更好地解释我的目标是什么:

class Behavior{
    public:
    virtual void act(Object * obj) = 0;
};

class SpecificBehavior : public Behavior{
    // many classes like this exist
    public:
    void act(Object * obj){ /* do something specific with obj*/ };
};

class Object{
    public:
    std::list<Behavior*> behavior;

    void behave(){
        std::list<Behavior*>::iterator iter = behavior.front();
        while(iter != behavior.end()){
             iter->act(this);
             ++iter;
        };
    };
};

我的问题是,在性能和可维护性方面,C ++组织这样一个功能池的最有效方法是什么。这是针对我正在进行的一些A.I研究,这种方法与我想要达到的最接近。

编辑:阵列本身可以随时由此处未列出的代码的任何其他部分进行更改,但保证在调用behave()期间永远不会更改。它存储的数组需要能够改变并扩展到任何大小

4 个答案:

答案 0 :(得分:5)

如果行为函数没有状态且只接受一个Object参数,那么我将使用一个函数对象的容器:

#include <functional>
#include <vector>

typedef std::function<void(Object &)> BehaveFun;
typedef std::vector<BehaveFun> BehaviourCollection;

class Object {
  BehaviourCollection b;

  void behave() {
    for (auto it = b.cbegin(); it != b.cend(); ++it) *it(*this);
  }
};

现在您只需要将所有功能加载到集合中。

答案 1 :(得分:2)

如果您要对此集合执行的主要操作是迭代它,您可能希望使用vector作为解除引用,并且递增迭代器将等同于简单的指针算法。

如果您想使用所有内核,并且您的操作不共享任何状态,您可能需要查看像英特尔TBB这样的库(请参阅parallel_for example

答案 2 :(得分:2)

我会完全按照你的要求保留它。 Perofmance应该没问题(由于vtable查找可能会有额外的间接,但这无关紧要。)

保持原样的理由是:

  1. 您可以将常见的子行为提升为行为与实现类之间的中间类。使用函数指针并不容易。

    struct AlsoWaveArmsBase : public Behaviour 
    {
       void act( Object * obj )
       {
          start_waving_arms(obj); // Concrete call
          do_other_action(obj);   // Abstract call
          end_waving_arms(obj);   // Concrete call
       }
       void start_waving_arms(Object*obj);
       void end_waving_arms(Object*obj);
       virtual void do_other_actions(Object * obj)=0;
    };
    
    struct WaveAndWalk : public AlsoWaveArmsBase
    {
       void do_other_actions(Object * obj) { walk(obj); }
    };
    
    struct WaveAndDance : pubic AlsoWaveArmsBase
    {
       void do_other_actions(Object * obj) { walk(obj); }    
    }
    
  2. 您可能希望在行为中使用州

    struct Count : public Behavior
    {
       Behaviour() : i(0) {}
       int i;
       void act(Object * obj)
       {
          count(obj,i);
          ++i;
       }
    }
    
  3. 您可能想要添加辅助功能,例如您可能想要添加can_act这样的内容:

    void Object::behave(){
      std::list<Behavior*>::iterator iter = behavior.front();
      while(iter != behavior.end()){
         if( iter->can_act(this) ){
            iter->act(this);
         }
         ++iter;
       };
     };
    
  4. IMO,这些灵活性超过了转向纯函数方法的好处。

答案 3 :(得分:1)

为了可维护性,您当前的方法是最好的(虚函数)。你可能从使用自由函数指针获得微小的收益,但我怀疑它是可测量的,即使如此,我认为这不值得麻烦。目前的面向对象方法足够快且可维护。我正在谈论的一点点好处来自于你正在取消引用一个指向一个对象的指针,然后(在幕后)取消引用一个指向一个函数的指针(它发生在调用虚函数的实现中)。

我不会使用std :: function,因为它不是非常高效(尽管实现之间可能会有所不同)。请参阅thisthis。在运行时需要这种动态时,函数指针的速度和它一样快。

如果你需要提高性能,我建议考虑改进算法,而不是这个实现。