模块化编程会影响计算时间吗?


19

每个人都说我应该使代码模块化,但是如果我使用更多的方法调用而不是更少但更大的方法,效率会降低吗?Java,C或C ++有什么区别?

我知道编辑,阅读和理解起来更容易,尤其是在小组中。那么,与代码整理优势相比,计算时间损失微不足道吗?


2
问题是,您节省的处理时间将花费多长时间才能花在更困难的维护上。答案完全取决于您的应用程序。
Blrfl 2013年

2
许多好的问题会根据专家的经验产生某种程度的意见,但是对这个问题的答案往往几乎完全基于观点,而不是事实,参考资料或特定的专业知识。
蚊蚋

10
还值得指出的是,对函数或方法调用的计算代价微乎其微,以至于即使在具有许多函数和方法调用的大型程序中,进行这些调用也不在图表上进行排名。
greyfade13年

1
@greyfade:对于直接跳转是正确的,但是额外的间接预测跳转可能会花费例如程序总运行时间的3%(这只是我最近检查过的程序中的一个数字-虽然可能不具有代表性)。根据您所在区域的不同,您可能会认为是否重要,但它确实在图表中注册了(当然,它至少部分垂直于模块化)。
Maciej Piechotka

4
过早的优化是万恶之源。线性代码比模块化代码要快一些。模块化代码比意大利面条代码快得多。如果您针对线性代码而没有一个非常完整的项目,那么最终会得到意大利面条代码,我保证。
SF。

Answers:


46

是的,这无关紧要。

计算机是不倦的,近乎完美的执行引擎,其运行速度是大脑无法比拟的。尽管函数调用会在程序的执行时间上增加可测量的时间,但与下一个参与代码的人的大脑需要解开难以理解的例程的大脑所需的额外时间相比,这可算是没有了甚至开始了解如何使用它。您可以开个玩笑来尝试计算-假设您的代码只需要维护一次,并且只花了半个小时就能使某人熟悉该代码。以您的处理器时钟速度进行计算:代码必须运行多少次才能实现梦想呢?

简而言之,在99.99%的时间内,完全可怜的CPU是完全错误的。对于极少数情况,请使用探查器。难道认为你能发现这些情况下-你不能。


2
虽然我同意大多数情况下它是过早的优化,但我认为您的观点是不好的。时间在不同情况下是相对的,您不能像您那样简单地进行计算。
Honza Brabec

28
+1只是表示“对CPU感到遗憾”,因为它很好地强调了过早优化中的错误观念。
Michael Borgwardt

4
高度相关: 每天计算机的运行速度如何? 去你的方式,以避免对“速度”的几个函数调用就像是在他们的过程中救一个人1分钟总的用自己的方式走出去的整个生活。简而言之:甚至不值得花时间考虑。
BlueRaja-Danny Pflughoeft13年

7
+1如此雄辩地售出了许多缺失的东西,但是您忘了增加最重要的重量,这使可怜的CPU变得更加严重:忽略了一次维护所浪费的金钱和时间;如果花了1秒钟,那么那里还有一个阴险得多的水槽。错误风险。后来的维护者更改代码越是混乱和困难,他在其中实施错误的风险就越大,这可能会给用户带来不可避免的巨大不可避免的成本损失,并且任何错误都会导致后续维护(这可能会导致bugs ...)
Jimmy Hoffa

我的一位老老师曾经说过:“如果您正在考虑是否要过早地进行优化,就停在这里。如果这是正确的选择,那么您就会知道。”
2013年

22

这取决于。

在Web编程这样缓慢的世界中,一切都以人类的速度进行,而方法繁重的编程中,方法调用的成本与方法所完成的处理成本相当或超过,可能并不重要。 。

在嵌入式系统中,用于高速率中断的编程和中断处理程序无疑很重要。在那种环境下,“内存访问便宜”和“处理器无限快”的常用模型崩溃了。我已经看到大型机面向对象的程序员编写他的第一个高速率中断处理程序会发生什么。不好看

几年前,我在实时FLIR图像上进行了非递归的8路连通性Blob着色,当时使用的是像样的处理器。第一次尝试使用了子例程调用,而子例程调用的开销使处理器存活了下来。(4个调用PER PIXEL x每帧64K像素x每秒30帧=您知道了)。第二次尝试将子例程更改为C宏,并且不降低可读性,并且一切都是玫瑰。

您必须对所从事的工作以及所要从事的环境具有“硬性”认识。


请记住,大多数现代编程问题最好使用许多面向对象的,方法友好的代码来解决。您将在嵌入式系统环境中知道。
凯文-恢复莫妮卡

4
+1,但有一个警告:通常情况下,与人相比,优化编译器通常会在内联,循环展开以及标量和向量优化方面做得更好。程序员仍然需要很好地了解语言,编译器和机器才能利用这一点,所以这并不是魔术。
2013年

2
没错,但是您编写逻辑并对其进行了概要分析以找到算法中有问题的部分之后就对代码进行了优化。您不是为了提高性能而是为了提高可读性而开始编写代码。宏将帮助您保持代码的可读性。
Uwe Plonus 2013年

2
即使在嵌入式系统编程中,也要从编写清晰正确的代码开始,然后在必要时在性能分析的指导下开始进行优化。否则,很容易进行大量工作,而对性能/代码大小没有任何实际影响,这只会使源代码难以理解。
Donal Fellows 2013年

1
@detly:是和否。在语言所施加的限制范围内,现代编译器通常可以做得更好。程序员知道语言所施加的限制是否适用于当前的特定情况。例如,语言标准要求使用Cn和C ++编译器,以使程序员能够做一些非常病理的事情,并生成仍然可以正常工作的代码。程序员可能知道他没有足够的疯狂,愚蠢或冒险的精神去做那些事情,并且进行了否则不安全的优化,因为他知道在这种情况下是安全的。
John R. Strohm 2013年

11

首先:较高语言的程序供人类阅读,而不是由机器阅读。

因此编写程序,以便理解它们。不要考虑性能(如果您严重遇到性能问题,则可以对应用程序进行概要分析并在需要的地方增强性能)。

即使调用方法或函数确实花费一些开销也没关系。如今,编译器应该能够将您的代码编译成高效的机器语言,从而使生成的代码对于目标体系结构而言是高效的。使用编译器的优化开关来获取有效的代码。


5
我要说的是,我们应该以让其他人理解它们的方式编写程序。
Bartlomiej Lewandowski

开发人员本人是第一个必须阅读程序的人。那就是我写信给的原因。如果其他人也可以阅读该程序,那就很好了,但是(在我看来)没有必要(首先)。如果您在团队中工作,那么其他人也应该理解该程序,但是我的意图是那个人必须阅读程序,而不是计算机。
Uwe Plonus

5

通常,当您否则会拥有较大的功能并将其拆分为许多较小的功能时,这些较小的功能将被内联,因为在这种情况下,内联的唯一缺点(过多重复相同的指令)并不重要。这意味着您的代码将像您编写了一个大型函数一样工作。

如果由于某种原因未内联它们,这将导致性能问题,则应考虑手动内联。并非所有应用程序都是具有巨大固有延迟的联网CRUD形式。


2

可能没有计算成本。通常,过去10到20年左右的编译器/ JIT处理的内联函数都很好。对于C / C ++,通常仅限于“不可插入”的函数(即,函数的定义在编译期间可用于编译器-即位于同一文件的标头中),但是LTO的当前技术克服了这一问题。

是否应花时间进行优化取决于您正在研究的领域。如果您处理花费大部分时间等待输入的“正常”应用程序,那么除非应用程序“感觉”缓慢,否则您可能不必担心优化。

即使在这种情况下,您也应该在进行微优化之前专注于很多事情:

  • 问题出在哪里?通常,由于我们以不同的方式阅读源代码,因此人们很难找到热点。我们有不同的操作时间比率,我们按顺序执行,而现代 处理器 没有
  • 每次都需要进行一些计算吗?例如,如果您从数千个参数中更改单个参数,则可能只想计算受影响的一部分而不是整个模型。
  • 您是否使用最佳算法?从更改O(n)O(log n)可能会产生比通过微优化可以实现的任何事情更大的影响。
  • 您使用适当的结构吗?假设您List在需要时使用a ,HashSet以便O(n)在可能时进行查找O(1)
  • 您是否有效地使用并行性?当前,即使是手机也可以具有4个或更多核,因此可能很想使用线程。但是,它们并不是同步的灵丹妙药,因为它们具有同步成本(更不用说如果问题是受内存限制的,那么它们还是毫无意义的)。

即使你决定,你需要进行微优化(这实际上意味着你的软件在高性能计算中,嵌入式或者只是使用非常大量的人-否则维修的额外费用克服了计算机时间成本),你需要以确定要加速的热点(内核)。但是您可能应该:

  1. 确切了解您正在使用的平台
  2. 确切了解您正在使用的编译器以及它可以执行的优化以及如何编写惯用代码来实现这些优化
  3. 考虑一下内存访问模式以及可以容纳多少缓存(确切的大小,您可以从第1点知道)。
  4. 然后,如果您受到计算的束缚,请考虑重新组织计算以保存计算

最后一点。通常,方法调用存在的唯一问题是分支跳转程序未预测的间接跳转(虚拟方法)(不幸的是,间接跳转是最困难的情况)。然而:

  • Java有一个JIT,它在许多情况下可以预测类的类型,因此可以预先预测目标,因此在热点中,您应该没有很多问题。
  • C ++编译器经常执行程序分析,至少在某些情况下可以在编译时预测目标。
  • 在两种情况下都可以预测目标,内联应该起作用。如果编译器无法执行内联的机会,我们也不会。

0

我的答案可能不会在现有答案的基础上扩大太多,但我觉得我的两分钱可能会有所帮助。

首先 是的,对于模块化,您通常会放弃一定程度的执行时间。用汇编代码编写所有内容将为您提供最佳速度。那个...

你知道YouTube吗?可能是现有带宽最高的站点,还是仅次于Netflix?他们用Python编写了大部分代码,这是一种高度模块化的语言,并非为实现一流性能而专门设计的。

关键是,当出现问题时,并且用户抱怨视频加载缓慢,在很多情况下,这种缓慢最终归因于Python的缓慢执行速度。但是,Python的快速重新编译以及其无需进行类型检查即可尝试新事物的模块化能力,可能使工程师可以很快地调试出问题的地方(“哇。我们的新实习生编写了一个循环,该循环执行新的SQL子查询”(或“哦,Firefox已弃用了旧的缓存标头格式;他们制作了一个Python库来轻松设置新的缓存库”。)

从这种意义上说,即使在执行时间方面,模块化语言也可以被认为是更快的,因为一旦找到瓶颈,重组代码以使其以最佳方式工作将变得更加容易。如此多的工程师会告诉您,严重的性能问题并不是他们认为的那样(实际上,几乎不需要他们DID优化的事情;或者甚至没有按他们期望的那样工作!)


0

是的,没有。正如其他人指出的那样,程序首先是为了提高可读性,然后才是提高效率。但是,存在既可读又有效的标准实践。大多数代码很少运行,并且无论如何您都不会从中获得很多好处。

Java可以内联较小的函数调用,因此没有理由避免编写函数。优化器往往以更简单易读的代码更好地工作。有研究表明,从理论上讲,快捷方式应该运行得更快,实际上却需要更长的时间。JIT编译器可能会更好地工作,因为代码较小,并且可以识别和优化经常运行的代码。我还没有尝试过,但是我希望有一个相对来说很少被称为不编译的大型函数。

这可能不适用于Java,但是一项研究发现,由于要求使用不同的内存引用模型,因此较大的函数实际上运行较慢。这是特定于硬件和优化程序的。对于较小的模块,使用了在内存页面中有效的指令。它们比该功能不适合页面时所需的指令更快,更小。

在某些情况下,优化代码是值得的,但是通常您需要分析代码以确定代码在哪里。我发现它通常不是我期望的代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.