dictionary - Summing Python Objects with MPI's Allreduce -
i using sparse tensor array manipulation built using dictionaries , counters in python. make possible use array manipulation in parallel. bottom line have ended having counters on each node add using mpi.allreduce (or nice solution). instance counters 1 can this
a = counter({a:1, b:2, c:3}) b = counter({b:1, c:2, d:3})
such that
c = a+b = counter({a:1, b:3, c:5, d:3}).
i same operation relevant nodes,
mpi.allreduce(send_counter, recv_counter, mpi.sum)
however, mpi doesn't seem recognize operation on dictionaries/counters, throwing error expecting buffer or list/tuple
. best option `user-defined operation,' or there way allreduce add counters? thanks,
edit (7/14/15): have attempted create user operation dictionaries there have been discrepancies. wrote following
def dict_sum(dict1, dict2, datatype): key in dict2: try: dict1[key] += dict2[key] except keyerror: dict1[key] = dict2[key]
and when told mpi function did this:
dictsumop = mpi.op.create(dict_sum, commute=true)
and in code used as
the_result = comm.allreduce(mydict, dictsumop)
however, threw unsupported operand '+' type dict
. wrote
the_result = comm.allreduce(mydict, op=dictsumop)
and throws dict1[key] += dict2[key] typeerror: 'nonetype' object has no attribute '__getitem__'
apparently wants know things dictionaries? how tell have type dictionary?
neither mpi nor mpi4py knows counters in particular, need create own reduction operation work; same other sort of python object:
#!/usr/bin/env python mpi4py import mpi import collections def addcounter(counter1, counter2, datatype): item in counter2: counter1[item] += counter2[item] return counter1 if __name__=="__main__": comm = mpi.comm_world if comm.rank == 0: mycounter = collections.counter({'a':1, 'b':2, 'c':3}) else: mycounter = collections.counter({'b':1, 'c':2, 'd':3}) countersumop = mpi.op.create(addcounter, commute=true) totcounter = comm.allreduce(mycounter, op=countersumop) print comm.rank, totcounter
here we've taken function sums 2 counter objects , created mpi operator out of them mpi.op.create; mpi4py unpickle objects, run function combine these items pairwise, pickle partial result , send off next task.
note we're using (lowercase) allreduce, works on arbitrary python objects, rather (uppercase) allreduce, works on numpy arrays or moral equivalents (buffers, map onto fortran/c arrays mpi api designed on).
running gives:
$ mpirun -np 2 python ./counter_reduce.py 0 counter({'c': 5, 'b': 3, 'd': 3, 'a': 1}) 1 counter({'c': 5, 'b': 3, 'd': 3, 'a': 1}) $ mpirun -np 4 python ./counter_reduce.py 0 counter({'c': 9, 'd': 9, 'b': 5, 'a': 1}) 2 counter({'c': 9, 'd': 9, 'b': 5, 'a': 1}) 1 counter({'c': 9, 'd': 9, 'b': 5, 'a': 1}) 3 counter({'c': 9, 'd': 9, 'b': 5, 'a': 1})
and modest changes works generic dictionary:
#!/usr/bin/env python mpi4py import mpi def addcounter(counter1, counter2, datatype): item in counter2: if item in counter1: counter1[item] += counter2[item] else: counter1[item] = counter2[item] return counter1 if __name__=="__main__": comm = mpi.comm_world if comm.rank == 0: mydict = {'a':1, 'c':"hello "} else: mydict = {'c':"world!", 'd':3} countersumop = mpi.op.create(addcounter, commute=true) totdict = comm.allreduce(mydict, op=countersumop) print comm.rank, totdict
running giving
$ mpirun -np 2 python dict_reduce.py 0 {'a': 1, 'c': 'hello world!', 'd': 3} 1 {'a': 1, 'c': 'hello world!', 'd': 3}
Comments
Post a Comment