运行时期间的非多态容器

时间:2016-09-05 06:36:07

标签: c++ templates c++11 template-meta-programming

我试图将对象存储在多种容器类型之一中,容器的类型在运行时是已知的。 容器的界面是相同,但容器多态。

我想要做的是避免在运行时额外跳转vtable力 - 通过具有应该是具体容器类型的变量。

考虑一下我有容器:

class Vector1<T>
{
     T get();
}
class Vector2<T>
{
     T get();
}

我希望有一个容器,它可以是Vector1或Vector2,具体取决于某些运行时参数。

所以我的班级应该是:

Class VectorWrapper
{
      Vector1<SomeType> v1; 
      Vector2<Sometype> v2;

      inline Sometype get()
      {
          **how do I choose between v1 and v2, without the extra jump?**
      }
}

3 个答案:

答案 0 :(得分:3)

为了避免在运行时决定,您必须在编译时做出决定。这意味着使用模板。如果你的时间有限,以至于关键路径上肯定没有必要的跳跃,那么你必须继续在你的程序中越来越高地推动tempaltedness,直到你达到额外的跳跃可以制作。是的,理论上这可能意味着在模板中有> = 90%的代码,但如果真的需要避免跳转,那就是价格。

换句话说,如果VectorWrapper不能使用if决定,那么它也必须是模板。所以必须是使用它的代码等,直到你最终达到if不那么昂贵的程度。

如果你发现这个模板化部分太大,但至少可以以某种方式被隔离,你甚至可以做一些事情,比如构建几个共享库(每个库中有不同的模板参数),并在运行时加载一个包含正确组合模板参数。

答案 1 :(得分:1)

你的问题很模糊。所以我能提供的只是模糊的解决方案。

值得考虑的第一项技术是手动vtable。与自动vtable相比,这消除了内存间接。基本上,您将函数指针存储到方法的正确实现中,并直接跟随该函数指针。

第二种方法是将分支从循环中提升出来。重要的是,您的代码必须重复运行。如果决定在重复之前采取哪个分支,则可以将分支移出那里。

想象一下你有

void foo() {
  bool which_branch = decide();
  VectorWrapper wrap( decide );
  wrap.populate();
  for (auto x : some_loop_domain) {
    x.set( wrap.get() );
  }
}

请注意,决定是在循环之外做出的,但价格可以在循环内支付。

如果我们可以将分支移到循环之外,则some_loop_domain每个元素支付一次而不是一次。

使这项工作的一种方法是为编译器提供更容易优化的常量。因此,我们避免在包装器中存储使用向量的状态,而是将其传入。我们传递的值被设置为&#34;显然是常数&#34;在使用它的上下文中,甚至是编译时常量。

要使用编译时常量,使用它的代码必须加上寺庙化。然而,这种寺庙化只需要在循环之上&#34;点,作为一个分支,通常便宜到不关心。

为了依赖优化器是合理的,你同样在那里填充一个明显的常量并将其作为值传递下去。根据我的经验,这可能不太可靠。

void foo() {
  bool which_branch = decide();

  auto body = [&](auto which_branch) {
    VectorWrapper wrap( which_branch );
    wrap.populate();
    for (auto x : some_loop_domain) {
      x.set( wrap.get() );
    }
  };
  if (which_branch) {
    body( std::true_type{} );
  } else {
    body( std::false_type{} );
  }
}

现在在body内,which_branch是一个编译时常量。所有这一切都可能导致分支被吊起,但可能需要:

  auto body = [&](auto which_branch) {
    static_assert( decltype(which_branch){} || !decltype(which_branch){} );
    VectorWrapper<decltype(which_branch)> wrap;
    wrap.populate();
    for (auto x : some_loop_domain) {
      x.set( wrap.get() );
    }
  };

我们在VectorWrapper上对template bool进行实例化,并在true上下文中读取falseconstexpr作为{{1} }}

VectorWrapper中的代码保持相似,除了它读取编译时常量而不是决定采用哪个分支的方法。

这种技术可能非常具有侵入性或根本不具有侵入性。它可能需要看似分支的代码并将分支提升出来。

你可以用类似的方式做许多低级分支的笛卡尔积,你可以生成2 ^ 10个不同的低级例程,在进入循环之前在它们之间进行选择,然后运行1024个循环中的一个。 / p>

答案 2 :(得分:0)

使程序中使用容器类型参数化的容器的部分:

template<class Container>
void criticalPath()
{
    // Create and use Container objects a lot
}


void enterCriticalPath(bool useVector1) {
    if (useVector1) {
        criticalPath<Vector1<SomeType>>();
    } else {
        criticalPath<Vector2<SomeType>>();
    }
}