【问题标题】:When should and shouldn't you break away from OOP for speed/performance?什么时候应该和不应该为了速度/性能而脱离 OOP?
【发布时间】:2009-11-25 19:44:00
【问题描述】:

在他们的 Android 开发者文章中,Google 声明您通常应该使用 getter 和 setter 声明公共变量而不是私有变量,以提高嵌入式设备的性能(我认为函数调用比仅仅写入地址更昂贵)。

我想知道 - 应该在多大程度上牺牲性能来坚持 OOP 范式?在哪些其他情况下,优化意味着脱离“良好”的编码实践?

【问题讨论】:

  • 这个问题令人困惑,因为您已将其标记为“android”和“java”。是关于android的问题还是关于java的问题?对于 android,他们的建议适用的原因是他们没有即时编译器,这使他们对 Java/Hotspot 处于可怕的劣势。
  • @Kevin,我认为问题是关于在 on android 上运行的 java,因此同时标记两者是有意义的。这不仅仅是关于 java 或只是关于 android。
  • 这里有一个问题:getter 和 setter 在 OOP 范式中并不比公共变量更重要。

标签: java android performance oop


【解决方案1】:

将其构建为可维护,然后将其修改为更快。

如果您从黑客开始 - 您可能不需要 - 维护通常是一场噩梦。

【讨论】:

  • 我正要回答同样的问题。添加使用分析来检测待改进的候选者。
【解决方案2】:

只要性能没有明显受到影响,就应该使用适当的抽象。

过早的优化很尴尬。

【讨论】:

  • -1,这与感知性能无关。这一次,这条规则不适用:我们在一个嵌入式设备上,每个函数调用都会消耗电池寿命。 Google 团队本身建议强烈关注此问题。
  • 一般来说,你是完全正确的。但在嵌入式世界中,我想你仍然需要尽可能少的 CPU/内存来做尽可能多的事情
  • 嗯,当然,使用更少的 CPU / 内存 == 使用更少的电池。
【解决方案3】:

我讨厌公司发表这样的声明,但不提供统计数据来量化问题。

也就是说:权衡并不是真正的 OO 范式。如果您同时拥有 get 和 set,并且这是您的类设计的固有部分,那么从 OO 的角度来看,将变量设为 public 是完全有效的。如果性能提升足够重要,我会在这些情况下公开变量,但不是其他情况。

【讨论】:

  • +1 要求对他们的断言进行量化。在性能方面,经验法则是移动的目标。我想知道,如果方法是最终的,编译器会内联调用吗?
【解决方案4】:

从某种意义上说,我猜这取决于性能对您的应用程序的重要性,以及通过使用您可能认为不“好”的编码实践,您真正看到了多少性能提升。但话虽如此,我总是鼓励开发人员不要固守教条,继续用同样的方式编写代码,因为这是你被教导的方式是“正确的”。如果您很好地记录代码并使用一致且可解释的编码实践,那么我认为做一些可能不适合“OOP 范式”的事情是完全可以的。最后,它是关于可维护的工作代码。

【讨论】:

  • 商定、一致性和文档比教条更有帮助。
【解决方案5】:

一般来说,所有形式的工程都需要权衡取舍。您需要将一种方法的成本与另一种方法的成本进行比较。

以牺牲其他任何东西为代价来牺牲可维护性总是很棘手,因为很容易低估不可维护代码的未来成本。

“程序的第一条规则 优化:不要这样做。第二 程序优化规则(对于 仅限专家!):不要这样做。 - 迈克尔·杰克逊

也就是说,有一些观点认为拥有访问器的好处实际上非常小。您有多少次不得不用实际执行某些操作的方法替换刚刚读取/写入字段的 getter 或 setter?鉴于这在实践中似乎是多么罕见,有人可能会争辩说,对所有事情都使用访问器是“过早的悲观化”。

【讨论】:

    【解决方案6】:

    大多数时候,您不应该在速度重要之前进行优化,但这次您应该这样做。

    这里的主要问题不是速度,而是电池。

    现在,Google 确实告诉用户避免使用 getter 和 setter,但仅限于内部使用,API 仍应仅公开 getter 和 setter。您应该避免创建对象,最好清空/清除对象并尽可能重用它。无论如何,避免创建太多活动,因为无线连接是所有过程中最昂贵的过程。

    也就是说,你一定要聪明。您不能一直遵循这些建议,您的代码很快就会变得一团糟,而且我们编写 Java 并不是为了感觉像在 Fortran 中编码。

    一个很好的平衡是首先创建一个在模拟器上运行良好的干净/标准代码。然后,当它工作得很好时,选择资源密集型部分,大多数时候循环,你可以减少它。 但在将您的应用发布到 android 市场之前,请先这样做。 对于许多应用已经杀死了 android 手机,我的应用即使从充满电的早晨开始也无法忍受一天。

    【讨论】:

      【解决方案7】:

      函数调用涉及什么? (我只用了 MIPS,所以其他架构可能会有所不同)。

      • 跳转到函数
      • 将寄存器保存到堆栈中
      • 执行函数[body]
      • 将结果复制到返回寄存器(如果需要)
      • 恢复寄存器
      • 返回调用者

      跳转/返回可能会导致 CPU 需要重置管道(我怀疑这可能不是现代 CPU 的问题,通常它是预测流量控制)。如果主体只是一个集合或获取,则可能不需要保存/恢复寄存器。如果您在函数内部,您通常希望分别在函数调用之前和之后将返回地址推/拉到堆栈,否则您将陷入无限循环。

      将其与保存到地址或从地址读取(通常是一条指令对一打以上)进行比较。

      【讨论】:

        【解决方案8】:

        我发现 Google 的编码指南有点误导,如果不是完全有问题的话。

        他们宣传基本的 OOP 范式,例如针对接口而不是实现进行编程,还宣传缓存字段查找等实践以节省计算时间。

        我发现它们有问题不是因为我认为它们不会产生任何影响,而是我发现它们有问题是因为它优化了应用程序花费 20% 时间的 80% 代码,而不是相反 (这些数字显然是虚构的,但我想每个人都同意大多数 Android 应用程序不会将大部分时间花在评估循环表达式上。

        Android 应用真正大部分时间都花在视图膨胀和布局上。如果你甚至构建了中途复杂的用户界面,并且曾经分析过你的应用程序,你会意识到这些才是真正让你的应用程序在一天结束时运行缓慢的原因。与视图膨胀等操作相比,花时间优化循环变量似乎几乎是荒谬的。

        有趣的是,谷歌在附录中显示了一些数字,从而荒谬地提出了自己的指导方针:

        从 XML 膨胀 1 个视图:22,000

        调用空接口方法:15

        现在运行一个分析器,看看您的应用在 onLayout()、Adapter.getView() 或 inflate() 中花费了多少时间。你可能会感到惊讶。

        【讨论】:

          【解决方案9】:

          对于他们正在谈论的特定环境和应用程序,这是一个很好的建议。不过,我认为将其推广到其他情况是没有意义的。总的来说,Android 应用程序将是小型项目,几乎没有相互依赖关系。因此,使用公共变量作为某个状态的接口并没有什么大不了的。

          特别是,如果您要在琐碎的传递 getter/setter 方法和公共变量之间进行选择,那么您已经在“打破封装”的道路上走得最远,所以您最好不要支付额外的性能没有获得灵活性的打击。鉴于它是 Java,无论如何,“构建世界”或多或少是当任何事情发生变化时的常见状态。

          对于更大的系统,使用像 C++ 这样的语言,您可能会从像这样的薄抽象层中获得更多好处。

          【讨论】:

            【解决方案10】:

            当且仅当测试表明系统无法满足其规范时,才应在性能祭坛上牺牲设计。

            【讨论】:

              【解决方案11】:

              我倾向于坚持“良好”的编码实践,对我来说,这意味着可读性,只要我可以。只有在性能至关重要的极少数情况下,我才会偏离该原则。通常只有在某些东西被证明在性能方面很糟糕时。

              至于 getter 和 setter;使用 getter 和 setter 的优点是您可以记录对参数的更改,并进行一些输入验证。如果这对您来说无关紧要,并且性能确实如此关键,那么您可以打破抽象。但是我真的不相信 getter 和 setter 不能通过使用例如 final 类来优化。

              【讨论】:

                【解决方案12】:

                对于 Android,将性能考虑在内很重要。如果您执行诸如保持较大的静态引用之类的操作,Android 框架可能会在用户启动另一个活动或打开键盘(这会重新加载您的视图)时强制退出您的应用程序。达到这个限制并不需要太多。这确实是一个了解您正在开发的环境的问题。

                在非移动 Java 开发的情况下,首先编写易于维护的代码,然后在需要时进行优化是有意义的。

                Google 并没有说要避免 getter/setter 并将您的变量公开给 Android。他们确实建议不要使用类中的 getter/setter 来访问其自己的字段。来自http://developer.android.com/guide/practices/design/performance.html

                "避免使用内部的 Getter/Setter

                在 C++ 等本地语言中,通常使用 getter(例如 i = getCount())而不是直接访问字段(i = mCount)。这对于 C++ 来说是一个很好的习惯,因为编译器通常可以内联访问,如果需要限制或调试字段访问,可以随时添加代码。

                在 Android 上,这是一个坏主意。虚拟方法调用很昂贵,比实例字段查找要贵得多。遵循常见的面向对象编程实践并在公共接口中使用 getter 和 setter 是合理的,但在类中您应该始终直接访问字段。”

                【讨论】:

                  【解决方案13】:

                  数以百万计的工时浪费在那些愚蠢的小吸气剂和二传手上。在大多数情况下,它们是不需要的。从公共字段开始,如有必要,您可以随时更改它们的用法以获取/设置方法。

                  【讨论】:

                  • 已经花费了数百万工时,但有一次,当您的字段正在更改并且您不知道为什么时,您唯一的希望是设置器上的良好日志条目。最好的问候,阿里
                  • 除非你不能在不改变所有使用你的类的代码的情况下从 public 转到 get/set 方法。除非您可以控制使用该类的所有代码,否则您将陷入困境。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-10-10
                  • 1970-01-01
                  • 2020-07-16
                  • 2018-06-26
                  • 2015-12-05
                  • 1970-01-01
                  相关资源
                  最近更新 更多