40

Go:指针能优化性能吗?【译】

 5 years ago
source link: https://studygolang.com/articles/17832?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

趁着元旦休假+春节,尝试把2018年期间让我受益的一些文章、问答,翻译一下。

欢迎指正、讨论,希望对你也有所帮助。

原文链接: Go: Are pointers a performance optimization?

以下,开始正文

过去几周时间,我回答了许多关于使用指针优化性能的问题。似乎很多人在这方面都感到困惑。这也可以理解,指针确实是个复杂的话题。 希望这篇文章对你有所帮助。

简而言之:不是使用指针就一定代表着性能优化。

如果要彻底解释这篇文章涉及的所有细节,那篇幅可能会长到没人愿意看。所以,我精简了一下,试图用中等篇幅也能涵盖想说明的高级概念。

阅读时需要说明一点:本文讨论的是微优化,性能优化都是极其细微的。在进行微优化之前,需先进行基准测试,否则很可能看不到明显的效果。代码易读性才是第一要义。

什么是指针?

指针底层代表的就是内存地址,指针解引用可以访问到内存存储的具体数据值。

应用指针之后是如何起到性能优化作用的?

函数调用时,变量传递实际上是将变量重新复制了一份,传给函数。多数情况下,指针都要比变量本身占用更小的空间。

通常,指针大小和系统的架构体系保持一致。32位系统就是32bit,64位系统,即是64bit大小。像bool、float等标量类型,占用的空间都小于等于指针;而多字段的符合类型,指针占的空间更少。

所有,我们的想法就基于复制指针比复制原值更高效。一定程度上,这样想没问题。但是性能问题涉及广泛,除了复制成本之外,还有很多因素要考虑。

指针是否会对性能产生负面影响?

会。主要出于两方面的考量:

  1. 解引用虽然耗能很小,但积少成多,不得不虑。
  2. 通过指针共享的数据,是放在堆上的。堆数据的清理是GC负责的,这也会产生开销。随着堆上数据增多,GC的工作量变大,对项目的性能影响也不容忽视。

堆与栈

堆和栈是两个让人头疼的概念,但是我们不得不直面它们。我在这尽量用简短的篇幅讨论完。如果没能快速理解也没关系,我曾经也没能很快理解。

栈:函数局部空间

每当函数被调用,都会分配一块栈空间来存储函数局部变量。函数占用的栈空间大小在编译时已经确定。函数返回时,这块空间就给下一个函数调用使用了,也无需立刻清理。虽然这个分配和使用过程要耗费资源,但相对来讲,消耗很小。

堆:共享数据空间

如上所述,函数返回后,局部变量会被销毁(译者注:空间被复用或者彻底回收,局部变量不再存在)。如果返回是非指针变量,返回值会被复制给调用者,存在于调用者的栈空间中。

但是,如果返回的是指针(译者注:也就是函数局部空间的地址),指针指向的数据就要保存在栈空间之外,这样才能保证函数返回后,数据仍然可以访问。这就是堆的用处。

与堆相关的性能问题有这些:

  1. 堆空间需要从runtime申请,虽然开销很小,也不能不考虑;
  2. 如果运行时没有足够空间,就需要系统调用了,这是额外的开销;
  3. 一旦数据占用了堆空间,就要一直占用到没有指针再指向它。这时候,需要GC来清理。GC会找到堆中所有没有被饮用的值,标记为空闲(译者注:请参考Go垃圾回收的三色标记)。垃圾越多,GC耗时越大,系统性能越差。

那为什么还要用指针?

  1. 指针能修改传参,提供了一种共享数据的方式。
  2. 指针能区分零值,确定你的变量是否被赋值了。

总结

指针可以节省复制的开销,但同时要考虑解引用和垃圾回收带来的影响。在我看来,性能分析结果显示复制是瓶颈之前,不应该考虑把指针作为优化方案。计算机在复制方面的速度可是极快的。

希望这篇文章可以让你认识到指针是可以派上用场的,但也不要因为要用而用。

默认还是使用值而不是地址吧。除非语法满足不了你了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK