大家好,我有一个长度为N的数组,我想在'size'处理器之间尽可能地划分它。 N /大小有一个余数,例如1000个数组元素除以7个进程,或14个进程3个进程。
我至少知道MPI中的两种工作共享方式,例如:
for (i=rank; i<N;i+=size){ a[i] = DO_SOME_WORK }
但是,这并没有将数组划分为连续的块,我想这样做,因为我认为由于IO原因更快。
另一个我知道的是:
int count = N / size;
int start = rank * count;
int stop = start + count;
// now perform the loop
int nloops = 0;
for (int i=start; i<stop; ++i)
{
a[i] = DO_SOME_WORK;
}
但是,使用这种方法,对于我的第一个例子,我们得到1000/7 = 142 = count。所以最后一个等级从852开始,到994结束。最后6行被忽略。
最好的解决方案是将这样的东西附加到上一个代码中吗?
int remainder = N%size;
int start = N-remainder;
if (rank == 0){
for (i=start;i<N;i++){
a[i] = DO_SOME_WORK;
}
这看起来很混乱,如果它是最好的解决方案我很惊讶我没有在其他地方见过它。
感谢您的帮助!
答案 0 :(得分:6)
如果我有N
个任务(例如,数组元素)和size
个工作人员(例如,MPI排名),我会按如下方式进行:
int count = N / size;
int remainder = N % size;
int start, stop;
if (rank < remainder) {
// The first 'remainder' ranks get 'count + 1' tasks each
start = rank * (count + 1);
stop = start + count;
} else {
// The remaining 'size - remainder' ranks get 'count' task each
start = rank * count + remainder;
stop = start + (count - 1);
}
for (int i = start; i <= stop; ++i) { a[i] = DO_SOME_WORK(); }
这就是它的工作原理:
/*
# ranks: remainder size - remainder
/------------------------------------\ /-----------------------------\
rank: 0 1 remainder-1 size-1
+---------+---------+-......-+---------+-------+-------+-.....-+-------+
tasks: | count+1 | count+1 | ...... | count+1 | count | count | ..... | count |
+---------+---------+-......-+---------+-------+-------+-.....-+-------+
^ ^ ^ ^
| | | |
task #: rank * (count+1) | rank * count + remainder |
| |
task #: rank * (count+1) + count rank * count + remainder + count - 1
\------------------------------------/
# tasks: remainder * count + remainder
*/
答案 1 :(得分:3)
考虑您的“1000步和7个流程”示例。
简单除法不起作用,因为整数除法(在C中)给你发言权,你留下一些余数:即1000/7是142,并且将有6个doodads挂出
天花板分区有相反的问题:ceil(1000/7)是143,但是最后一个处理器超出阵列,或者最终用比其他处理器做得少。
您要求的方案是将余数均匀分配到处理器上。一些流程应该有142个,其他流程143个。必须有一个更正式的方法,但考虑到这个问题在过去六个月中得到的关注可能不是。
这是我的方法。每个进程都需要执行此算法,并选择自己需要的答案。
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char ** argv)
{
#define NR_ITEMS 1000
int i, rank, nprocs;;
int *bins;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
bins = calloc(nprocs, sizeof(int));
int nr_alloced = 0;
for (i=0; i<nprocs; i++) {
remainder = NR_ITEMS - nr_alloced;
buckets = (nprocs - i);
/* if you want the "big" buckets up front, do ceiling division */
bins[i] = remainder / buckets;
nr_alloced += bins[i];
}
if (rank == 0)
for (i=0; i<nprocs; i++) printf("%d ", bins[i]);
MPI_Finalize();
return 0;
}
答案 2 :(得分:2)
这是一个封闭形式的解决方案。
设N =数组长度,P =处理器数。
从 j = 0到 P -1,
处理器上的数组的起点 j = floor( N * j / P )
处理器上的数组长度 j = floor( N *( j + 1)/ P ) - floor( N * j / P )
答案 3 :(得分:1)
我认为最好的解决方案是为自己编写一个小功能,以便跨进程分配。这里有一些伪代码,我确定你能写出C(在你的问题中是C吗?)比我能做得更好。
function split_evenly_enough(num_steps, num_processes)
return = repmat(0, num_processes) ! pseudo-Matlab for an array of num_processes 0s
steps_per_process = ceiling(num_steps/num_processes)
return = steps_per_process - 1 ! set all elements of the return vector to this number
return(1:mod(num_steps, num_processes)) = steps_per_process ! some processes have 1 more step
end
答案 4 :(得分:1)
我知道这已经很久没了,但是一个简单的方法是给每个进程提供(项目数)/(进程数)+(如果process_num&lt; num_items mod num_procs则为1)。在python中,一个具有工作计数的数组:
# Number of items
NI=128
# Number of processes
NP=20
# Items per process
[NI/NP + (1 if P < NI%NP else 0)for P in range(0,NP)]
答案 5 :(得分:1)
改进@Alexander 的回答:利用 min
来浓缩逻辑。
int count = N / size;
int remainder = N % size;
int start = rank * count + min(rank, remainder);
int stop = (rank + 1) * count + min(rank + 1, remainder);
for (int i = start; i < stop; ++i) { a[i] = DO_SOME_WORK(); }
答案 6 :(得分:0)
这个怎么样?
int* distribute(int total, int processes) {
int* distribution = new int[processes];
int last = processes - 1;
int remaining = total;
int process = 0;
while (remaining != 0) {
++distribution[process];
--remaining;
if (process != last) {
++process;
}
else {
process = 0;
}
}
return distribution;
}
这个想法是你将一个元素分配给第一个进程,然后将元素分配给第二个进程,然后将元素分配给第三个进程,依此类推,每当到达最后一个进程时跳回第一个进程。 / p>
即使进程数大于而不是元素数,此方法也能正常工作。它只使用非常简单的操作,因此应该非常快。
答案 7 :(得分:0)
我遇到了类似的问题,这是我使用Python和mpi4py API的非最佳解决方案。一个最佳的解决方案将考虑处理器的布局,这里额外的工作是低级别的。不均衡的工作量只有一项任务不同,所以一般来说这不应该是一件大事。
from mpi4py import MPI
import sys
def get_start_end(comm,N):
"""
Distribute N consecutive things (rows of a matrix , blocks of a 1D array)
as evenly as possible over a given communicator.
Uneven workload (differs by 1 at most) is on the initial ranks.
Parameters
----------
comm: MPI communicator
N: int
Total number of things to be distributed.
Returns
----------
rstart: index of first local row
rend: 1 + index of last row
Notes
----------
Index is zero based.
"""
P = comm.size
rank = comm.rank
rstart = 0
rend = N
if P >= N:
if rank < N:
rstart = rank
rend = rank + 1
else:
rstart = 0
rend = 0
else:
n = N//P # Integer division PEP-238
remainder = N%P
rstart = n * rank
rend = n * (rank+1)
if remainder:
if rank >= remainder:
rstart += remainder
rend += remainder
else:
rstart += rank
rend += rank + 1
return rstart, rend
if __name__ == '__main__':
comm = MPI.COMM_WORLD
n = int(sys.argv[1])
print(comm.rank,get_start_end(comm,n))