在PThreads的上下文中的多处理器与多线程

kfm*_*e04 4 c hardware multithreading pthreads multiprocessing

我有关于硬件选择及其对软件开发的影响的应用程序级别(PThreads)问题.

我在多核单CPU盒上测试了多线程代码.

我正在尝试为下一台机器购买什么:

  • 一个6核单CPU盒
  • 一个4核双CPU盒

我的问题是,如果我选择双CPU盒,那会不会严重影响我的代码移植?或者我可以分配更多线程并让操作系统处理其余的线程吗?

换句话说,在PThreads应用程序的上下文中,多处理器编程是否与(单CPU)多线程不同?

我认为在这个级别上没什么区别,但是在配置新盒子时,我注意到必须为每个CPU购买单独的内存.那是我遇到一些认知失调的时候.

关于代码的更多细节(对于那些感兴趣的人): 我从磁盘读取大量数据到大块内存(很快就会有~24GB),然后我产生了我的线程.最初的内存块是"只读"(由我自己的代码策略强制执行),因此我不对该块进行任何锁定.当我看着4核双CPU盒时,我感到困惑 - 它们似乎需要单独的内存.在我的代码的上下文中,如果我分配了一堆额外的线程,我不知道会在"幕后"发生什么.OS会将我的一块内存从一个CPU的内存库复制到另一个内存库吗? 这会影响我将要购买多少内存(提高此配置的成本). 理想的情况(成本方面和易于编程)是让双CPU共享一大堆内存,但如果我理解正确,这可能不适用于新的英特尔双核MOBO(如HP ProLiant ML350e)?

Jav*_*ier 8

现代CPU 1在本地处理RAM并使用单独的通道2在它们之间进行通信.这是十多年前为超级计算机创建的NUMA架构的消费级版本.

我们的想法是避免可能导致严重争用的共享总线(旧FSB),因为每个核心都使用它来访问内存.随着您添加更多NUMA单元,您可以获得更高的带宽.缺点是从CPU的角度来看内存变得不均匀:某些RAM比其他RAM更快.

当然,现代操作系统调度程序具有NUMA感知能力,因此他们尝试减少任务从一个单元格迁移到另一个单元格.有时可以在同一个插槽中从一个核心移动到另一个核心; 有时会有一个完整的层次结构,指定哪些资源(一,二,三级缓存,RAM通道,IO等)是共享的,哪些不是,并且通过移动任务来确定是否会有惩罚.有时它可以确定等待正确的核心是没有意义的,最好将整个东西铲到另一个插座上....

在绝大多数情况下,最好让调度程序执行它最熟悉的操作.如果没有,你可以玩numactl.

至于给定程序的具体情况; 最好的体系结构在很大程度上取决于线程之间的资源共享级别.如果每个线程都有自己的游乐场并且大部分都在其中独立工作,那么足够聪明的分配器将优先考虑本地RAM,使得每个线程恰好在哪个单元上变得不那么重要.

另一方面,如果对象由一个线程分配,由另一个线程分配并由第三个线程消耗; 如果他们不在同一个小区,性能会受到影响.您可以尝试创建小线程组并限制组内的大量共享,然后每个组可以在没有问题的情况下进入不同的单元.

最糟糕的情况是所有线程都参与了数据共享的狂欢.即使您已经对所有锁和流程进行了很好的调试,也没有任何方法可以优化它以使用比单元上可用的内核更多的内核.甚至可能最好将整个过程限制为仅在单个单元中使用核心,从而有效地浪费其余部分.

1现代,我的意思是任何AMD-64bit芯片,Nehalem或更好的英特尔.

2 AMD将此通道称为HyperTransport,而英特尔名称为QuickPath Interconnect

编辑:

你提到你初始化"一大块只读内存".然后产生很多线程来处理它.如果每个线程都在它自己的那个块上运行,那么如果你在生成它之后在线程上初始化它会好得多.这将允许线程扩展到多个核心,分配器将为每个核心选择本地RAM,这是一种更有效的布局.也许有一些方法可以提示调度程序在生成线程后立即迁移它们,但我不知道细节.

编辑2:

如果您的数据是从磁盘逐字读取的,而不进行任何处理,那么使用mmap而不是分配大块和块可能是有利的read().有一些共同的优点:

  1. 无需预先分配RAM.
  2. mmap操作几乎是瞬间的,你可以开始使用它.根据需要将懒惰地读取数据.
  3. 在应用程序,mmap内存,缓冲区和缓存之间进行选择时,操作系统可能比您更聪明.
  4. 它的代码更少!
  5. 不会读取不需要的数据,不会耗尽RAM.
  6. 您可以专门标记为只读.尝试写入的任何错误都会导致coredump.
  7. 由于操作系统知道它是只读的,因此它不能"脏",因此如果需要RAM,它将简单地丢弃它,并在需要时重新读取.

但在这种情况下,你还得到:

  • 由于数据是懒惰读取的,因此在线程扩展到所有可用内核,将选择每个RAM页面; 这将允许操作系统选择接近该过程的页面.

所以,我认为如果有两个条件:

  • 磁盘和RAM之间不以任何方式处理数据
  • 数据的每个部分(大部分)由一个单独的线程读取,而不是全部触及.

然后,只需使用mmap,您就应该能够利用任何规模的机器.

如果数据的每个部分都由多个单个线程读取,则可能您可以识别哪些线程(大多数)将共享相同的页面,并尝试提示调度程序将这些线程保存在同一个NUMA单元中.