1)我尝试在程序中使用线程时如何分配内存,但无法找到答案。这里What and where are the stack and heap?是调用单个程序时堆栈和堆的工作方式。但是当涉及到线程编程时会发生什么?
2)使用OpenMP并行区域创建线程,并行代码将在每个线程中并发执行。这是否在内存中分配的空间比顺序执行的相同代码占用的内存多?
答案 0 :(得分:6)
通常,是的,[用户空间]堆栈是每个线程一个,而堆通常由所有线程共享。请参阅示例this Linux question。但是,在Windows in particular的某些操作系统(OS)上,即使是单个线程应用程序也可能使用多个堆。使用OpenMP进行线程化不会改变这些基础知识,这些基础知识主要依赖于操作系统。因此,除非你将问题缩小到特定的操作系统,否则在这种普遍性水平上不能说更多。
由于我自己懒得画这个,这是Nichols等人的 PThreads Programming 的比较例子。 (1996)
在免费的LLNL POSIX Threads Programming tutorial by B. Barney.
中可以找到更详细(并且可能有点混乱)的图表是的,正如您所怀疑的那样,运行更多线程会消耗更多堆栈内存。实际上你可以exhaust the virtual address space of a process just with thread stacks if you make enough of them。 OpenMP的各种实现都有STACKSIZE environment variable(或thereabout)来控制OpenMP为线程分配的堆栈数。
关于Z boson关于线程本地存储(TLS)的问题/建议:粗略地(即从概念上讲),Thread Local Storage是每线程堆。与用于操作它的API中的每个进程堆有所不同,至少因为每个线程都需要自己的单独指针指向自己的TLS,但基本上你有一个类似于堆的进程地址空间块到每个线程。 TLS是可选的,您不必使用它。 OpenMP provides its own abstraction/directive for TLS-like persistent per-thread data,名为THREADPRIVATE
。 OpenMP THREADPRIVATE
没有必要使用操作系统的TLS支持,但至少在该环境中有Linux-focused paper which says that such an implementation gave the best performance。
这是一个微妙的(或者为什么我说“粗略说”当我将TLS与每个线程堆进行比较时):假设你想要一个每线程堆,比如说,以减少对主堆的锁争用。实际上,您不必在每个线程的TLS中存储整个每线程堆。 It suffices在每个线程的TLS中存储一个不同的头指针,指向在共享的每个进程空间中分配的堆。识别并自动使用程序中的每个线程堆(为了减少主堆上的锁争用)是farily difficult CS problem。自动执行此操作的堆分配器称为可伸缩/并行[izing]堆分配器或其附近。例如,Intel TBB provides one such allocator和can be used in your program even if you use nothing else from TBB。虽然有些人似乎认为英特尔的TBB分配器包含黑魔法,但事实上并没有真正区别于上述使用TLS指向某些线程局部堆的基本思想,而后者又由几个由块隔离的双向链表组成。 / object-size,如Intel paper on TBB中的以下图表所示:
对于AIX 7.1,IBM有一些相似之处,但有点复杂。您可以告诉其(默认)分配器为多线程应用程序使用固定数量的堆,例如MALLOCOPTIONS=multiheap:3
。 AIX 7.1还有另一个选项(可以组合多个数字)MALLOCOPTIONS=threadcache
,它看起来有点类似于英特尔TBB所做的,因为它保留了解除分配区域的每线程缓存,未来的分配请求可以是使用较少的全局堆争用进行服务。除了默认分配器的那些选项之外,AIX 7.1还有一个(非默认)"Watson2" allocator“使用特定于线程的机制,该机制使用不同数量的堆结构,这取决于程序的行为。因此不需要配置选项。“ (但你确实需要用MALLOCTYPE=Watson2
明确选择这个分配器。)Watson2的操作听起来更接近英特尔TBB分配器的作用。
上面详述的上述两个例子(英特尔TBB和AIX)仅仅是具体的例子,但不应该被理解为持有一些专属酱。每线程或每CPU堆缓存/ arena / magazine的想法相当普遍。 BSDcan jemalloc paper引用1998 MS Research paper作为第一个为此目的系统评估竞技场的人。上述MS论文确实将ptmalloc网页引用为“1998年5月11日访问过”,并总结了ptmalloc的工作原理如下:“它使用了一个子链表,其中每个子堆都有一个锁,128个空闲列表和一些内存来管理当一个线程需要分配一个块时,它会扫描子堆列表并抓取第一个未锁定的块,分配所需的块,然后返回。如果找不到解锁的子堆,它会创建一个新的子堆并将其添加到通过这种方式,线程永远不会等待锁定的子空间。“