多线程可以加快内存分配吗?

时间:2011-02-01 05:30:03

标签: c++ multithreading memory-management new-operator boost-thread

我正在使用8核处理器,并使用Boost线程来运行大型程序。 从逻辑上讲,程序可以分成组,每个组由一个线程运行。 在每个组中,一些类总共调用“new”运算符10000次。 Rational Quantify表明,“新”内存分配占用了程序运行时的最大处理时间,并且正在减慢整个程序的速度。

我可以加快系统速度的一种方法是在每个'组'中使用线程,这样10000个内存分配就可以并行发生。

我不清楚如何在这里管理内存分配。 OS调度程序是否真的能够并行分配内存?

10 个答案:

答案 0 :(得分:12)

标准CRT

虽然较旧的Visual Studio默认的CRT分配器是阻塞的,但至少对于Visual Studio 2010及更新版本来说这已不再适用,它直接调用相应的OS函数。 Windows堆管理器阻塞,直到Widows XP,在XP中为可选Low Fragmentation Heap is not blocking,而默认值为,而较新的操作系统(Vista / Win7)默认使用LFH。最近(Windows 7)分配器的性能非常好,与下面列出的可扩展替换相当(如果针对较旧的平台或者当您需要其他功能时,您仍然可能更喜欢它们)。存在多个具有不同许可和不同缺点的多个“可扩展分配器”。我认为在Linux上,默认运行时库已经使用了可扩展的分配器(PTMalloc的某种变体)。

可扩展的替换

我知道:

您可能需要查看Scalable memory allocator experiences,了解我在Windows项目中尝试使用其中一些的经历。

实际上,大多数工作都是通过为每个线程缓存和每个线程预先分配的区域进行分配,这意味着小分配通常只发生在线程的上下文中,OS服务很少被调用。

答案 1 :(得分:6)

内存的动态分配使用应用程序/模块/进程的堆(但不是线程)。堆一次只能处理一个分配请求。如果您尝试在“并行”线程中分配内存,它们将由堆按适当顺序处理。你不会得到这样的行为:一个线程正在等待获取其内存,而另一个线程可以要求一些,而第三个线程正在获得一些。线程必须在队列中排队才能获得他们的内存块。

你需要的是一堆堆。使用目前不忙的堆来分配内存。但是,你必须要注意这个变量的整个生命周期,以免它在另一个堆上被解除分配(这会导致崩溃)。

我知道Win32 API具有诸如GetProcessHeap(),CreateHeap(),HeapAlloc()和HeapFree()之类的函数,它们允许您创建新堆并从特定堆HANDLE分配/释放内存。我不知道其他操作系统的等价性(我已经找过它们,但无济于事)。

当然,您应该尽量避免频繁进行动态分配。但是,如果你不能,你可能会考虑(为了便携性)创建自己的“堆”类(不必是一个堆本身,只是一个非常有效的分配器),可以管理大块内存,当然一个智能指针类,它将保存对它所来自的堆的引用。这将使您能够使用多个堆(确保它们是线程安全的)。

答案 2 :(得分:5)

我知道有两个可扩展的malloc替代品:

我对Hoard(在研究中表现不佳)没有任何经验,但是Emery Berger潜伏在这个网站上并对结果感到惊讶。他说他会看一看,我猜测可能有一些特定的测试或实施“困住”Hoard,因为一般反馈通常是好的。

使用jemalloc时需要注意的一点是,当您快速创建然后丢弃线程时,它会浪费一些空间(因为它会为您分配的每个线程创建一个新池)。如果你的线程稳定,那么这应该没有任何问题。

答案 3 :(得分:4)

我相信你问题的简短回答是:是的,可能。正如已经有几个人在这里指出的那样,有很多方法可以做到这一点。

除了你的问题以及已在此处发布的答案之外,从改进开始你的期望是好的,因为这几乎可以说明采取哪条路径。也许你需要快100倍。另外,你是否认为自己在不久的将来也会进行速度提升,或者是否有一个足够好的水平?不了解您的应用程序或问题域,很难专门为您提供建议。例如,你是否在一个需要不断提高速度的问题领域?

在进行性能改进时开始的一件好事是询问您是否需要按照当前的方式执行操作?在这种情况下,您能否预先分配对象?系统中是否有最大数量的X对象?你可以重复使用对象吗?所有这些都更好,因为您不一定需要在关键路径上进行分配。例如。如果你可以重用对象,那么带有预先分配对象的自定义分配器就可以正常工作。另外,你在用什么操作系统?

如果您没有具体的期望或某种程度的表现,只需开始尝试这里的任何建议,您就会发现更多。

祝你好运!

答案 4 :(得分:3)

将您自己的非多线程新内存分配器转换为每个线程都有的独特副本。

(你可以覆盖新的和删除)

所以它在大块中分配它可以通过并且不需要任何锁定,因为每个都由单个线程拥有。

将您的线程限制为您可用的核心数。

答案 5 :(得分:1)

new几乎是阻塞,它必须找到下一个可用的内存,如果你有很多线程都要求一次这么做就很难。

内存分配很慢 - 如果你这样做了几次,特别是在很多线程上,你需要重新设计。你可以在开始时预先分配足够的空间,你可以只分配一个带有'new'的大块然后自己分区吗?

答案 6 :(得分:1)

您需要检查编译器文档是否使分配器线程安全。如果没有,那么您将需要重载新运算符并使其线程安全。 否则它将导致段错误或UB。

答案 7 :(得分:1)

在Windows等某些平台上,操作系统会对全局堆进行序列化。拥有一个线程独立的堆可以大大改善分配时间。

当然,在这种情况下,可能值得质疑你是否真的需要堆分配而不是其他形式的动态分配。

答案 8 :(得分:1)

您可能需要查看The Hoard Memory Allocator:“是malloc()的替代品,可以显着提高应用程序性能,尤其是对于在多处理器上运行的多线程程序。 “

答案 9 :(得分:0)

  1. 最好你可以尝试达到〜并行8个内存分配(因为你有8个物理内核),而不是你写的10000个

  2. 标准malloc使用互斥锁,标准STL分配器也是如此。因此,引入线程时它不会自动加速。 不过,您可以使用另一个不使用全局锁定的malloc库(例如“ptmalloc”)。如果你使用STL分配(例如分配字符串,向量),你必须编写自己的分配器。

  3. 相当有趣的文章:http://developers.sun.com/solaris/articles/multiproc/multiproc.html