218namespace distributed {
226 Axis(
const ExprNode* tensor,
int dim,
int tuple_index = 0)
227 : tensor(tensor), dim(dim), tuple_index(tuple_index) {
232 return tensor == other.tensor && dim == other.dim && tuple_index == other.tuple_index;
238 size_t operator()(
const Axis& axis)
const {
239 size_t const h1(std::hash<const ExprNode*>()(axis.tensor));
240 size_t const h2(std::hash<int>()(axis.dim));
241 size_t const h3(std::hash<int>()(axis.tuple_index));
242 return h1 ^ (h2 << 1) ^ (h3 << 2);
246using AxisGroup = std::unordered_set<Axis, AxisHash>;
250 size_t operator()(
const AxisGroup& axis_group)
const {
252 for (
auto axis : axis_group) {
253 seed ^= AxisHash()(axis) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
259using ShardingSpec = std::pair<DeviceMesh, Placement>;
262using AxisShardingSpec = std::pair<DeviceMesh, int>;
263class AxisShardingSpecEqual {
265 bool operator()(
const AxisShardingSpec& lhs,
const AxisShardingSpec& rhs)
const {
266 return ffi::StructuralEqual()(lhs.first, rhs.first) && lhs.second == rhs.second;
270class AxisShardingSpecHash {
272 size_t operator()(
const AxisShardingSpec& sharding_spec)
const {
274 seed ^= ffi::StructuralHash()(sharding_spec.first);
275 seed ^= std::hash<int>()(sharding_spec.second) << 1;
285class AxisGroupGraph {
287 enum class EdgeType { kAscend, kDescend, kSimbling };
290 static EdgeType ReverseEdgeType(EdgeType type) {
292 case EdgeType::kAscend:
293 return EdgeType::kDescend;
294 case EdgeType::kDescend:
295 return EdgeType::kAscend;
296 case EdgeType::kSimbling:
297 return EdgeType::kSimbling;
299 TVM_FFI_THROW(InternalError) <<
"Unreachable code";
303 static int GetEdgePriority(EdgeType type) {
305 case EdgeType::kAscend:
307 case EdgeType::kDescend:
309 case EdgeType::kSimbling:
312 TVM_FFI_THROW(InternalError) <<
"Unreachable code";
316 struct AxisGraphEdge {
326 bool operator==(
const AxisGraphEdge& other)
const {
327 return src == other.src && dst == other.dst && type == other.type;
334 Path AddEdge(EdgeType type) {
return {direction |= (1 << GetEdgePriority(type))}; }
336 int GetPriority()
const {
351 AxisGroupGraph() =
default;
362 void JoinAxis(Axis axis1, Axis axis2, EdgeType type) {
363 AddEdge(axis1, axis2, type);
364 AddEdge(axis2, axis1, ReverseEdgeType(type));
372 void AddSrcShardingPoint(Axis axis, AxisShardingSpec spec) {
373 src_axis_sharding_spec_.insert_or_assign(axis, spec);
379 void PropagateShardingSpec() {
380 axis_sharding_specs_priority_.clear();
381 for (
const auto& pr : src_axis_sharding_spec_) {
382 std::unordered_set<Axis, AxisHash> visited;
383 PropagateShardingSpec(pr.first, pr.second, Path(), &visited);
385 ChooseAxisShardingSpec();
394 void AddPropagationCutPoint(Axis axis, AxisShardingSpec spec) {
395 cutpoint_axis_sharding_spec_.insert_or_assign(axis, spec);
405 std::tuple<std::optional<AxisShardingSpec>,
bool> GetAxisShardingSpec(Axis axis) {
406 if (axis_sharding_specs_priority_.count(axis)) {
407 return {axis_sharding_specs_priority_[axis].begin()->first,
true};
409 return {std::nullopt,
false};
414 void AddEdge(Axis src, Axis dst, EdgeType type) {
415 if (!graph_.count(src)) {
418 graph_[src].push_back({src, dst, type});
421 void PropagateShardingSpec(Axis axis, AxisShardingSpec spec, Path path,
422 std::unordered_set<Axis, AxisHash>* visited) {
423 if (cutpoint_axis_sharding_spec_.count(axis) ||
424 (src_axis_sharding_spec_.count(axis) &&
425 !AxisShardingSpecEqual()(src_axis_sharding_spec_.at(axis), spec)) ||
426 visited->count(axis)) {
429 visited->insert(axis);
430 if (!axis_sharding_specs_priority_.count(axis)) {
431 axis_sharding_specs_priority_[axis] = {};
433 axis_sharding_specs_priority_[axis][spec] = path.GetPriority();
434 for (
auto edge : graph_[axis]) {
435 PropagateShardingSpec(edge.dst, spec, path.AddEdge(edge.type), visited);
439 void ChooseAxisShardingSpec() {
440 for (
auto& pr : axis_sharding_specs_priority_) {
441 auto& axis = pr.first;
442 auto& specs = pr.second;
443 int max_priority = std::numeric_limits<int>::min();
444 for (
auto& pr2 : specs) {
445 max_priority = std::max(max_priority, pr2.second);
447 for (
auto it = specs.begin(); it != specs.end();) {
448 if (it->second != max_priority) {
449 it = specs.erase(it);
454 TVM_FFI_ICHECK(specs.size() == 1)
455 <<
"multiple possible sharding for axis: (" << ffi::GetRef<Expr>(axis.tensor) <<
", "
461 std::unordered_map<Axis, std::vector<AxisGraphEdge>, AxisHash> graph_;
462 std::unordered_map<Axis, AxisShardingSpec, AxisHash> src_axis_sharding_spec_;
463 std::unordered_map<Axis, AxisShardingSpec, AxisHash> cutpoint_axis_sharding_spec_;
465 Axis, std::unordered_map<AxisShardingSpec, int, AxisShardingSpecHash, AxisShardingSpecEqual>,
467 axis_sharding_specs_priority_;
470using FBuildAxisGraph = std::function<void(
const Var& output_var,
const Call& call,
471 distributed::AxisGroupGraph* axis_group_graph)>;
473void BuildAxisGraphUnary(
const Var& output_var,
const Call& call,
474 distributed::AxisGroupGraph* axis_group_graph);
475void BuildAxisGraphBinary(
const Var& output_var,
const Call& call,
476 distributed::AxisGroupGraph* axis_group_graph);
477void BuildAxisGraphReduce(
const Var& output_var,
const Call& call,
478 distributed::AxisGroupGraph* axis_group_graph);
479void BuildAxisGraphMatmul(
const Var& output_var,
const Call& call,
480 distributed::AxisGroupGraph* axis_group_graph);
481void BuildAxisGraphPermuteDims(
const Var& output_var,
const Call& call,
482 distributed::AxisGroupGraph* axis_group_graph);
483void BuildAxisGraphReshape(
const Var& output_var,
const Call& call,
484 distributed::AxisGroupGraph* axis_group_graph);
486 distributed::AxisGroupGraph* axis_group_graph);