编程中的内存管理是否已成为无关紧要的问题?


38

背景信息
我重新访问了一个我很久没去过的古老(但很棒)的网站-Alioth Language Shootout(http://benchmarksgame.alioth.debian.org/)。

几年前,我开始使用C / C ++进行编程,但是由于参与的项目中的语言限制,从那时起,我几乎一直只在Java中工作。我不记得这些数字,而是想大致了解一下Java的状况。在资源使用方面与C / C ++背道而驰。

执行时间仍然比较好,与Java在表现最差的4倍比C / C ++慢,但平均约(或以下)2倍。由于Java本身实现的性质,这不足为奇,并且它的性能时间实际上比我预期的要

真正的难题是内存分配 -最糟糕的是,Java分配了:

  • 内存比C高52倍
  • 比C ++高25倍。

52倍的内存...绝对令人讨厌,对吗?...还是?内存现在相对便宜。

问题:
如果我们不是在对工作内存(即嵌入式系统等)有严格限制的目标平台上说话,那么今天选择通用语言时是否应该考虑内存使用情况?

我之所以提出这样的要求,部分原因是因为我正在考虑迁移到Scala作为我的主要语言。我非常喜欢它的功能方面,但是从内存上看,它比Java还要昂贵。但是,由于内存似乎正在以每年更快,更便宜和更丰富的方式(似乎越来越难找到没有至少4GB DDR3 RAM的家用笔记本电脑),因此可以不争辩说资源管理正在变得越来越多与(可能需要昂贵的实现方式)高级语言功能无关,后者可以更快地构建更具可读性的解决方案?


32
不要忘记,因为Java为小型基准测试分配的内存比C多52倍,但这并不意味着它将为大型应用程序使用52倍的内存。该内存的最大份额将是JVM所需的固定数量,并且您的应用程序越大,该部分将变得越不重要。
Carson63000

4
如果移动开发无关紧要,那就可以了。
JeffO

3
问题是Java基准测试与C / C ++相比有多糟糕,在两种语言之间进行选择意味着什么。我认为这是主题,与所有程序员都相关,清晰,专注,并且能够以当前形式合理地回答。我已投票决定重新开放。
GlenPeterson

大多数性能问题是在设计级别而不是工具级别引起并解决的。有些问题需要1ms的粒度,因此需要C / C ++。如果您有10ms的回旋余地,那么Scala或Java是一个不错的选择。大多数游戏输入控制器的工作时间为50-100ms。今天,许多人使用一种语言编写关键部分,而使用另一种语言编写程序的其余部分。
GlenPeterson

4
当在此测试中查看“比C ++多25倍”时,需要考虑不断增加的运行时间(大约13 Mb)。随着问题的加剧,运行时内存需求在整个程序中所占的比例也越来越小。如果C ++内存使用量小于1 MB,则从Java内存使用量中减去C ++内存使用量,您将获得一个相当恒定的值。

Answers:


34

内存管理是绝对相关的,因为它可以控制某件东西的显示速度,即使某件东西有很多内存也是如此。最好,最经典的例子是《使命召唤》或《生化奇兵》等AAA级游戏。这些是有效的实时应用程序,需要在优化和使用方面进行大量控制。问题的本质不是使用本身,而是管理。

它归结为两个词:垃圾回收。垃圾回收算法可能会导致性能出现轻微故障,甚至导致应用程序挂起一两秒钟。在会计应用程序中通常无害,但在《使命召唤》游戏中的用户体验方面可能会造成破坏。因此,在时间很重要的应用程序中,垃圾收集的语言可能会带来很大的问题。例如,这是Squirrel的设计目标之一,它试图通过使用引用计数来补救Lua的GC问题。

更让人头疼吗?可以,但是如果您需要精确控制,则可以接受。


14
-1“ ...在游戏中实际上是致命的...”-我的日常工作与生命安全一样,是一项至关重要的安全系统。游戏软件中最糟糕的情况是作家破产,因为它糟糕透顶,没人买。这是不容忽视的差异。
mattnz

4
@mattnz我的措词选择不当。已修复。我无意琐碎任何事情。
世界工程师

19
@Mattnz:如果您熟悉游戏,他显然意味着这可能对您的角色具有致命,这是完全正确的说法。
梅森·惠勒2013年

8
+1是因为答录器带有菱形,所以答案必须正确。
psr

8
实时垃圾收集器已经存在了很长时间。
约尔格W¯¯米塔格

30

真正的难题是内存分配-最糟糕的是,Java分配的内存比C高出52倍,比C ++多25倍。

了解问题所依据的数字吗?

  • 分配了多少内存?
  • 程序在做什么?

当这些Java和C程序之间存在很大差异时,大多数情况默认的JVM内存分配与libc所需的任何东西:

  • n体
    Java程序13,996KB :: C程序320KB ::免费Pascal 8KB

查看确实需要分配内存的任务(或使用其他缓冲区来累积多核程序的结果):

  • 曼德尔布罗
    Java程序67,880KB :: C程序30,444KB

  • K-核苷酸
    Java程序494,040KB :: C程序153,452KB

  • 反向互补
    的Java程序511,484KB :: C程序248,632KB

  • 正则表达式-DNA
    Java程序557,080KB :: C程序289,088KB

  • 二进制树
    Java程序506,592KB :: C程序99,448KB

...今天选择通用语言时应该关注内存使用情况吗?

这取决于用于解决需要解决的特定问题的特定方法的特定用法是否会受到将要使用的特定平台上可用内存的特定限制的约束。


3
您关于挖掘数字的观点是正确的,并且该站点肯定有很多关于其测试的免责声明。直接解决核心问题“应该关注内存使用情况”,将使您的答案得到加强。

1
解决了相对较差的问题的出色答案(模糊指定的基准甚至比过早的优化还差:)。支持分析的数据被很好地呈现,具体且令人深思。绝对值得“模范答案”的赏金
蚊蚋

17

与所有事物一样,这是一个权衡。

如果您要构建一个将在单个用户桌面上运行的应用程序,并且可以合理地预期它将控制该计算机上很大一部分RAM,那么为实现速度而牺牲内存使用可能是值得的。如果您以同一台计算机为目标,但是您正在构建一个小型实用程序,该实用程序将与同时运行的其他大量内存消耗型应用程序竞争,那么您可能需要对此谨慎一些。如果用户希望游戏运行时拥有所有记忆,那么用户可能会满意(尽管正如World Engineer指出的那样,我们会担心垃圾收集器是否决定定期暂停该操作以进行扫描)-如果他们在后台运行的音乐播放器在做其他事情的同时决定吞噬大量内存,则他们的热情可能会大大降低。干扰他们的工作能力。如果要构建基于Web的应用程序,则服务器上使用的任何内存都会限制您进行扩展的能力,从而迫使您在更多的应用程序服务器上花更多的钱来支持同一组用户。这可能会对公司的经济产生重大影响,因此您可能需要在进行权衡时非常谨慎。您在服务器上使用的任何内存都会限制您进行扩展的能力,从而迫使您在更多的应用程序服务器上花更多的钱来支持同一组用户。这可能会对公司的经济产生重大影响,因此您可能需要在进行权衡时非常谨慎。您在服务器上使用的任何内存都会限制您进行扩展的能力,从而迫使您在更多的应用程序服务器上花更多的钱来支持同一组用户。这可能会对公司的经济产生重大影响,因此您可能需要在进行权衡时非常谨慎。


8

这取决于许多因素,尤其是您的工作规模。

仅出于争论的目的,我们假设内存差异为30倍,CPU使用率为2倍。

如果您要处理的交互式程序使用C语言编写,将占用10兆字节的内存和1毫秒的CPU,那么这几乎是无关紧要的-在典型的台式机上,300兆字节的内存和2毫秒的执行时间通常是完全不相关的,而且即使在手机或平板电脑上也没有太大意义。

但是,大约需要1台服务器的一半资源与需要15台服务器之间的差异大得多-尤其是因为要扩展到15台服务器可能需要大量的额外工作来开发,而不是减少。就未来的扩展而言,您提到的相同因素往往表明,除非您的客户群大幅增长,否则,如果它现在可以在一台服务器上运行,那么当您增长该服务器的机会非常大能够用一台较新的服务器替换它而没有任何问题。

您真正需要考虑的另一个因素是,您将为特定任务看到多少开发成本差异。现在,您基本上是在看方程式的一侧。为了对成本与收益有一个很好的了解,您(显然已经)需要同时查看成本和收益,而不仅仅是隔离的收益。真正的问题基本上是:“ x是否大于y?” -但是您无法仅通过查看x来确定。您显然也需要注意y。


2
+1表示音阶。看一下这篇文章,真正了解大规模的资源管理。
Guy Coder 2013年

6

内存管理在当今世界中绝对重要。但是,并非您所期望的那样。即使使用垃圾收集的语言,也必须确保没有参考泄漏

如果这是您的代码,则您做错了:

static List<string> Cache;

...
Cache.Add(foo); //and then never remove anything from Cache

垃圾回收无法神奇地知道您将永远不会再使用某些引用,除非您这样做,否则就无法再次使用它,即,这样做Cache=null,可以有效地提醒垃圾回收器:“嘿,我将无法可以访问它了。随便做什么吧”

它比这更复杂,但是参考泄漏与传统的内存泄漏一样有害,甚至更多。

在某些地方,您无法放入垃圾收集器。例如,ATTiny84是具有512字节代码ROM和32字节RAM的微控制器。祝好运!那是一个极端,除了汇编语言,可能不会用其他任何方式编程,但是仍然如此。在其他情况下,您可能拥有1M的内存。当然,你可以安装一个垃圾收集器,但是如果处理器速度很慢(通过限制或保存电池),那么你就不会想使用一个垃圾收集器因为它太贵了跟踪程序员什么知道。

当您需要有保证的响应时间时,使用垃圾回收也会变得更加困难。就像,如果您有心脏监护仪之类的东西,并且当它1在某个端口上收到信号时,则需要确保可以在10ms内用适当的信号或某些东西对它做出响应。如果在响应例程的中间,垃圾收集器需要通过,并且最终需要100毫秒来响应,则可能有人死了。如果需要保证时序要求,那么即使不是不可能,也很难使用垃圾收集。

当然,即使在现代硬件上,在某些情况下,您也不需要担心垃圾收集器的开销,就需要将性能提高2%。


3

正如Donald Knuth所说,过早的优化是万恶之源。除非您有理由相信内存将成为瓶颈,否则请不要担心。鉴于摩尔定律仍在提供更大的内存容量(即使我们没有从中获得更快的单线程代码),所以我们有理由相信,将来我们对内存的限制将比我们更少今天。

也就是说,如果优化还不成熟,则一定要这样做。我现在正在一个项目上,我非常详细地了解我的内存使用情况,实际上我需要精确的控制,而垃圾清除会杀死我。因此,我正在用C ++进行此项目。但是对于我来说,这种选择似乎是每隔几年一次的活动。(希望再过几周,我不会再接触C ++了。)


4
这种态度使我们最终在无法持续分页的速度非常慢的计算机上使用庞大的企业软件。所有人都说:“当然,我的应用程序需要更多的内存,但是谁在乎,它实际上是免费的!” 然后您最终获得了一大堆需要大量内存的应用程序,这些应用程序使4GB RAM计算机的运行速度比10年前的512MB RAM计算机慢。
MrFox 2013年

@MrFox实际上,企业软件的问题在于,决定使用该软件的人不是遭受该软件困扰的人。请参阅lists.canonical.org/pipermail/kragen-tol/2005-April/000772.html,以详细了解其损坏原因。至于其余的内容,您是否错过了我的观点,即有时需要担心内存使用情况?
btilly 2013年

3

对于处理“大数据”的人来说,内存管理仍然是一个巨大的问题。天文学,物理学,生物信息学,机器学习等程序都必须处理数千兆字节的数据集,并且如果可以将相关部分保留在内存中,则程序运行速度会大大提高。即使在具有128GB RAM的计算机上运行也无法解决问题。

还有可能要利用GPU,尽管您可能会将其归类为嵌入式系统。使用CUDA或OpenCL的大多数刻苦思考归结为将数据从主内存传输到GPU内存的内存管理问题。


1

公平地说,许多Java都沉迷于某些真正毫无意义的类爆炸模式,这些模式只会破坏性能和猪的内存,但我确实想知道其中有多少内存只是JVM,理论上(嘿)让您运行在多个环境中使用同一应用程序,而不必完全重写新的应用程序。因此,设计权衡的问题归结为:“这样的开发优势对您来说,多少用户的内存值得您使用?”

这是IMO绝对值得考虑和合理考虑的折衷方案。但是让我感到恼火的是,因为现代PC如此强大,而内存却如此便宜,我们可以完全忽略这些顾虑,过时的功能和过时的代码,并在选择似乎有些东西的时候懒惰我现在在Windows PC上运行,所需时间与Window '95一样。说真的,Word?他们在18年内可能会增加80%的用户群实际需要的新功能?可以肯定,我们在窗口前进行了拼写检查吗?但是我们在谈论的是记忆,如果您拥有足够的记忆,那并不一定是速度,所以我离题。

但是,当然,如果您可以在2周内完成该应用程序,而付出的代价可能是额外的几兆字节,而不是2年才能获得仅需几K的版本,那么值得考虑一下与我猜)普通用户的计算机上有4-12个演出,然后嘲笑如此草率的想法。

但是,除了权衡问题之外,这与Scala有什么关系?仅仅因为它是垃圾收集,并不意味着您不应该总是试图根据范围和闭包中的内容以及是否应将其保留或以某种方式使用来考虑数据流。不再需要时由GC释放。这甚至是我们JavaScript UI Web开发人员都必须考虑的事情,希望随着我们扩展到其他问题领域(例如精通癌症)(大家都应该被Flash或Applets杀死,或者有机会的话杀死),我们将继续考虑这一问题。我们是。


0

编程中的内存管理是否已成为无关紧要的问题?

内存管理(或控制)实际上是我使用C和C ++的主要原因。

内存现在相对便宜。

没有快速记忆。我们仍在查看少量寄存器,例如i7上L1的32KB数据高速缓存,L2的256KB和L3 /内核的2MB。说:

如果我们不是在对工作内存(即嵌入式系统等)有严格限制的目标平台上说话,那么今天选择通用语言时是否应该考虑内存使用情况?

总体而言,内存使用情况可能并非如此。我有点不切实际,因为我不喜欢这样的记事本,即需要50 MB的DRAM和数百MB的硬盘空间,即使我有多余的备用空间。我已经待了很长时间了,看到这样一个简单的应用程序占用了相当大的内存来处理应该以千字节为单位的内容,这让我感到很奇怪,而且有点讨厌。就是说,如果我仍然遇到这样的事情,并且仍然很好并且反应迅速,我也许可以和我自己住在一起。

内存管理在我的领域对我很重要的原因通常不是减少太多内存使用。如果没有频繁访问该内存,则数百兆的内存使用不一定会以任何不平凡的方式减慢应用程序的运行速度(例如:仅在单击按钮或其他形式的用户输入时,除非您在谈论韩国星际争霸玩家,他们每秒可能会点击一百万次)。

在我的领域中,重要的原因是要使内存紧密和紧密地连接在一起,这些内存在那些关键路径中经常被访问(例如,遍历每个帧)。我们不想每次访问只需要在一个循环中访问的一百万个元素中的一个元素时就丢失高速缓存。当我们将内存从慢速内存大块下移到快速内存(例如64个字节的缓存行)的层次结构中时,如果这64个字节全部包含相关数据,并且我们可以将多个值的数据容纳到这64个字节中,这将非常有用。如果我们的访问模式足以在驱逐数据之前全部使用。

即使我们有千兆字节,上百万个元素的频繁访问数据也可能仅跨越20兆字节。如果内存太紧且紧密在一起以最大程度地减少高速缓存未命中,那么在每个绘制的帧上循环遍历该数据的帧速率仍然存在很大差异,因此内存管理/控制非常有用。具有数百万个顶点的球面上的简单视觉示例:

在此处输入图片说明

上面的内容实际上比我的可变版本慢,因为它正在测试网格的持久性数据结构表示形式,但除此之外,我过去即使在一半的数据上也都难以达到这样的帧速率(不可否认,自从我奋斗以来,硬件变得更快了),因为我没有将网格数据的高速缓存未命中和内存使用降至最低的念头。在这方面,网格是我处理过的最棘手的数据结构,因为它们存储了如此多的相互依赖的数据,这些数据必须保持同步,例如多边形,边缘,顶点,用户想要附加的纹理贴图,骨骼权重,彩色图,选择集,变形目标,边缘权重,多边形材质等等等。

在过去的几十年中,我已经设计并实现了许多网格系统,它们的速度通常与它们的内存使用成正比。尽管我正在使用这种方法,但是比起初的内存要多得多,但是我的新网格系统比我的第一个设计(将近20年前)要快10倍以上,并且很大程度上是因为它们使用了大约1/10的内存。记忆。最新版本甚至使用索引压缩来填充尽可能多的数据,尽管解压缩会产生处理开销,但压缩实际上提高了性能,因为同样,宝贵的快速内存也很少。现在,我可以将一百万个多边形网格与纹理坐标,边缘压痕,材质分配等配合使用,并为其提供空间索引(大约30兆字节)。

这是可变的原型,具有超过800万个四边形,并在带有GF 8400的i3上采用了多分辨率细分方案(这是几年前的产品)。它比我的不可变版本要快,但由于我发现不可变版本非常容易维护并且性能影响还不错,因此没有在生产中使用。请注意,线框并不表示刻面,而是贴片(导线实际上是曲线,否则整个网格将为纯黑色),尽管刻面中的所有点均已被笔刷修改。

在此处输入图片说明

因此,无论如何,我只想在上面显示其中的一些内容,以显示一些具体的示例和领域,其中内存管理非常有帮助,并且希望人们不会认为我只是在胡说八道。当人们说内存是如此丰富和便宜时,我往往会有些恼火,因为这是在谈论诸如DRAM和硬盘之类的慢速内存。当我们谈论快速内存时,它仍然是如此的小而珍贵,真正关键的(即,通常情况下,并非针对所有情况)路径的性能与播放少量的快速内存并尽可能有效地利用它有关。 。

对于这种事情,使用一种语言是非常有帮助的,例如,允许您设计高级对象(例如C ++),同时仍然能够将这些对象存储在一个或多个连续数组中,并确保所有这些对象将被连续表示,并且每个对象没有任何不必要的内存开销(例如:并非所有对象都需要反射或虚拟分派)。当您真正进入那些对性能至关重要的领域时,通过这样的内存控制来摆弄对象池并使用原始数据类型来避免对象开销,GC成本并保持经常访问的内存,实际上可以提高生产率。在一起连续。

因此,在我的案例中,内存管理/控制(或缺少内存管理/控制)实际上是一个主要的原因,因为它决定了哪种语言可以最有效地帮助我解决问题。我确实确实编写了我的代码,这些代码不是性能至关重要的,为此,我倾向于使用很容易从C嵌入的Lua。

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.