20

最近,当我使用多处理模块和 mpi4py 作为通信工具来测量并行应用程序的性能时,我观察到了一种奇怪的效果。

该应用程序对数据集执行进化算法。除求值外,大多数操作都是按顺序完成的。在应用所有进化算子之后,所有个体都需要接收新的适应度值,这是在评估期间完成的。基本上它只是对浮点数列表(python 的)执行的数学计算。在评估之前,数据集由 mpi 的 scatter 或 python 的 Pool.map 分散,然后是并行评估,然后数据通过 mpi 的收集或再次通过 Pool.map 机制返回。

我的基准测试平台是运行 Ubuntu 11.10 的虚拟机(virtualbox),Core i7(4/8 核)上具有 Open MPI 1.4.3、8 GB RAM 和 SSD 驱动器。

我发现真正令人惊讶的是,我获得了不错的加速,但是取决于通信工具,经过一定的进程阈值后,性能变得更差。可以通过下面的图片来说明。

y 轴 - 处理时间
x 轴 - 处理
颜色的 nr - 每个个体的大小(浮动的 nr)

1)使用多处理模块 - Pool.map 在此处输入图像描述

2) 使用 mpi - 分散/聚集 在此处输入图像描述

3) 两张图片相互重叠 在此处输入图像描述

起初我认为这是超线程的错,因为对于大型数据集,它在达到 4 个进程(4 个物理核心)后会变慢。但是,它在多处理情况下也应该可见,但不是。我的另一个猜测是 mpi 通信方法远不如 python 有效,但我觉得很难相信。

有人对这些结果有任何解释吗?

添加:

毕竟,我开始相信这是超线程的错误。我在具有核心 i5(2/4 核心)的机器上测试了我的代码,并且 3 个或更多进程的性能更差。我想到的唯一解释是我正在使用的 i7 没有足够的资源(缓存?)来计算与超线程同时进行的评估,并且需要安排超过 4 个进程在 4 个物理内核上运行。

然而有趣的是,当我使用 mpi htop 显示所有 8 个逻辑核心的完全利用,这应该表明上述陈述是不正确的。另一方面,当我使用 Pool.Map 时,它并没有完全利用所有内核。它最多使用一个或 2 个,其余的只是部分使用,同样不知道它为什么会这样。明天我将附上显示此行为的屏幕截图。

我没有在代码中做任何花哨的事情,它真的很简单(我没有给出整个代码不是因为它是秘密的,而是因为它需要安装额外的库,如 DEAP。如果有人真的对这个问题感兴趣并准备好了要安装 DEAP,我可以准备一个简短的示例)。MPI 的代码有点不同,因为它不能处理人口容器(从列表继承)。当然有一些开销,但没什么大不了的。除了我在下面显示的代码之外,其余部分是相同的。

池.map:

def eval_population(func, pop):
    for ind in pop:
        ind.fitness.values = func(ind)

    return pop

# ...
self.pool = Pool(8)
# ...

for iter_ in xrange(nr_of_generations):
    # ...
    self.pool.map(evaluate, pop) # evaluate is really an eval_population alias with a certain function assigned to its first argument.
    # ...

MPI - 分散/聚集

def divide_list(lst, n):
    return [lst[i::n] for i in xrange(n)]

def chain_list(lst):
    return list(chain.from_iterable(lst))

def evaluate_individuals_in_groups(func, rank, individuals):
    comm = MPI.COMM_WORLD
    size = MPI.COMM_WORLD.Get_size()

    packages = None
    if not rank:
        packages = divide_list(individuals, size)

    ind_for_eval = comm.scatter(packages)
    eval_population(func, ind_for_eval)

    pop_with_fit = comm.gather(ind_for_eval)

    if not rank:
        pop_with_fit = chain_list(pop_with_fit)
        for index, elem in enumerate(pop_with_fit):
            individuals[index] = elem

for iter_ in xrange(nr_of_generations):
        # ...
        evaluate_individuals_in_groups(self.func, self.rank, pop)
        # ...

添加 2: 正如我之前提到的,我在 i5 机器(2/4 核)上进行了一些测试,结果如下: 在此处输入图像描述

我还找到了一台具有 2 个至强(2x 6/12 内核)的机器并重复了基准测试: 在此处输入图像描述

现在我有 3 个相同行为的示例。当我在比物理内核更多的进程中运行我的计算时,它开始变得更糟。我相信是因为资源不足,同一物理核心上的进程无法同时执行。

4

1 回答 1

6

MPI 实际上是为进行节点间通信而设计的,因此可以通过网络与其他机器通信。与例如线程相比,在同一节点上使用 MPI 可能会导致必须发送的每条消息的开销很大。

mpi4py 为每条消息制作一个副本,因为它是针对分布式内存使用的。如果您的 OpenMPI 未配置为使用共享内存进行节点内通信,则此消息将通过内核的 tcp 堆栈发送,然后返回,以传递给另一个进程,这将再次增加一些开销。

如果你只打算在同一台机器上进行计算,这里不需要使用 mpi。

其中一些在此线程中进行了讨论。

更新ipc-benchmark项目试图弄清不同通信类型在不同系统上的执行方式 。(多核、多处理器、共享内存)尤其是这如何影响虚拟机!

我建议在虚拟机上运行 ipc-benchmark,然后发布结果。如果它们看起来像这个基准,它可以让您深入了解 tcp、套接字和管道之间的区别。

于 2013-06-21T13:32:15.723 回答