python mpi多线程_使用mpi而不是多处理模块时,python中的并行应用变得更慢

最近,当我使用多处理模块和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个相同行为的例子。当我在更多的进程中运行我的计算,而不是物理内核,它开始变得更糟。我相信这是因为同一物理核心的进程由于缺乏资源而无法同时执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个比较复杂的问题,需要一定的MPI编程和线性代数知识。以下是一个简单的示例代码,帮助您理解如何使用MPI多线程从文件读取矩阵、并行矩阵求逆并输出逆矩阵。 ```c #include <stdio.h> #include <stdlib.h> #include <mpi.h> #include <math.h> #include <stdbool.h> #include <string.h> #include <time.h> #define TAG 0 #define ROOT 0 #define MAX_FILENAME_SIZE 256 int rank, size; MPI_Status status; void read_matrix(double *matrix, int n, char *filename) { FILE *fp; fp = fopen(filename, "r"); if (fp == NULL) { printf("File open error\n"); return; } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { fscanf(fp, "%lf", &matrix[i * n + j]); } } fclose(fp); } void print_matrix(double *matrix, int n) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { printf("%.2lf ", matrix[i * n + j]); } printf("\n"); } } void identity_matrix(double *matrix, int n) { memset(matrix, 0, sizeof(double) * n * n); for (int i = 0; i < n; i++) { matrix[i * n + i] = 1.0; } } void matrix_multiplication(double *matrix1, double *matrix2, double *result, int n) { double *temp = (double*)malloc(sizeof(double) * n * n); memset(temp, 0, sizeof(double) * n * n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { temp[i * n + j] += matrix1[i * n + k] * matrix2[k * n + j]; } } } memcpy(result, temp, sizeof(double) * n * n); free(temp); } void swap(double *a, double *b) { double temp = *a; *a = *b; *b = temp; } bool is_zero(double val) { return fabs(val) < 1e-8; } bool gauss_elimination(double *matrix, double *inverse, int n) { int *pivot = (int*)malloc(sizeof(int) * n); for (int i = 0; i < n; i++) { pivot[i] = i; } for (int k = 0; k < n; k++) { int max_index = k; double max_value = fabs(matrix[pivot[k] * n + k]); for (int i = k+1; i < n; i++) { if (fabs(matrix[pivot[i] * n + k]) > max_value) { max_index = i; max_value = fabs(matrix[pivot[i] * n + k]); } } if (is_zero(max_value)) { printf("Matrix is not invertible!\n"); return false; } if (max_index != k) { swap(&pivot[k], &pivot[max_index]); } double pivot_value = matrix[pivot[k] * n + k]; for (int j = k; j < n; j++) { matrix[pivot[k] * n + j] /= pivot_value; inverse[pivot[k] * n + j] /= pivot_value; } for (int i = k+1; i < n; i++) { double factor = matrix[pivot[i] * n + k]; for (int j = k; j < n; j++) { matrix[pivot[i] * n + j] -= factor * matrix[pivot[k] * n + j]; inverse[pivot[i] * n + j] -= factor * inverse[pivot[k] * n + j]; } } } for (int k = n-1; k >= 0; k--) { for (int i = k-1; i >= 0; i--) { double factor = matrix[pivot[i] * n + k]; for (int j = k; j < n; j++) { matrix[pivot[i] * n + j] -= factor * matrix[pivot[k] * n + j]; inverse[pivot[i] * n + j] -= factor * inverse[pivot[k] * n + j]; } } } free(pivot); return true; } int main(int argc, char **argv) { if (argc < 2) { printf("Usage: ./matrix_inverse <filename>\n"); return -1; } char filename[MAX_FILENAME_SIZE]; strcpy(filename, argv[1]); MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); double *matrix, *inverse; int n; double start_time, end_time; if (rank == ROOT) { // Read matrix from file FILE *fp = fopen(filename, "r"); if (fp == NULL) { printf("File open error\n"); return -1; } fscanf(fp, "%d", &n); fclose(fp); matrix = (double*)malloc(sizeof(double) * n * n); inverse = (double*)malloc(sizeof(double) * n * n); read_matrix(matrix, n, filename); identity_matrix(inverse, n); start_time = MPI_Wtime(); } MPI_Bcast(&n, 1, MPI_INT, ROOT, MPI_COMM_WORLD); int rows_per_process = n / size; double *submatrix = (double*)malloc(sizeof(double) * rows_per_process * n); double *subinverse = (double*)malloc(sizeof(double) * rows_per_process * n); MPI_Scatter(matrix, rows_per_process * n, MPI_DOUBLE, submatrix, rows_per_process * n, MPI_DOUBLE, ROOT, MPI_COMM_WORLD); MPI_Scatter(inverse, rows_per_process * n, MPI_DOUBLE, subinverse, rows_per_process * n, MPI_DOUBLE, ROOT, MPI_COMM_WORLD); gauss_elimination(submatrix, subinverse, rows_per_process); MPI_Gather(subinverse, rows_per_process * n, MPI_DOUBLE, inverse, rows_per_process * n, MPI_DOUBLE, ROOT, MPI_COMM_WORLD); if (rank == ROOT) { end_time = MPI_Wtime(); printf("Inverse matrix:\n"); print_matrix(inverse, n); printf("Elapsed time: %lf seconds\n", end_time - start_time); free(matrix); free(inverse); } free(submatrix); free(subinverse); MPI_Finalize(); return 0; } ``` 在这个代码,我们使用MPI的Scatter和Gather函数来将矩阵分发给多个进程,并将结果收集到主进程。在主进程,我们使用MPI_Wtime来计算整个算法的运行间。在每个进程,我们使用高斯消元法来求解子矩阵的逆矩阵。最后,我们将所有子矩阵的逆矩阵合并成最终的逆矩阵。 需要注意的是,这个代码仅仅是一个简单的示例,实际上还有很多地方可以优化,比如使用更高效的高斯-约旦消元法、使用更好的矩阵乘法算法等都可以进一步提高运行效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值