考虑以下情况:
class MyFoo {
public:
MyFoo();
~MyFoo();
void doSomething(void);
private:
unsigned short things[10];
};
class MyBar {
public:
MyBar(unsigned short* globalThings);
~MyBar();
void doSomething(void);
private:
unsigned short* things;
};
MyFoo::MyFoo() {
int i;
for (i=0;i<10;i++) this->things[i] = i;
};
MyBar::MyBar(unsigned short* globalThings) {
this->things = globalThings;
};
void MyFoo::doSomething() {
int i, j;
j = 0;
for (i = 0; i<10; i++) j += this->things[i];
};
void MyBar::doSomething() {
int i, j;
j = 0;
for (i = 0; i<10; i++) j += this->things[i];
};
int main(int argc, char argv[]) {
unsigned short gt[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
MyFoo* mf = new MyFoo();
MyBar* mb = new MyBar(gt);
mf->doSomething();
mb->doSomething();
}
有没有先天的理由相信mf.doSomething()会比mb.doSomething()运行得更快?如果可执行文件是100MB,那会改变吗?
答案 0 :(得分:2)
没有理由相信一个会明显快于另一个。如果gt
(例如)足够重要,那么您可能会从以下方面获得更好的性能:
int j = std::accumulate(gt, gt+10, 0);
然而,只有10个元素,似乎不太可能存在可衡量的差异。
答案 1 :(得分:2)
因为任何东西都可以修改你的gt
数组,所以可能会对MyFoo执行一些不可用于MyBar的优化(但是,在这个特定的例子中,我没有看到任何优化)
由于gt生活在本地(我们曾经称之为DATA段,但我不确定它是否仍然适用),而things
仍然存在于堆中(以及mf和mb的其他部分) )可能有一些内存访问&amp;处理things
的缓存问题。但是,如果您在本地创建了mf(MyFoo mf = MyFoo()
),那么这将是一个问题(即things
和gf
在这方面将处于平等地位。)
可执行文件的大小应该有所不同。数据的大小可能,但在大多数情况下,在第一次访问后,两个数组都将在CPU缓存中,并且应该没有区别。
答案 2 :(得分:2)
MyFoo::DoSomething
可能比MyBar::DoSomething
稍微快一点
这是因为当事物本地存储在数组中时,我们只需要取消引用它来获取内容,我们就可以立即访问数组。当事物存储在外部时,我们首先需要取消引用它,然后我们需要在我们访问数组之前取消引用。所以我们有两条加载指令。
我已将您的源代码编译为汇编程序(使用-O0),MyFoo::DoSomething
的循环如下所示:
jmp .L14
.L15:
movl -4(%ebp), %edx
movl 8(%ebp), %eax //Load this into %eax
movzwl (%eax,%edx,2), %eax //Load this->things[i] into %eax
movzwl %ax, %eax
addl %eax, -8(%ebp)
addl $1, -4(%ebp)
.L14:
cmpl $9, -4(%ebp)
setle %al
testb %al, %al
jne .L15
现在DoSomething::Bar
我们有:
jmp .L18
.L19:
movl 8(%ebp), %eax //Load this
movl (%eax), %eax //Load this->things
movl -4(%ebp), %edx
addl %edx, %edx
addl %edx, %eax
movzwl (%eax), %eax //Load this->things[i]
movzwl %ax, %eax
addl %eax, -8(%ebp)
addl $1, -4(%ebp)
.L18:
cmpl $9, -4(%ebp)
setle %al
testb %al, %al
jne .L19
从上面可以看出有双重负荷。如果this
和this->things
地址差异很大,则问题可能会更加复杂。然后它们将存在于不同的缓存页面中,并且CPU可能必须在主存储器中进行两次拉取才能访问this->things
。当它们属于同一个对象时,当我们得到此对象时,我们会在this->things
的同时获得this
。
Caveate - 优化器可能会提供一些我没想过的快捷方式。
答案 3 :(得分:1)
最有可能的额外取消引用(MyBar
,必须获取成员指针的值)在性能上是无意义的,特别是如果数据数组非常大。
答案 4 :(得分:0)
可能会慢一些。问题是您访问的频率。你应该考虑的是你的机器有一个固定的缓存。当加载MyFoo以调用DoSomething时,处理器可以将整个数组加载到缓存中并读取它。但是,在MyBar中,处理器首先必须加载指针,然后加载它指向的地址。当然,在你的示例main中,它们可能都在相同的缓存行中或者足够接近,对于更大的数组,负载的数量不会随着一个额外的解引用而显着增加。
然而,总的来说,这种影响远非可以忽略。当您考虑取消引用指针时,与实际加载指向的内存相比,该成本几乎为零。如果指针指向某个已经加载的内存,则差异可以忽略不计。如果没有,你有一个缓存未命中,这是非常糟糕和昂贵的。此外,指针引入了别名问题,这基本上意味着您的编译器可以对其执行更少的乐观优化。
尽可能在对象内分配。