【问题标题】:Overwriting vs allocation/deallocation - efficiency覆盖与分配/释放 - 效率
【发布时间】:2009-06-02 13:39:45
【问题描述】:

我正在编写一个 C++ 应用程序,它需要一块内存(大约 1000 字节)作为一些文本处理的临时缓冲区。该操作每秒可重复多达 10,000 次。

任何人都可以确认每次我需要缓冲区时分配内存会更昂贵(即带有智能指针的新内存,超出范围时释放内存),而不是拥有一个固定缓冲区并清除它(写每次处理完成时,它的每个字节都有一个零)?

这听起来像是 C++ 的常识,但我只是在互联网上找不到任何可以证实它的东西。

具有自动垃圾收集功能的计算机语言(例如 Java、.net)的情况是否有所不同?

【问题讨论】:

  • 显然是静态缓冲区!!!如果您需要将所有内存归零,请使用 memset :)

标签: java .net c++ memory-management


【解决方案1】:

每次需要时分配和释放内存可能会更昂贵,但更大的问题是:这有关系吗?以最简单的方式编写它,您知道它如何正确工作并且不会泄漏/损坏内存。只有这样,如果您的性能不够好,请分析代码以查看可以改进的地方。

【讨论】:

  • 维持每秒 10,000 次分配听起来可能很重要 - 如果一秒钟内的 9,999 次迭代太少......如果存在实时限制,则可能需要在设计中构建可证明的限制。并非所有设计时“优化”都为时过早。
  • 我认为克里斯一针见血。为了保证吞吐量水平,您必须仔细考虑代码的算法复杂性。静态和本地分配具有可预测的性能。这不适用于动态分配。
【解决方案2】:

虽然我不能给你一个学术上的“确认”,但请考虑一下:

每当 CPU 执行一条指令时,都会耗费时间。如果您拆除缓冲区,然后重新分配它,CPU 将不得不执行解除分配/重新分配指令。如果您重用缓冲区,它将不必执行这些操作。

可以肯定地说,重用速度更快(我们甚至还没有讨论过内存局部性,相同的内存可以保留在 CPU 缓存中)。

话虽如此,除非您正在编写必须非常非常紧凑的东西(例如实时应用程序),或者除非您已将此缓冲区工作确定为应用程序中的瓶颈(在您进行性能测量时) ,我会说以最有意义且最容易维护的方式编写它。在现代机器上,您的维护成本在软件总成本中的比例可能比管理 1,000 字节的性能成本更大。

【讨论】:

    【解决方案3】:

    分配内存(通过 new 或 malloc)不会清除它。如果在使用之前必须将内存设置为 0,那么无论哪种方式都需要清除它。在这种情况下,使用静态缓冲区是一个很大的胜利。此外,如果您只使用缓冲区的一部分,您可以跟踪使用了多少,只需清除已使用的部分。

    calloc 确实将整个缓冲区设置为 0,但我无法想象它比 malloc+memset 快得多。

    【讨论】:

      【解决方案4】:

      关于Java:

      一个很大的区别是 Java 保证新分配的内存为 0,并且可能比手动执行此操作更快。此外,Java 的垃圾收集器在分配和释放短期对象时非常有效,而另一方面,不必要的长期对象可能会导致额外的工作。所以很有可能每次重新分配数组在 Java 中的性能都会比在 C++ 中更好。

      但是垃圾收集的性能很难预测,所以我只是双向进行,看看哪个更快。这可能比在这里提出问题并阅读所有答案所花费的时间更少。

      【讨论】:

      • 谢谢。我问了 Java 问题,因为我知道您无法控制已释放的内存。但是,您正确地指出,当您必须自己进行清除时,任何分配的内存都将用零填充,这与 C++ 不同。
      【解决方案5】:

      与往常一样,回答这个问题的最佳方法是两种方式都实施,然后对两种解决方案进行时间/基准测试,以进行实际比较,而不是将您的意见建立在推测或其他人的经验中有用的东西(在你没有意识到的方面可能与你的不同)。

      【讨论】:

        【解决方案6】:

        我将跳过标准的“不要担心优化”,因为其他人都已经涵盖了这一点。将缓冲区声明为函数的本地,并将其使用 memset 将是最快的。使用本地缓冲区,编译器只需将堆栈指针移动正确的字节数即可“分配”空间。分配不会比这更快。使用 Visual Studio,您可以添加 #pragma intrinsic( memset )。我知道 gcc 也支持内在函数,但我不记得如何告诉它使用它们。我认为最新版本将尽可能使用它们而无需被告知。内部 memset 扩展为一些内联指令,告诉处理器将内存范围设为 0。你不会比这更快。也就是说,如果不需要,请不要将内存归零。

        此外,只需使用本地声明的缓冲区,您的代码就会更加清晰。根据您所说,您的缓冲区不需要在将要使用它的例程范围之外持续存在。在现代术语中,1000 字节很小。使用动态而不是自动内存将添加一堆必须测试和维护的代码。不要这样做。自动记忆是显而易见的选择。

        【讨论】:

        • 请记住,堆栈空间不一定是无限的,您可能无法分配任意大的堆栈对象。
        • 是的。 Visual Studio 的默认堆栈大小仅为 1M。
        【解决方案7】:

        如果您对算法效率(大符号等)进行过任何类型的研究,您就会知道(或能够解决)大多数免费商店实现不能保证较低(甚至是较高) ) 算法迭代的边界,将被执行以在空闲存储中找到可用块以满足 new/malloc() 请求。

        重复使用一个固定缓冲区将提供更高数量级的性能:- 特别是垃圾收集环境,未使用的内存块可能会在空闲存储中逗留,直到运行垃圾收集周期。

        【讨论】:

        • 其实可以有保证的。不能保证 O(1),这是通过重用得到的。
        • 我真希望我能找到免费商店算法效率的在线分析。
        【解决方案8】:

        我不得不说,当涉及到大内存分配时,最好只做一次,而不是每次需要分配一些东西时都做。原因是内存可能会变得碎片化和缓慢(在内存中创建和删除大量内容会花费大量资源)。如果您有一个数据结构可以为您的操作保留足够的内存量,那就更好了。这样做的好处是会占用大部分内存。

        下面是 C++ 引擎盖下的 new 和 delete:

        #include <cstdlib>
        using std::malloc;
        using std::free;
        #include <new>
        using std::bad_alloc;
        
        void * operator new(size_t n)
        {
            void * p = malloc(n);
            if(!p) throw bad_alloc();
            return p;
        }
        
        void operator delete (void *p)
        {
            if (p) free(p);
        }
        

        一直做一个新的和删除的可能代价高昂!这就是 C# 和 Java 等语言比 C++ 慢的原因。垃圾收集器的唯一优点是它可以为您的程序将内存中的所有内容集中在一起(对内存进行碎片整理)。如果您的程序内存中有大量内容,这可能会很昂贵。

        另外,看看 STL 中的算法。它可能会通过优化某些操作来帮助您。

        【讨论】:

          猜你喜欢
          • 2019-03-06
          • 1970-01-01
          • 2021-10-03
          • 1970-01-01
          • 1970-01-01
          • 2012-01-11
          • 1970-01-01
          • 1970-01-01
          • 2014-02-19
          相关资源
          最近更新 更多