我的问题的背景是我正在尝试创建一个惰性网格结构,其中网格的区域仅在需要时被实例化,否则它们在查询时返回默认值.
稍微解决问题,请考虑以下我的情况模型:
struct Container {
std::vector<Base> data;
float get(int indexOuter, int indexInner) {
return data[indexOuter].get(indexInner);
}
}
我想在某些情况下将Base :: get函数存根,以便始终返回相同的值,而在其他情况下,我想在某些数组中返回值.我想象两种可能的解决方案
第一个解决方案是在Base上使用一个标志,即
struct Base {
std::vector<float> data;
float get(int indexInner) {
if (data.empty()) return 0;
return data[indexInner];
}
}
此解决方案将涉及在对象进入“存根模式”时销毁数据(调整大小为零),并在对象再次具体时重新创建数据.
我能想到的第二个解决方案是继承,即
struct Container {
std::vector<Base*> data;
float get(int indexOuter, int indexInner) {
return data[indexOuter]->get(indexInner);
}
}
struct Base {
virtual float get(int indexInner) = 0;
}
struct Stub : public Base {
float get(int indexInner) {
return 0;
}
}
struct Concrete : public Base {
std::vector<float> data;
float get(int indexInner) {
return data[indexInner];
}
}
然后在必要时在Container的数据数组中用Stub的实例替换Concrete的实例,反之亦然(注意确保干净的破坏).
问题是表现之一. Container :: get将被称为每秒1000次,也许更多.但是,进入/退出“存根模式”的基站的发生频率要低得多.
使用上面提出的两个解决方案,第一个解决方案涉及一个额外的if-then子句,而第二个解决方案涉及从Base指针到对象的间接,以及从抽象方法到派生实现的间接.
哪些解决方案能够提供最佳性能?我还没有考虑过其他更高性能的解决方案吗?
最佳答案 大多数现代处理器都不喜欢条件代码,特别是如果它不是“可预测的”(换句话说,您的数据有时会被填充,有时则不会.
跳过指向函数的指针通常比条件分支更快(考虑到你还需要检查它是否是存根[尽管你可以使用data.empty()来检查编译器很简单]).
因此,如果不对两种解决方案进行基准测试,我的猜测是虚拟Stub和Concrete会更快.但是,它将取决于用例.
Stub实现也将占用少量数据.另一方面,如果这是在std :: vector< Base *>中使用指针的唯一原因. data;,那么你可能想要考虑使用isStubbed(或data.empty())并使用std :: vector< Base>相反 – 整体保持间接水平 – 根据具体情况可以更好.
最终,如果它对性能很重要,那么您将需要实现两者,运行具有不同负载模式的基准测试,并测量时间.还要查看每个运行的分析数据,以了解代码花费时间的位置.