2012-05-28 77 views
4

使用指针时会有性能影响吗?指针和性能影响?

是不是更好避免使用指针,如果是这样,在什么情况下?显然,他们和参考文献一起帮助减少数据复制。我认为如果指向的数据类型很小,指针的需求就会变小。相反,最好通过指针传递一个大对象,因为指针的开销小于复制对象的开销。

我也想知道除参数/参数以外的区域中的指针吗?

引用通常比这个性能上下文中的指针更好吗?

我赞赏我接近微优化的SO“肮脏”主题,但我正在编写一个非常专注于延迟的应用程序。

+1

对于它的价值,引用的方式完全相同的指针通常实现的,所以不会有性能差异。 –

+0

局部性可能会影响性能,但我不想对此做任何猜测。我的意思是你应该衡量一下。 –

+0

根据您的程序逻辑选择使用引用,指针和值,而不是对性能的期望。然后分析你的代码,如果你发现结果有趣,请告诉我们。 –

回答

6

我知道性能可能很重要,但是语义是更多重要的是:快和错是无用的。

使用指针或引用具有语义含义,比如分享:

void foo(A& a) { 
    a.a = 1; 
    if (a.b != 0) { throw ... } 
    a.b = 0; 
} 

在这种情况下a.b == 0,那么a第一场已经变更,但是不使用其第二。

而且,这种共享可以产生潜在的混叠:

void foo(struct A a, struct A b); 

void foo(struct A* a, struct A* b); 

在第一种情况中,两个结构是必然不同,但在后者它们不是。这种可能的别名可能会阻止优化。

首先关注语义。一旦你得到了他们的权利,你可以调整和衡量在你的特定情况下的影响。

+1

用于'语义'的+1。 –

+0

当downvote没有伴随理由时,我只是讨厌它。我明白,一个特别糟糕的答案可能会很快被低估,它有助于保持干净......但总的来说,没有解释**为什么**没有什么意义。 –

1

通过指针访问数据比直接访问数据要慢一些,但解引用操作非常快,除非您正在执行一些非常具体的重复性数字运算任务,否则这通常不是什么大事。

对于传递大对象或小对象的指针,你完全正确。例如,int *和int的大小可能相同,具体取决于实现。

引用和指针通常与性能相同。但是,如果您习惯于使用const Foo &而不是Foo *,那么编译器通常可以更好地优化代码。

+0

你可以备份你的第一句话吗? –

+0

当然 - 通过指针访问数据需要2个内存读取:1指针找出从哪里读取数据。今天它不是什么大问题,但是它已经是30年前了。 – gbjbaanb

+0

我的意思是支持一个可靠的来源。我怀疑是否有任何收益。 –

4

引用通常比这个性能上下文中的指针更好吗?

是的,当你可以的时候使用引用,当你必须的时候使用引用。 从表现上看,它们是一样的。

通过引用或指针传递大型结构通常会更好,以防止额外的复制,是的。

通过指针或引用访问变量或对象可能是 ,与直接访问变量或对象一样快。这种效率的原因在于微处理器的构造方式。所有在函数中声明的非静态变量和对象都存储在 堆栈中,实际上是相对于堆栈指针进行寻址的。 同样,类 中声明的所有非静态变量和对象都可以通过C++中已知的隐式指针访问为“this”。因此,我们可以得出结论:在一个结构良好的C++ 程序中,大多数变量事实上都是通过指针以某种方式访问​​的。因此,必须设计微处理器以便使指针 有效,这就是它们。

一些缺点,虽然,但他们同时适用于指针和引用:

不过,也有使用指针和引用的缺点。 最重要的是,它需要一个额外的寄存器来保存指针或引用的值 。寄存器是一种稀缺资源,尤其是在32位模式下的 。如果没有足够的寄存器,则每次使用指针 都必须从内存中加载,这会使程序变慢。另一个缺点是 指针的值在变量 指向的时间可以被访问之前的几个时钟周期内被需要。

here is the source。如果你问这个问题,我想你会发现它是一个很好的阅读。

让我们看看一些代码:

int x = 0; 
00412E0E mov   dword ptr [x],0 
    foo(x); 
00412E15 lea   eax,[x] 
00412E18 push  eax 
00412E19 call  foo (4111C2h) 
00412E1E add   esp,4 
    foo(&x); 
00412E21 lea   eax,[x] 
00412E24 push  eax 
00412E25 call  foo (4111BDh) 
00412E2A add   esp,4 

没有区别调用函数时。

void foo (int& x) 
{ 
00411370 push  ebp 
00411371 mov   ebp,esp 
00411373 sub   esp,0C0h 
00411379 push  ebx 
0041137A push  esi 
0041137B push  edi 
0041137C lea   edi,[ebp-0C0h] 
00411382 mov   ecx,30h 
00411387 mov   eax,0CCCCCCCCh 
0041138C rep stos dword ptr es:[edi] 
    x = 3; 
0041138E mov   eax,dword ptr [x] 
00411391 mov   dword ptr [eax],3 
} 

void foo (int* x) 
{ 
004117A0 push  ebp 
004117A1 mov   ebp,esp 
004117A3 sub   esp,0C0h 
004117A9 push  ebx 
004117AA push  esi 
004117AB push  edi 
004117AC lea   edi,[ebp-0C0h] 
004117B2 mov   ecx,30h 
004117B7 mov   eax,0CCCCCCCCh 
004117BC rep stos dword ptr es:[edi] 
    *x = 3; 
004117BE mov   eax,dword ptr [x] 
004117C1 mov   dword ptr [eax],3 
} 

功能里面没有什么区别。

+1

是的,没有。指针算术是一个巨大的性能增益。 –

+0

@Cicada如果你想做指针算术,你必然会使用指针。 –

-2

使用指针没有性能影响。指针的使用非常快。指针被用作大多数(所有?)专业项目中的常用工具。它们用于建立列表,队列,地图,树木,数据库,每个地方。习惯他们!

+0

拥有大量指针需要非常多的间接寻址,这很慢(因为你需要额外的指令,而且你可能会有大量的缓存未命中)。 [Bjarne Stroustrup](http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-Bjarne-Stroustrup-Cpp11-Style)在他的例子中解释了这一点,为什么插入到结束列表通常比插入到矢量的末尾,正是出于这个原因。 – 2012-05-28 13:59:20

+6

我认为即使使用更多惊叹号,您也不会错。 –

1

基于堆的对象最大的问题是,当你需要它们时,它们通常不会被放置在一起,这意味着你需要更多的时间来读取这些对象到CPU缓存中来处理它们(例如if你有10个需要处理的对象,如果它们全部被连续分配一个内存,那么读取会将它们全部移到缓存中,如果它们散布在整个堆中,则需要为它们中的每一个读取) 。公平地说,这适用于所有堆系统,包括垃圾收集后的垃圾收集系统。答案(显然)是将这些对象分配在一起,或者为缓冲区提供可用空间以随时分配它们。

指针的另一个问题是你需要间接访问指针对象。如果你有一个对象数组,你很好 - 它们通常是连续的。但是,如果你有一个指针数组,你必须阅读这些指针才能找出读取对象的位置。现在,指针也可以很快 - 如果你使用指针运算来访问你的连续对象(因为你只需要一个指针来读取所有对象的位置)。

因此,简而言之,问题不在于指针本身,而在于如何组织它们指向的数据。

+2

请注意,您也可以使用指向堆栈中对象的指针,以避免复制大型结构。 –

1

这取决于指向的指针在哪里。如果在堆栈上分配的对象,并且在许多情况下传递指针参数的对象将比指向堆上的对象的指针快。如果你传递的对象在被CPU缓存的大部分机会之前被主动使用,那么性能将大致相同。

关于复制...大多数编译器将使用CPU寄存器来传递指针,这是CPU中速度最快的内存。但是,如果您将通过值编译器将需要复制整个对象,并且还要调用ctor/dtor。

所以我的建议尝试保持在堆栈上的东西,并通过指针/引用。

很难说性能不稳定,可能会在不同的硬件/操作系统/编译器上发生变化,所以我的建议是使用profiler工具来分析代码,以分析缓存未命中,内存碎片,CPU使用率等问题。

http://en.wikipedia.org/wiki/VTune

http://developer.amd.com/tools/CodeAnalyst/Pages/default.aspx