我有一个代码,其中数据结构分别由两个类A1
和A2
的向量表示。
vector<A1> v1;
vector<A2> v2;
我的代码的缓慢部分(大约占总运行时间的80%)包括迭代这些向量并在funA1
和v1
的元素上应用方法funA2
。 v2
。
double result = 0;
for (int i = 0 ; i < v1.size() ; i++)
{
results *= v1[i].funA1();
}
for (int i = 0 ; i < v2.size() ; i++)
{
results *= v2[i].funA2();
}
虽然数据结构使用这两个向量,但直觉宁愿使用混合数据类型A1
和A2
的单个向量。使用两个不同的向量会导致代码中其他地方丢失可读性和维护。
我以为我可以把它们合并成一个矢量。我们的想法是创建父类A1
的{{1}}和A2
兄弟类,并使用A
作为单个向量。我会在班级vector<A> v
中定义虚拟fun
并在A
和A1
中覆盖它,以便我可以
A2
在第一种情况下,编译器可以内联方法double result = 0;
for (int i = 0 ; i < v.size() ; i++)
{
results *= v[i].fun();
}
和funA1
(实际上我自己将它们内联,因为它们是非常简单的函数)。
在第二种情况下,我担心内联是不可能的。它是否正确?
编译器是否设法内联方法funA2
?
当然,性能损失可以忽略不计,我应该对其进行测试,但在尝试进行所有修改之前,我想知道它是否值得先验。
答案 0 :(得分:4)
答案是明确的“可能”。判断编译器是否实际内联调用的唯一方法是编译它,然后查看汇编列表。这一切都取决于fun
的复杂性(其中“复杂性”是一个非常符合编译器的指标)。
就个人而言,除非这是您申请中最关键的部分,否则我不会担心。函数调用并不昂贵。与往常一样,为了清晰和可读性而编写代码,然后进行优化。
答案 1 :(得分:2)
简短的回答是&#34; no&#34;。您对具有不同类型的向量实例的要求不利于编译器能够内联虚函数调用。
假设A1
和A2
来自A
,A1
和A2
的实例无法安全地存储到std::vector<A>
中。这样做的结果将是对象切片(例如,仅将每个对象的A
部分复制到std::vector<A>
)。如果A
是一个抽象类(即具有任何纯虚函数),则不会编译这样的东西。如果A
不是抽象的,那么std::vector<A>
中所有对象的静态类型都是A
- 因此调用v[i].fun()
会为每个元素调用A::fun()
。因此永远不会调用A1::fun()
和A2::fun()
。
如果在std::vector<A *>
中存储对象的地址,则v[i]->fun()
将根据对象的实际类型调用虚函数。但是,这不能由编译器内联,因为要调用的函数是在运行时根据向量中每个对象的实际类型确定的(即编译器不知道每个对象的类型)。如果您使用std::vector<std::unique_ptr<A>>
,也可以使用相同的评论。
注意:在A
或std::vector<A *>
中存储指向多态类std::vector<std::unique_ptr<A>>
的指针存在其他问题,例如需要A
拥有虚拟析构函数,对于要正确管理的对象的生命周期等。如果您打算使用这种方法,您需要考虑这些事情 - 否则结果将是未定义的行为。
如果您希望帮助优化代码的性能,则需要提供更多信息(例如,有关您如何分析的信息,类型A
,A1
,{{1}的信息是,功能正在做什么,等等。)
答案 2 :(得分:1)
您建议将A::fun
设置为虚拟,并且有两个覆盖A1::fun
和A2::fun
,只有一个调用,然后期望编译器内联两者在那次电话会议中A1::fun
和A2::fun
?
这很乐观。但并非不可能。
答案 3 :(得分:0)
从计算速度的角度来看,最好使用单独的向量。内联虚拟函数需要虚拟化,虽然编译器经常这样做,但很难提供任何保证。
除此之外,还有缓存(指令和数据),预取器,分支预测器 - 所有这些都可以从同质遍历中受益。
如果你绝对需要合并向量,你可以尝试将一个向量相加,这样一种类型的对象就会聚集在一起。也许你甚至可以对大型向量的两个部分进行两次单独的遍历,并避免使用virtaul调用。
在任何情况下,您最好的选择是描述您可以做的一切,并确定性能损失是否可接受(或根本不明显)。
同样@Someprogrammerdude提到,在查看代码时会想到std::accumulate
:)
答案 4 :(得分:-2)
编译器将在Class中内联简单函数。例如:一个函数只有返回。所以对于你的问题,我认为不是。