def run(self): """ this is the simulation loop of each cell """ cellsize = self.cellsize while self.iteration < NUM_ITER: # in each iteration, this cell's particles move randomly. some # particles might move out of the boundary of this cell, and need # to be sent to neighboring cells. # We could directly send the list of outgoing particles to each # neighbor cell, but this would cause Charm4py to pickle a possibly # long list of Particle objects, and is not the most efficient # option. Instead, for each neighbor, we insert the particle data # of outgoing particles into an array, and send that. This bypasses # pickling (Charm4py copies the contents of the array buffer # directly into a message) # we are sending an array of particle data to each neighbor outgoingParticles = {nb_idx: array.array('d') for nb_idx in self.neighbor_indexes} i = 0 while i < len(self.particles): p = self.particles[i] p.perturb(cellsize) dest_cell = (int(p.coords[0] / cellsize[0]), int(p.coords[1] / cellsize[1])) if dest_cell != self.thisIndex: # this particle is moving to a neighboring cell outgoingParticles[dest_cell].extend(p.coords) self.particles[i] = self.particles[-1] self.particles.pop() else: i += 1 # send outgoing particles to neighboring cells for i, channel in enumerate(self.neighbors): channel.send(outgoingParticles[self.neighbor_indexes[i]]) # receive incoming particles from neighboring cells. iawait iteratively # yields channels as they become ready (have data to receive) for channel in charm.iwait(self.neighbors): incoming = channel.recv() self.particles += [Particle(float(incoming[i]), float(incoming[i+1])) for i in range(0, len(incoming), 2)] if self.iteration % 10 == 0: # reduction to report the current max particles per cell. # this call is asynchronous and doesn't block me self.reduce(self.thisProxy[(0,0)].reportMax, len(self.particles), Reducer.max) if self.iteration % 20 == 0: # tell Charm that this cell is ready for load balancing. # load balancing will start when all the cells call this. self.AtSync() # now we need to exit the coroutine because the coroutine stack # doesn't migrate self.iteration += 1 return # the cell proceeds to the next iteration without need for global synchronization self.iteration += 1 # simulation done (when all the cells reach this point) self.reduce(self.sim_done_future)
def work(self, mainProxy): """ this is the main simulation loop for each chare """ # size of my rectangular portion of the image self.mywidth = IMAGE_WIDTH // CHARE_ARRAY_WIDTH self.myheight = IMAGE_HEIGHT // CHARE_ARRAY_HEIGHT self.setInitialConditions() i = self.thisIndex X, Y = CHARE_ARRAY_WIDTH, CHARE_ARRAY_HEIGHT # establish a Channel with neighbor chares in the 2D grid left = Channel(self, remote=self.thisProxy[(i[0]-1)%X, i[1]]) right = Channel(self, remote=self.thisProxy[(i[0]+1)%X, i[1]]) top = Channel(self, remote=self.thisProxy[i[0], (i[1]-1)%Y]) bottom = Channel(self, remote=self.thisProxy[i[0], (i[1]+1)%Y]) width, height = self.mywidth, self.myheight # coordinate where my portion of the image is located sx = self.thisIndex[0] * width sy = self.thisIndex[1] * height # data will store my portion of the image data = np.zeros(width*height*3, dtype=np.uint8) buffers = [None] * 4 # run simulation now while True: top_edge = self.pressure[[0],:].reshape(width) bottom_edge = self.pressure[[-1],:].reshape(width) left_edge = self.pressure[:,[0]].reshape(height) right_edge = self.pressure[:,[-1]].reshape(height) # send ghost values to neighbors left.send(RIGHT, left_edge) right.send(LEFT, right_edge) bottom.send(UP, bottom_edge) top.send(DOWN, top_edge) # receive ghost values from neighbors. iawait iteratively yields # channels as they become ready (have data to receive) for channel in charm.iwait((left, right, bottom, top)): side, ghost_values = channel.recv() buffers[side] = ghost_values check_and_compute(height, width, buffers[LEFT], buffers[RIGHT], buffers[UP], buffers[DOWN], self.pressure, self.pressure_old, self.pressure_new) # advance to next step by shifting the data back one step in time self.pressure_old, self.pressure, self.pressure_new = self.pressure, self.pressure_new, self.pressure_old # draw my part of the image, plus a nice 1 pixel border along my # right/bottom boundary fill_subimage(data, width, height, self.pressure) # provide my portion of the image to the mainchare mainProxy.depositSubImage(data, (sx, sy), (width, height)) # wait for message from mainchare to resume simulation self.resumeFuture = Future() reset = self.resumeFuture.get() if reset: self.setInitialConditions()
def work(self, level, done_fut): msgs = 0 start = LEVELS_START[level] me = self.thisProxy[self.thisIndex] channels = list(self.channels[level]) for i in range(LEVELS_NUM_ITER[level]): random.shuffle(channels) for ch in channels: ch.send(me, start + i) for ch in charm.iwait(channels): remote, data = ch.recv() assert data == start + i assert ch.remote == remote assert remote in self.nbs[level] msgs += 1 self.reduce(done_fut, msgs, Reducer.sum)
def __init__(self, args): N = min(4, charm.numPes()) g = Group(Test) channels = [Channel(self, g[i]) for i in range(N)] for i in range(N): g[i].work(self.thisProxy) channels.reverse() t0 = time.time() idx = 0 for ch in charm.iwait(channels): assert ch.recv() == idx idx += 1 print(time.time() - t0) assert idx == N exit()
def main(args): N = min(4, charm.numPes()) g = Group(Test) futures = [Future() for _ in range(N)] for i in range(N): g[i].work(futures[i]) futures.reverse() t0 = time.time() idx = 0 for f in charm.iwait(futures): assert f.get() == idx idx += 1 print(time.time() - t0) assert idx == N exit()
def run(self): """ this is the main computation loop """ iteration = 0 converged = False while not converged and iteration < MAX_ITER: # send ghost faces to my neighbors. sends are asynchronous if not self.leftBound: self.left_nb.send(RIGHT, self.temperature[1, 1:blockDimY + 1]) if not self.rightBound: self.right_nb.send( LEFT, self.temperature[blockDimX, 1:blockDimY + 1]) if not self.topBound: self.top_nb.send(BOTTOM, self.temperature[1:blockDimX + 1, 1]) if not self.bottomBound: self.bottom_nb.send( TOP, self.temperature[1:blockDimX + 1, blockDimY]) # receive ghost data from neighbors. iawait iteratively yields # channels as they become ready (have data to receive) for nb in charm.iwait(self.nbs): direction, ghosts = nb.recv() if direction == LEFT: self.temperature[0, 1:len(ghosts) + 1] = ghosts elif direction == RIGHT: self.temperature[blockDimX + 1, 1:len(ghosts) + 1] = ghosts elif direction == TOP: self.temperature[1:len(ghosts) + 1, 0] = ghosts elif direction == BOTTOM: self.temperature[1:len(ghosts) + 1, blockDimY + 1] = ghosts else: charm.abort('Invalid direction') max_error = check_and_compute(self.temperature, self.new_temperature, self.istart, self.ifinish, self.jstart, self.jfinish) self.temperature, self.new_temperature = self.new_temperature, self.temperature converged = self.allreduce(max_error <= THRESHOLD, Reducer.logical_and).get() iteration += 1 if self.thisIndex == (0, 0): # notify main function that computation has ended and report the iteration number self.sim_done_future.send(iteration)