最近,当我使用多处理模块和mpi4py作为通信工具测量我的并行应用程序的性能时,我观察到了一个奇怪的影响。
该应用程序对数据集进行演化算法。除了评估之外,大多数操作是按顺序完成的。在所有进化运算符应用之后,所有个体都需要接受新的适应度值,这是在评估过程中完成的。基本上,这只是一个浮动列表(python)的数学计算。在评估之前,数据集由mpi的分散或python的Pool.map分散,然后进行并行评估,然后数据通过mpi的收集或再次的Pool.map机制回来。
我的基准平台是运行Ubuntu 11.10的虚拟机(虚拟机),在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它并不完全利用所有的核心。它最多只使用一个或两个,而仅部分使用其余的,再次不知道为什么它以这种方式行事。明天我会附上一个屏幕截图,显示这个行为。
我没有在代码中做任何事情,这是非常简单的(我没有给整个代码不是因为它的秘密,而是因为它需要其他的库,如DEAP被安装如果有人真的对这个问题感兴趣,准备好要安装DEAP我可以准备一个简短的例子)。 MPI的代码有点不同,因为它不能处理一个人口容器(从列表继承)。当然有一些开销,但没有什么重大的。除了我下面的代码,其余的是一样的。
Pool.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个xeons(2x 6/12内核),并重复了基准:
现在我有3个相同行为的例子。当我在更多的进程中运行我的计算,而不是物理内核,它开始变得更糟。我相信这是因为同一物理核心的进程由于缺乏资源而无法同时执行。