package edu.oregonstate.cartography.grid.operators; import edu.oregonstate.cartography.grid.Grid; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * A base class for multi-threaded grid operators. Allocates as many threads * for operating on the grid as CPU cores are available. * @author Bernhard Jenny, Institute of Cartography, ETH Zurich */ public abstract class ThreadedGridOperator implements GridOperator { /** * Operate row-wise on the passed source grid and store the result in the passed * destination grid. The source and the destination can be the same object * if isOverwrittingSupported() returns true. * @param src The source grid. * @param dst The destination grid. * @param startRow The index of the first row to operate on. * @param endRow The index of the last row. Derived classes should not * operate on this row. It may be equal to src.getRows(). */ protected abstract void operate(Grid src, Grid dst, int startRow, int endRow); /** * Returns whether the source and destination grids can be the same object. * Defaults to true, that is, the source grid can be overwritten. Needs to * be overridden by derived classes that access multiple cells for computing * a single new cell value. In this case, false needs to be returned. * @return True if the source and destination can be identical, false otherwise. */ public boolean isOverwritingSupported() { return true; } /** * Creates a new grid that will store the results of the operator. This * method creates a new grid of the same size as the source grid. It must be * overridden if the derived operator generates a grid that has a different * dimension or position than the source grid. * @param src The source grid. * @return The new grid of the same size and position as the source grid. */ protected Grid initDestinationGrid(Grid src) { if (src == null || !src.isWellFormed()) { throw new IllegalArgumentException(getName() + ": invalid source grid"); } int nrows = src.getRows(); int ncols = src.getCols(); Grid newGrid = new Grid(ncols, nrows, src.getCellSize()); newGrid.setWest(src.getWest()); newGrid.setSouth(src.getSouth()); return newGrid; } /** * Apply the filter and store the result in a new grid that is returned. * @param src The source grid. * @return The new grid storing the result. */ @Override public Grid operate(Grid src) { Grid dst = initDestinationGrid(src); return operate(src, dst); } /** * Apply the filter and store the result in the passed destination grid. * @param src The source grid. * @param dst The destination grid. May not be null. * @return The passed dst grid is returned. */ public Grid operate(Grid src, Grid dst) { if (src == null || !src.isWellFormed()) { throw new IllegalArgumentException(getName() + ": invalid source grid"); } if (dst == null || !dst.isWellFormed()) { throw new IllegalArgumentException(getName() + ": invalid destination grid"); } if (!isOverwritingSupported() && src.getGrid() == dst.getGrid()) { throw new IllegalArgumentException(getName() + ": overwriting source grid is not possible"); } int nRows = src.getRows(); int nThreads = Runtime.getRuntime().availableProcessors(); ArrayList<GridOperatorThread> threads = new ArrayList(nThreads); int rowChunk = (nRows / nThreads) + 1; for (int i = 0; i < nThreads; i++) { int startRow = i * rowChunk; int endRow = Math.min(nRows, startRow + rowChunk); GridOperatorThread t = new GridOperatorThread(src, dst, startRow, endRow); t.setName(this.getName() + " " + i); threads.add(t); t.start(); } for (Thread t : threads) { try { t.join(); } catch (InterruptedException ex) { Logger.getLogger(ThreadedGridOperator.class.getName()).log(Level.SEVERE, null, ex); } } return dst; } /** * A private utility class for wrapping a thread. */ private class GridOperatorThread extends Thread { final Grid srcGrid; final Grid dstGrid; final int startRow; final int endRow; public GridOperatorThread(Grid srcGrid, Grid dstGrid, int startRow, int endRow) { this.srcGrid = srcGrid; this.dstGrid = dstGrid; this.startRow = startRow; this.endRow = endRow; } @Override public void run() { operate(srcGrid, dstGrid, startRow, endRow); } } }