【问题标题】:Will Garbage Collected C be Faster Than C++?垃圾收集的 C 会比 C++ 更快吗?
【发布时间】:2023-04-04 17:38:01
【问题描述】:

我一直想知道如何在我的下一个项目中管理内存。这是用 C/C++ 编写 DSL。

这可以通过三种方式中的任何一种来完成。

  1. 引用计数的 C 或 C++。
  2. 垃圾收集 C.
  3. 在 C++ 中,将类和结构从堆栈复制到堆栈并使用某种 GC 单独管理字符串。

社区可能已经在这些方法上积累了丰富的经验。哪个会更快?各有什么优缺点?

一个相关的附带问题。 malloc/free 会比在程序开始时分配一大块并在其上运行我自己的内存管理器要慢吗? .NET 似乎可以做到这一点。但我很困惑为什么我们不能指望操作系统比我们自己做的更好更快。

【问题讨论】:

    标签: c++ c optimization memory-management garbage-collection


    【解决方案1】:

    这一切都取决于!这是一个非常开放的问题。它需要一篇文章来回答它!

    嘿..这是有人早先准备好的:

    http://lambda-the-ultimate.org/node/2552

    http://www.hpl.hp.com/personal/Hans_Boehm/gc/issues.html

    这取决于您的对象有多大、有多少、分配和丢弃它们的速度、您希望投入多少时间进行优化和调整以进行优化。如果您知道您需要多少内存的限制,为了获得快速性能,我认为您无法真正从操作系统中获取所需的所有内存,然后自行管理。

    从操作系统分配内存缓慢的原因是它处理磁盘和内存中的大量进程和内存,因此要获得内存,必须确定是否有足够的内存。可能,它可能必须将另一个进程的内存从 ram 分页到磁盘,以便为您提供足够的内存。有很多事情发生。因此,自己管理(或使用 GC 收集的堆)可能比为每个请求转到操作系统要快得多。此外,操作系统通常会处理更大的内存块,因此它可能会向上取整您发出的请求的大小,这意味着您可能会浪费内存。

    你对超快的速度有真正的硬性要求吗?许多 DSL 应用程序不需要原始性能。我建议使用最简单的代码。你可能会花费一生的时间来编写内存管理系统并担心哪个是最好的。

    【讨论】:

    • 您是说这一切都将取决于一个人使用的语言、C 还是C++?由于在这两种语言中的任何一种中,都必须自己实现 GC,所以我认为没有每种语言的变化,只是每个算法的变化。
    • 不完全确定你在问什么。但是,相同的内存管理算法可以应用于两种语言。但是,在 C++ 中,您会执行诸如覆盖运算符 new 和 delete 之类的操作来实现它。在 C 中我不确定;可能命名您自己的函数,并确保将它们用于所有分配。
    【解决方案2】:

    为什么垃圾回收 C 比 C++ 快?唯一可用于 C 的垃圾收集器是非常低效的东西,更多的是为了堵塞内存泄漏而不是实际提高代码质量。

    无论如何,C++ 有潜力以更少的代码达到更好的性能(请注意,这只是一种潜力。也很有可能编写比等效 C 慢得多的 C++ 代码)。

    考虑到两种语言的当前状态,GC 目前不会提高代码的性能。 GC 可以在为其设计的语言中变得非常高效。 C/C++ 不在其中。 ;)

    除此之外,很难说。语言没有速度。问哪种语言更快是没有意义的。这取决于 1) 特定代码,2) 编译它的编译器,以及 3) 它运行的系统(硬件和操作系统)。

    malloc 是一个相当慢的操作,比 .NET 等价物慢得多,所以是的,如果你正在执行大量的小分配,你最好分配一次大的内存池,然后使用它的块.

    原因是操作系统必须找到一块空闲的内存,基本上是通过跟踪所有空闲内存区域的链表。在 .NET 中,new() 调用基本上只是将堆指针移动到分配所需的字节数。

    【讨论】:

      【解决方案3】:

      uh ...这取决于如何为您的 DSL 编写垃圾收集系统。 C 或 C++ 都没有内置垃圾收集工具,但都可以用来编写非常高效或非常低效的垃圾收集器。顺便说一句,写这样的东西是一项不平凡的任务。

      DSL 通常是用 Ruby 或 Python 等高级语言编写的,因为语言编写者可以利用该语言的垃圾收集和其他功能。 C 和 C++ 非常适合编写完整的工业级语言,但您当然需要知道自己在做什么才能使用它们 - 了解 yacc 和 lex 在这里特别有用,但对 dynamic memory management 的良好理解也很重要,因为你说。如果您仍然喜欢 C/C++ 中的 DSL 的想法,您还可以查看keykit,这是一个用 C 编写的开源音乐 DSL。

      【讨论】:

        【解决方案4】:

        对于大多数垃圾收集实现,分配可以看到速度提高,但是您需要在程序执行的任何时候触发收集阶段的额外成本,从而导致突然(看似随机)延迟。

        至于您的第二个问题,这取决于您的内存管理算法。坚持使用库的默认 malloc 实现是安全的,但也有其他性能更好的替代方案。

        【讨论】:

          【解决方案5】:

          一个相关的附带问题。 malloc/free 会比在程序开始时分配一个大卡盘并在其上运行我自己的内存管理器要慢吗? .NET 似乎可以做到这一点。但我很困惑为什么我们不能指望操作系统比我们自己做的更好更快。

          让操作系统处理内存分配的问题在于它引入了不确定的行为。程序员无法知道操作系统返回一块新内存需要多长时间 - 如果内存必须被分页到磁盘,分配可能会非常昂贵。

          因此预分配可能是个好主意,尤其是在使用复制垃圾收集器时。它会增加内存消耗,但分配会很快,因为在大多数情况下它只是一个指针增量。

          【讨论】:

          • 操作系统(或者实际上是库,因为 malloc 分配大块,然后将它们一块一块地分配给您的程序)是一个通用分配器。如果你能以某种方式限制你的分配器,你可以让它更快。
          • 已经分配的读/写内存如果已经被分页到磁盘也可能非常昂贵。 :)
          【解决方案6】:

          正如人们所指出的 - GC 分配速度更快(因为它只是为您提供列表中的下一个块),但总体速度较慢(因为它必须定期压缩堆,以便分配速度更快)。

          所以 - 寻求妥协的解决方案(这实际上非常好):

          您创建自己的堆,为您通常分配的每种大小的对象(或 4 字节、8 字节、16 字节、32 字节等)创建一个堆,然后,当您想要一块新的内存时,您可以获取适当堆上的最后一个“块”。因为您从这些堆中预分配,所以在分配时您需要做的就是抓取下一个空闲块。这比标准分配器效果更好,因为您很乐意浪费内存 - 如果您想分配 12 个字节,您将从 16 字节堆中放弃整个 16 字节块。您保留已用 v 空闲块的位图,这样您就可以快速分配而不会浪费大量内存或需要压缩。

          此外,由于您正在运行多个堆,因此高度并行的系统工作得更好,因为您不需要经常锁定(即每个堆都有多个锁,因此几乎不会发生争用)

          试试看 - 我们在一个非常密集的应用程序上用它来替换标准堆,性能提升了很多。

          顺便说一句。标准分配器速度慢的原因是它们尽量不浪费内存——因此,如果您从标准堆中分配 5 字节、7 字节和 32 字节,它将保留这些“边界”。下次您需要分配时,它会遍历那些正在寻找足够空间来满足您要求的人。这适用于低内存系统,但您只需要查看当今大多数应用程序使用了多少内存,就可以看到 GC 系统采用了相反的方式,并尝试尽可能快地进行分配,同时不关心有多少内存。浪费了。

          【讨论】:

            【解决方案7】:

            这个问题有很多变数,但是如果你的应用程序在编写时考虑到了垃圾回收,并且如果你利用了Boehm collector的特殊功能,例如对不包​​含指针的块进行不同的分配调用,然后作为一般规则您的应用程序 - 将有更简单的界面 - 会跑得更快一些 - 需要 1.2 倍到 2 倍的空间 与使用显式内存管理的类似应用程序相比。

            有关支持这些说法的文档和证据,您可以查看 Boehm 网站上的信息,以及 Ben Zorn 的几篇关于保守垃圾收集成本测量的论文。

            最重要的是,您将节省大量精力,并且不必担心一大类内存管理错误。

            C 与 C++ 的问题是正交的,但 GC 肯定会比引用计数快,尤其是在没有编译器支持引用计数的情况下。

            【讨论】:

              【解决方案8】:

              C 和 C++ 都不会免费为您提供垃圾。他们会给你的是内存分配库(提供 malloc/free 等)。有许多用于编写垃圾收集库的算法的在线资源。一个好的开始是link text

              【讨论】:

                【解决方案9】:

                大多数非 GC 语言将根据需要和不再需要分配和取消分配内存。 GC 语言通常会预先分配大块内存,并且仅在空闲时释放内存,而不是在密集任务中,所以如果 GC 在正确的时间启动,我会是的。

                D 编程语言是一种垃圾收集语言,ABI 与 C 兼容,部分 ABI 与 C++ 兼容。 This Page 展示了 C++ 和 D 中字符串性能之间的一些基准。

                【讨论】:

                  【解决方案10】:

                  我建议,如果您编写了一个内存分配和释放(显式或 GC'ed)是瓶颈的程序,那么您应该重新考虑您的架构、设计和实现。

                  【讨论】:

                    【解决方案11】:

                    如果您不想显式管理内存,请不要使用 C/C++。有很多语言具有引用计数或编译器支持的垃圾收集器,它们可能会更好地为您工作。

                    C/C++ 是在程序员管理自己的内存的环境中设计的。尝试改进 GC 或 ref 计数可能会有所帮助,但您会发现您要么必须牺牲 GC 的性能(因为它没有任何编译器提示指针可能在哪里),要么您'会找到新的有趣的方法来搞砸引用计数或 GC 或其他任何东西。

                    我知道这听起来不错,但实际上,您应该选择一种更适合该任务的语言。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-04-01
                      • 1970-01-01
                      • 2012-03-24
                      • 2021-11-19
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-05-04
                      相关资源
                      最近更新 更多