性能与C ++内存模型

时间:2014-01-21 15:00:36

标签: c++ performance c++11 concurrency memory-model

使用C ++ 11的新共享内存并发功能,两个线程可以同时分配内存。此外,由于编译器事先不知道编译的代码是否将由多个线程同时运行,因此必须假设最坏的情况。因此,我的想法是编译的代码必须以某种方式同步到堆的行程。这将减慢不需要同步的单线程代码。

这与C ++的格言形成鲜明对比,即#34;你只需为你使用的东西付费"?开销是否很小,以至于不被认为是重要的? C ++内存模型的其他区域是否会减慢代码,最终只能单线程使用?

5 个答案:

答案 0 :(得分:3)

堆管理器确实需要同步,这是多线程代码可能存在的性能问题。如有必要,可以通过该计划来减轻这种影响。标准库也在做出反应,试图获得更好的多线程分配器。

编辑:关于第二段中的问题的一些想法。

即使是C ++也需要足够安全才能使用。 “YDPFWYU”很不错,但是如果你想在多线程环境中使用代码就意味着你必须在每个分配中包装一个互斥锁,那么你就会遇到很大的问题。它就像异常一样:即使是不主动使用它们的代码也应该有点意识到它可能会在它们存在的上下文中使用,程序员和编译器都需要知道这一点。编译器需要创建异常支持代码/数据结构,而程序员需要编写异常安全的代码。多线程是相同的,只是更糟糕:您编写的任何代码片段都可能在多线程环境中使用,因此您需要编写线程安全的代码,并且编译器/环境需要了解线程(放弃一些非常不安全的优化,并有一个线程安全的分配器。)

这些是C ++中的一些要点,就标准而言,你甚至为不使用的东西付费。您的特定编译器可能会为您提供一个转义窗口(禁用异常,使用单线程运行时库),但那时不再是真正的C ++。

即使(或特别是)如果你有一个全局分配器锁,单线程程序的开销是最小的:锁只在争用时很昂贵。与其余的分配器操作相比,无争议的互斥锁定/解锁不是很重要。

在争论中,故事是不同的,也就是自定义分配器可能进入的地方。

正如我上面简要提到的,C ++中的另一个地方因多线程的存在而略微放缓:禁止某些特定的优化。编译器不能在通常不具有这些访问的代码路径中发明对可能的共享变量(如全局变量或您发出指针的东西)的读取和写入(尤其是写入)。这可能会减慢非常具体的代码片段,但总体而言,在程序中,您不太可能注意到这一点。

答案 1 :(得分:3)

您正在混合堆内存的分配和访问。

多线程堆分配确实是同步的,但在C库级别,至少在所有现代(con)当前操作系统的C库中都是如此。可能存在不执行此操作的特定用途C库。请参阅示例the old single- and multithreaded C runtime library for MSVC(注意新版本的MSVS如何弃用甚至删除单线程变体)。我假设glibc有一个类似的机制,可能也只是多线程,所以总是同步。我没有听到有人抱怨多线程内存分配速度,所以如果你有一个具体的抱怨,我希望通过可重现的代码正确解释和记录。

堆内存的访问(即在调用newmalloc之后已经返回)不受任何机制的保护。 C ++ 11为您提供mutex以及其他同步可能性,如果您想要保护您免受竞争条件的影响,您需要在代码中实现这些同步。如果不这样做,就不会失去弹性。

答案 2 :(得分:0)

编译器真的没有被迫不优化。总是有可能制作一个非常糟糕的编译器和“标准”库。而现在它只是质量差而已。尽管它可能被宣传为“唯一真正正确的C ++”。

“您编写的任何代码都可能在多线程环境中使用,因此您需要编写线程安全的代码,并且编译器/环境需要了解线程” - 这是一个明显的愚蠢。< / p>

良好的实现始终可以提供优化单线程代码(和必要的库...)的常规方法,以及不使用异常的代码,并允许其他功能......

(例如,线程需要一些特定的函数来协调线程以及创建线程,并且在链接时它们的使用是可见的并且可能影响工具链......或者在第一次调用线程创建函数时它可能会影响内存分配方法(并有其他影响)并且可能有其他好的方法,比如特殊的开关到编译器等......)

答案 3 :(得分:0)

不是真的。物理内存和后备存储都是现代操作系统上的系统资源。因此,必须正确地同步它们的分配和对它们的访问。

线程共享虚拟内存的情况只是调度实体可以共享虚拟内存的许多其他方式的特例。考虑内存映射相同库或数据文件的两个进程。

线程的唯一额外开销是对虚拟内存映射的修改,因为线程共享虚拟内存映射。大部分同步开销是不可避免的。例如,如果您要取消映射某些内容,则通常必须将某些资源返回到系统级池,并且无论如何都需要同步。

在许多平台上,需要一些特殊的平台特定的东西让其他线程同时运行,知道他们的虚拟内存视图已经改变。但是如果没有其他线程,这种情况就会消失,因为没有什么可以通知的。

一些功能即使不使用也会产生成本。即使您从未交换过,内核中存在交换逻辑和检查也会产生一些成本。工程师是现实主义者,必须平衡成本和收益。

答案 4 :(得分:0)

  

物理内存和后备存储都是现代操作系统上的系统资源。因此,必须正确地同步它们的分配和对它们的访问。

     

线程共享虚拟内存的情况只是调度实体可以共享虚拟内存的许多其他方式的特例。

一旦它是操作系统的功能,就不需要在应用程序中的C / C ++分配函数中使用额外的代码(当然,多线程需要在标准库中进行特殊的附加同步。并且需要额外的“系统”打电话给“并在开头看问题。”

真正的麻烦可能是系统中同一个库(标准C / C ++库以及其他)的许多类型(单线程和多线程)......但是......