Partitions a systolic array into rectangular tiles.
The default strategy divides the grid into approximately equal-sized
rectangular tiles. Tile size is configurable via the tile_rows:
and tile_cols: options.
Why tiles?
Tiles balance granularity and locality:
- Too fine (1 PE per tile) -- excessive coordination overhead
- Too coarse (1 tile for the whole array) -- no parallelism
- Just right -- good data locality, parallel across tiles
Partitioning algorithm
Given a grid of rows x cols and tile size tr x tc:
- The grid is divided into
ceil(rows/tr) x ceil(cols/tc)tiles - Edge tiles may be smaller than
tr x tc - Each tile owns the PEs and internal links within its bounds
- A link is internal to a tile when both its
fromandtoendpoints are coordinates within that tile - Links crossing tile boundaries (where one endpoint is in a different tile) are not assigned to any tile; they are managed globally by the partitioned backend's BSP read/write cycle
Examples
iex> alias ExSystolic.{Array, TilePartitioner, PE.MAC}
iex> array = Array.new(rows: 4, cols: 4) |> Array.fill(MAC) |> Array.connect(:west_to_east) |> Array.connect(:north_to_south)
iex> tiles = TilePartitioner.partition(array, tile_rows: 2, tile_cols: 2)
iex> length(tiles)
4
iex> hd(tiles).id
{0, 0}
Summary
Functions
Partitions an array into tiles.
Functions
@spec partition( ExSystolic.Array.t(), keyword() ) :: [ExSystolic.Tile.t()]
Partitions an array into tiles.
Options
:tile_rows-- number of rows per tile (default:rows):tile_cols-- number of columns per tile (default:cols)
When no tile dimensions are given, the entire array becomes a single tile (equivalent to the interpreted backend).
Examples
iex> alias ExSystolic.{Array, TilePartitioner, PE.MAC}
iex> array = Array.new(rows: 3, cols: 3) |> Array.fill(MAC)
iex> tiles = TilePartitioner.partition(array, tile_rows: 2, tile_cols: 2)
iex> length(tiles)
4