【发布时间】:2014-05-13 09:55:29
【问题描述】:
我有一个库,里面有很多小对象,现在它们都有虚函数。它达到了这样的程度,即指向虚函数表的指针的大小可以超过对象中有用数据的大小(它通常可以只是一个带有单个float 的结构)。对象是稀疏图上数值模拟中的元素,因此不能轻易合并/等。
我不太关心虚函数调用的成本,而是存储成本。发生的事情是指向虚函数表的指针基本上是在降低缓存的效率。我想知道将类型 id 存储为整数而不是虚函数是否会更好。
我不能使用静态多态性,因为我的所有对象都在一个列表中,并且我需要能够对由索引选择的项目执行操作(这是一个运行时值 - 因此无法静态确定类型)。
问题是:在给定类型列表(例如在类型列表中)和类型索引的情况下,是否存在可以从接口动态调用函数的设计模式或通用算法?
接口已定义并且没有太大变化,但是将来会由库的(可能不太熟练的)用户声明新对象,这样做不需要很大的努力。性能是最重要的。可悲的是,没有 C++11。
到目前为止,我可能有一个愚蠢的概念证明:
typedef MakeTypelist(ClassA, ClassB, ClassC) TList; // list of types
enum {
num_types = 3 // number of items in TList
};
std::vector<CommonBase*> uniform_list; // pointers to the objects
std::vector<int> type_id_list; // contains type ids in range [0, num_types)
template <class Op, class L>
class Resolver { // helper class to make a list of functions
typedef typename L::Head T;
// specialized call to op.Op::operator ()<T>(p)
static void Specialize(CommonBase *p, Op op)
{
op(*(T*)p);
}
// add a new item to the list of the functions
static void BuildList(void (**function_list)(CommonBase*, Op))
{
*function_list = &Specialize;
Resolver<Op, typename L::Tail>::BuildList(function_list + 1);
}
};
template <class Op>
class Resolver<Op, TypelistEnd> { // specialization for the end of the list
static void BuildList(void (**function_list)(CommonBase*, Op))
{}
};
/**
* @param[in] i is index of item
* @param[in] op is a STL-style function object with template operator ()
*/
template <class Op>
void Resolve(size_t i, Op op)
{
void (*function_list[num_types])(CommonBase*, Op);
Resolver<Op, TList>::BuildList(function_list);
// fill the list of functions using the typelist
(*function_list[type_id_list[i]])(uniform_list[i], op);
// call the function
}
我还没有查看程序集,但我相信如果设为静态,函数指针数组的创建几乎可以免费进行。另一种选择是使用在 typelist 上生成的二叉搜索树,这将启用内联。
【问题讨论】:
-
旁注:您也可以将 type_id_list 设为
vector<uint8_t>或类似的,以获得更好的缓存使用率,> 255 种行为即使对于最复杂的模拟也很多(如果您确实超过了它,还有uint16_t)。 -
当您用虚拟指针所需的空间换取
enum(本质上是int)的空间时,您几乎不会获得太多收益。 Flyweight pattern 让您假装许多小的原语是真正的对象,但我不确定如何在您的对象是多态的情况下应用它,并且您宁愿不保留额外的指针。 -
size_t可以,但指针不行吗?如果您可以接受 32 位的编译,那么它们很可能是相同的大小。 -
能否将类型信息编码到索引中?因此,使用给定的索引,您可以获得数据对象和调用该对象的函数。
-
@delnan 是的,实际上整型时会根据
num_types的值选择type_id_list的整数类型。
标签: c++ design-patterns virtual-functions typelist