【问题标题】:C vs. C++ for performance in memory allocationC 与 C++ 在内存分配方面的性能
【发布时间】:2011-01-31 14:34:47
【问题描述】:

我计划参与开发用 C 语言编写的代码,用于复杂问题的蒙特卡罗分析。此代码在内存中分配大量数据数组以加快其性能,因此代码作者选择了 C 而不是 C++,声称可以使用 C 编写更快、更可靠(关于内存泄漏)的代码。

你同意吗?如果您在计算过程中需要在内存中存储 4-16 GB 的数据数组,您会选择什么?

【问题讨论】:

  • 我认为分配 16 GB 不会带来任何性能。空间也会影响时间。
  • 可靠性的很大一部分来自经验。即使 C++ 在资源管理方面比 C 更可靠(RAII 在这方面有很大帮助),但有经验的 C 程序员可能会发现在 C 中实现它比在 C++ 中更容易(并且犯的错误更少)。可靠性来自程序员,而不是语言(语言可以帮助也可以阻碍,但主要是程序员)

标签: c++ c performance memory-management


【解决方案1】:

在内存分配方面,C 和 C++ 之间没有真正的区别。如果您选择在对象上使用虚拟方法,C++ 会有更多“隐藏”数据,例如虚拟指针等。但是在 C 中分配字符数组与在 C++ 中一样昂贵,事实上,它们可能都使用 malloc 来完成。在性能方面,C++ 为数组中的每个对象调用一个构造函数。请注意,只有在有一个时才会这样做,默认构造函数什么都不做并且被优化掉了。

只要您预先分配数据池以避免内存碎片,您就可以开始了。如果你有简单的 POD 结构,没有虚方法,也没有构造函数,没有区别。

【讨论】:

  • “事实上,他们都在使用 malloc 来做到这一点” 只是一个书呆子,但这不一定是真的; new/delete 不必默认使用mallocfree
  • @GMan;当然不是,那里应该有一个“可能”。我认为 G++ 默认情况下会这样做。 C也不必使用它。 :)
  • 对于 g++/gcc,newmalloc 最终都调用 brk
  • @Dan Andreatta:仅在 Linux 系统上。
  • @Stephen Canon:当然,是的,实际上只有当他们最终需要来自操作系统的新内存池时。
【解决方案2】:

绝对是 C++。默认情况下,两者之间没有显着差异,但是 C++ 提供了一些 C 没有的东西:

  1. 构造函数/析构函数。这些可让您自动执行大部分内存管理,从而提高可靠性。
  2. 每类分配器。这些使您可以根据特定对象的设计和/或使用方式来优化分配。如果您需要大量小对象(举一个明显的例子),这会特别有用。

底线是,在这方面,C 绝对没有提供优于 C++ 的可能性。在最坏的情况下,你可以用同样的方式做同样的事情。

【讨论】:

  • @roe:从两者中获得最大性能需要小心——但 ctor 和 dtor 最终只是打包您在 C 中执行的相同操作的一种方式。唯一的区别是它们使管理变得足够容易当你甚至不会在 C 中考虑它时,你经常会想使用它们。
  • 呃,如果你使用构造函数和析构函数,C++肯定会更慢,因为它必须分配初始化。跨度>
  • @paxdiablo:当然,如果你必须初始化,无论是在构造函数中还是通过其他地方的代码块,都必须进行初始化。如果你需要初始化内存,你必须分配初始化。如果您不需要初始化,则不需要用户声明的构造函数,并且实现可以将其生成的一对一优化。
  • @paxdiablo:这是我根据引用的性能和可靠性标准选择的。 C++一定在性能上没有优势,但在最坏的情况下,它可以提供完全相同的性能,大约 90% 的情况下它至少会有一点优势。
  • ... 并且 C++ 中的可靠性要好得多。即使您只使用 C++ 的 C 子集,您也可以放入几个 RAII 对象来帮助处理复杂函数中的内存泄漏(多次返回、潜在的错误快捷方式......),并且代码将更加可靠。
【解决方案3】:

您也可以在 C++ 中使用 C 系列的内存分配函数:标准的 mallocfreerealloc 用于放大/缩小数组,alloca 用于在堆栈上分配内存。

如果您使用new,它将分配比所需更多的内存(主要是在调试期间)并进行额外的一致性检查。它还将调用类的构造函数。在发布 (-O3) 版本中,对于大多数应用程序而言,差异可以忽略不计。

现在 new 带来的 malloc 不是就地 new。您可以预先分配一个缓冲区,然后使用就地 new 将您的结构放入该缓冲区中,从而使“分配”它是即时的。

总而言之,我不会因为性能问题而远离 C。如果有的话,您的代码将更有效率,因为类在寄存器中传递 this 指针,而不是像 C 等效项中的参数。远离 C 的一个真正原因是 C++ 运行时的大小。如果您为嵌入式系统或引导加载程序开发程序,则无法嵌入 ~4mb 运行时。然而,对于普通应用程序,这不会产生影响。

【讨论】:

    【解决方案4】:

    对于分配原始数据,C 和 C++ 在大多数系统上应该没有区别,因为它们通常都使用相同的运行时库机制。我想知道这是否是典型的基准测试陷阱,他们还测量了 C++ 中构造函数调用的运行时间,并且方便地忘记了在 C 中包含任何类型的初始化代码的运行时间。

    此外,如果您在 C++ 中使用 RAII(您应该这样做),“更可靠(关于内存泄漏)”的论点也不成立。除非有人指的是让它更可靠地泄漏,否则使用 RAII、智能指针和容器类将减少而不是增加泄漏的可能性。

    我对分配这么多内存的主要担忧是双重的:

    • 如果在运行 Monte Carlo 模拟的机器上接近物理内存限制,这是降低性能的好方法,因为当虚拟内存系统需要启动时,磁盘很可能会开始抖动分页很多。尽管很多人认为虚拟内存不是“免费”的。
    • 需要仔细考虑数据布局,以最大限度地提高处理器缓存的使用率,否则您将部分失去最初将数据保存在主内存中的好处。

    【讨论】:

      【解决方案5】:

      如果内存分配是此类代码中的瓶颈,我建议宁愿重新设计,而不是更改语言以加快分配速度。如果您分配一次内存然后执行大量计算,我希望这些计算成为瓶颈。如果分配成本很高,那么这里就有问题。

      【讨论】:

        【解决方案6】:

        如果你在计算时需要在内存中存储 4-16GB 的数据数组,而你的机器只有 2GB 的物理内存,那怎么办?

        如果您的机器有 16GB 的物理内存怎么办?操作系统不占用物理内存吗?

        操作系统是否允许您使用 4GB、16Gb 等的地址空间?

        我建议,如果性能是主要的实现约束,那么了解打算使用的平台如何运行和执行比在相同环境下 C 和 C++ 之间任何可测量的性能差异的问题更重要和算法。

        【讨论】:

          【解决方案7】:

          C99 的一个特性是 C++ 所没有的,它可能会在繁重的数字运算代码中显着提高速度,那就是关键字 restrict。如果您可以使用支持它的 C++ 编译器,那么在进行优化时,您的套件中有一个额外的工具。不过,这只是一个潜在的好处:足够的内联可以允许与restrict 相同的优化等等。它也与内存分配无关。

          如果代码的作者可以证明分配 4-16GB 数组的 C 和 C++ 代码之间的性能差异,那么 (a) 我很惊讶,但是好吧,有差异,以及 (b) 有多少 times 程序会分配这么大的数组吗?您的程序实际上会花费大量时间分配内存,还是花费大部分时间访问内存并进行计算?与分配所花费的时间相比,用 4GB 数组实际任何事情都需要很长时间,这意味着您应该担心“任何事情”的性能,而不是分配的性能.短跑运动员非常关心他们能多快摆脱障碍。马拉松运动员,没那么多。

          您还必须小心如何进行基准测试。例如,您应该将malloc(size)new char[size] 进行比较。如果你测试malloc(size)new char[size](),那么这是一个不公平的比较,因为后者将内存设置为 0 而前者没有。与calloc 进行比较,但还要注意malloccalloc 在(不太可能的)事件中都可以从C++ 中获得,它们确实被证明更快。

          但最终,如果作者“拥有”或启动该项目,并且更喜欢用 C 而不是 C++ 编写,那么他不应该用可能是虚假的性能声明来证明这个决定,他应该说“我更喜欢 C,这就是我正在使用的”。通常,当有人对语言性能提出这样的要求,而测试结果证明不是真的时,您会发现性能并不是语言偏好的真正原因。证明声明是错误的实际上不会导致该项目的作者突然开始喜欢 C++。

          【讨论】:

            【解决方案8】:

            唯一不喜欢 C++ 的是它的额外复杂性 - 将其与不正确使用它的程序员结合起来,您很容易显着减慢速度。使用没有 C++ 特性的 C++ 编译器会给你同样的性能。正确使用 C++,你有一些可能更快。

            语言不是你的问题,分配和遍历大型数组才是。

            您在分配中可能犯的主要致命错误(在任何一种语言中)是分配 16G 的内存,将其初始化为零,然后才用实际值填充它。

            我期望从改进参考局部性的算法优化中获得最大的性能提升。

            根据底层操作系统,您可能还会影响缓存算法 - 例如表示只按顺序处理一段内存。

            【讨论】:

              猜你喜欢
              • 2018-11-18
              • 1970-01-01
              • 1970-01-01
              • 2017-06-10
              • 1970-01-01
              • 2019-06-10
              • 1970-01-01
              • 2010-11-23
              • 2014-10-25
              相关资源
              最近更新 更多