sisl.mpi.BlockCyclicDistribution
- class sisl.mpi.BlockCyclicDistribution
Bases:
DistributionBlocks of
blocksizeindices dealt round-robin, as ScaLAPACK lays out a matrix.Ranks are visited in order, each taking
blocksizeconsecutive indices, and the cycle repeats until the indices run out. A rank therefore receives one block, then later another, and so on – which is whyrangereturns several runs rather than one.Equivalently: index
ibelongs to blocki // blocksize, and blockkto rankk % size.blocksize=1, the default, is plain cyclic: indexito ranki % size. That balances load when work varies systematically along the axis, at the cost of destroying locality. Larger values give the block-cyclic layout a distributed dense eigensolver expects, so a matrix destined for one should be laid out this way from the start rather than redistributed later.blocksize=0is shorthand for one contiguous block per rank – the whole axis split intosizepieces, which maximises locality, e.g. k-points or energies that are close to each other end up on the same rank.Contiguous is a special case rather than a large
blocksizebecause no uniform block size reproduces it. Withn=10, size=3a contiguous split is[4, 3, 3], whileblocksize=4gives[4, 4, 2]; worse, atn=5, size=4a uniformblocksize=2yields[2, 2, 1, 0]and leaves a rank idle, where the contiguous split gives[2, 1, 1, 1]. The remainder is therefore spread one index per rank across the leading ranks, so the largest block exceeds the smallest by at most one.For any other
blocksizethe load is only even when the blocks divide the indices evenly. That is inherent – the block structure is the point, and evening out the tail would break it.- Parameters:
n – number of indices to distribute
blocksize – indices per block. 1 is plain cyclic, 0 one contiguous block per rank; ScaLAPACK codes typically use 32 or 64.
comm – communicator the indices are distributed across,
Nonemeanssisl.mpi.get_comm.
Examples
With
commhaving 3 ranks:>>> BlockCyclicDistribution(10, comm=comm).indices(1) array([1, 4, 7], dtype=int32) >>> BlockCyclicDistribution(10, 2, comm=comm).range(0) ((0, 2), (6, 8)) >>> BlockCyclicDistribution(10, 0, comm=comm).counts array([4, 3, 3], dtype=int32)
Methods
count([rank])Number of indices owned by
rank(default this process).indices([rank])The indices owned by
rank(default this process), ascending.owner(indices)Which block an index falls in decides the rank.
range([rank])The contiguous runs owned by
rank, as ascending(start, stop)pairs.Attributes
Indices per block; 0 meaning one contiguous block per rank.
The communicator the indices are distributed across.
Whether each rank holds a single unbroken run.
Number of indices owned by each rank.
Total number of indices across all ranks.
Indices consumed by one full cycle over all ranks.
The rank of this process in
comm.Number of ranks this is distributed across.
- __call__(obj, *args, **kwargs)
Distribute obj, by calling
obj.__sisl_distribute__(self, *args, **kwargs).
- range(rank=None)[source]
The contiguous runs owned by
rank, as ascending(start, stop)pairs.A rank owning nothing gets an empty tuple. Runs are the primitive rather than a flat index array because they slice: a consumer can copy each run directly instead of gathering scattered elements.
- property comm
The communicator the indices are distributed across.