cose brutte
Validate Operations / validate-operations (push) Has been cancelled

This commit is contained in:
NiccoloN
2026-07-07 12:54:34 +02:00
parent 47f6715296
commit 8338caf3f3
4 changed files with 2482 additions and 2170 deletions
@@ -3,86 +3,255 @@
#include "mlir/IR/Diagnostics.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/ADT/STLExtras.h"
#include <algorithm>
using namespace mlir;
namespace onnx_mlir::spatial {
namespace {
struct GroupSignature {
CommunicationPhase phase = CommunicationPhase::Local;
ClassId sourceClass = 0;
ClassId targetClass = 0;
Type payloadType;
bool sourceIsBatch = false;
bool targetIsBatch = false;
bool operator==(const GroupSignature& other) const {
return phase == other.phase && sourceClass == other.sourceClass && targetClass == other.targetClass
&& payloadType == other.payloadType && sourceIsBatch == other.sourceIsBatch
&& targetIsBatch == other.targetIsBatch;
StringRef stringifyExchangeKind(CommunicationExchangeKind kind) {
switch (kind) {
case CommunicationExchangeKind::HostForward: return "HostForward";
case CommunicationExchangeKind::OrdinaryValue: return "OrdinaryValue";
case CommunicationExchangeKind::PackedScalarRun: return "PackedScalarRun";
case CommunicationExchangeKind::IndexedBatchRun: return "IndexedBatchRun";
case CommunicationExchangeKind::ProjectedInput: return "ProjectedInput";
}
};
llvm_unreachable("unknown communication exchange kind");
}
struct GroupSignatureInfo {
static GroupSignature getEmptyKey() {
return {CommunicationPhase::Local,
llvm::DenseMapInfo<ClassId>::getEmptyKey(),
llvm::DenseMapInfo<ClassId>::getEmptyKey(),
llvm::DenseMapInfo<Type>::getEmptyKey(),
false,
false};
unsigned getExchangeKindOrder(CommunicationExchangeKind kind) {
switch (kind) {
case CommunicationExchangeKind::HostForward: return 0;
case CommunicationExchangeKind::OrdinaryValue: return 1;
case CommunicationExchangeKind::PackedScalarRun: return 2;
case CommunicationExchangeKind::IndexedBatchRun: return 3;
case CommunicationExchangeKind::ProjectedInput: return 4;
}
static GroupSignature getTombstoneKey() {
return {CommunicationPhase::Local,
llvm::DenseMapInfo<ClassId>::getTombstoneKey(),
llvm::DenseMapInfo<ClassId>::getTombstoneKey(),
llvm::DenseMapInfo<Type>::getTombstoneKey(),
false,
false};
}
static unsigned getHashValue(const GroupSignature& key) {
return llvm::hash_combine(static_cast<unsigned>(key.phase),
key.sourceClass,
key.targetClass,
key.payloadType,
key.sourceIsBatch,
key.targetIsBatch);
}
static bool isEqual(const GroupSignature& lhs, const GroupSignature& rhs) { return lhs == rhs; }
};
llvm_unreachable("unknown communication exchange kind");
}
void emitPlanError(Operation* anchor, StringRef message) {
if (anchor)
anchor->emitError(message);
}
struct PeerPair {
int64_t minCore = 0;
int64_t maxCore = 0;
bool operator==(const PeerPair& other) const {
return minCore == other.minCore && maxCore == other.maxCore;
}
};
struct PeerPairInfo {
static PeerPair getEmptyKey() {
return {llvm::DenseMapInfo<int64_t>::getEmptyKey(), llvm::DenseMapInfo<int64_t>::getEmptyKey()};
}
static PeerPair getTombstoneKey() {
return {llvm::DenseMapInfo<int64_t>::getTombstoneKey(), llvm::DenseMapInfo<int64_t>::getTombstoneKey()};
}
static unsigned getHashValue(PeerPair key) { return llvm::hash_combine(key.minCore, key.maxCore); }
static bool isEqual(PeerPair lhs, PeerPair rhs) { return lhs == rhs; }
};
PeerPair getPeerPair(const CommunicationExchange& exchange) {
return {std::min(exchange.sourceCore, exchange.targetCore), std::max(exchange.sourceCore, exchange.targetCore)};
}
bool exchangeOrderLess(const CommunicationExchange& lhs, const CommunicationExchange& rhs) {
if (getExchangeKindOrder(lhs.kind) != getExchangeKindOrder(rhs.kind))
return getExchangeKindOrder(lhs.kind) < getExchangeKindOrder(rhs.kind);
if (lhs.sourceClass != rhs.sourceClass)
return lhs.sourceClass < rhs.sourceClass;
if (lhs.targetClass != rhs.targetClass)
return lhs.targetClass < rhs.targetClass;
if (lhs.producerOrder != rhs.producerOrder)
return lhs.producerOrder < rhs.producerOrder;
if (lhs.producer.resultIndex != rhs.producer.resultIndex)
return lhs.producer.resultIndex < rhs.producer.resultIndex;
if (lhs.producer.instance.laneStart != rhs.producer.instance.laneStart)
return lhs.producer.instance.laneStart < rhs.producer.instance.laneStart;
if (lhs.producer.instance.laneCount != rhs.producer.instance.laneCount)
return lhs.producer.instance.laneCount < rhs.producer.instance.laneCount;
if (lhs.sourceLane != rhs.sourceLane)
return lhs.sourceLane < rhs.sourceLane;
if (lhs.targetLane != rhs.targetLane)
return lhs.targetLane < rhs.targetLane;
if (lhs.ordinal != rhs.ordinal)
return lhs.ordinal < rhs.ordinal;
return lhs.channelId < rhs.channelId;
}
void sortExchangeIds(ArrayRef<CommunicationExchange> exchanges,
SmallVectorImpl<CommunicationExchangeId>& ids) {
llvm::stable_sort(ids, [&](CommunicationExchangeId lhs, CommunicationExchangeId rhs) {
return exchangeOrderLess(exchanges[lhs.value], exchanges[rhs.value]);
});
}
void addEvent(CommunicationPlan& plan,
DenseMap<ClassId, SmallVector<CommunicationEvent, 16>>& eventsByClass,
CommunicationExchangeId id,
CommunicationEventKind kind,
ClassId classId,
int64_t core,
unsigned& order) {
eventsByClass[classId].push_back(CommunicationEvent {id, kind, classId, core, order++});
(void)plan;
}
void buildEventStreams(CommunicationPlan& plan,
SmallVectorImpl<CommunicationExchange>& exchanges,
DenseMap<ClassId, SmallVector<CommunicationEvent, 16>>& eventsByClass) {
DenseMap<PeerPair, SmallVector<CommunicationExchangeId, 8>, PeerPairInfo> exchangesByPair;
for (const CommunicationExchange& exchange : exchanges)
exchangesByPair[getPeerPair(exchange)].push_back(exchange.id);
SmallVector<PeerPair, 8> pairs;
pairs.reserve(exchangesByPair.size());
for (const auto& entry : exchangesByPair)
pairs.push_back(entry.first);
llvm::sort(pairs, [](PeerPair lhs, PeerPair rhs) {
if (lhs.minCore != rhs.minCore)
return lhs.minCore < rhs.minCore;
return lhs.maxCore < rhs.maxCore;
});
unsigned order = 0;
for (PeerPair pair : pairs) {
SmallVector<CommunicationExchangeId, 8> hiToLo;
SmallVector<CommunicationExchangeId, 8> loToHi;
for (CommunicationExchangeId id : exchangesByPair.lookup(pair)) {
const CommunicationExchange& exchange = exchanges[id.value];
if (exchange.sourceCore == pair.maxCore && exchange.targetCore == pair.minCore)
hiToLo.push_back(id);
else
loToHi.push_back(id);
}
sortExchangeIds(exchanges, hiToLo);
sortExchangeIds(exchanges, loToHi);
for (CommunicationExchangeId id : hiToLo) {
const CommunicationExchange& exchange = exchanges[id.value];
addEvent(plan, eventsByClass, id, CommunicationEventKind::Receive, exchange.targetClass, exchange.targetCore, order);
}
for (CommunicationExchangeId id : hiToLo) {
const CommunicationExchange& exchange = exchanges[id.value];
addEvent(plan, eventsByClass, id, CommunicationEventKind::Send, exchange.sourceClass, exchange.sourceCore, order);
}
for (CommunicationExchangeId id : loToHi) {
const CommunicationExchange& exchange = exchanges[id.value];
addEvent(plan, eventsByClass, id, CommunicationEventKind::Send, exchange.sourceClass, exchange.sourceCore, order);
}
for (CommunicationExchangeId id : loToHi) {
const CommunicationExchange& exchange = exchanges[id.value];
addEvent(plan, eventsByClass, id, CommunicationEventKind::Receive, exchange.targetClass, exchange.targetCore, order);
}
}
}
} // namespace
FailureOr<CommunicationPlan>
CommunicationPlan::buildProjectedInputPlan(Operation* anchor,
ArrayRef<CommunicationClassInfo> classes,
const DenseMap<CpuId, ClassId>& cpuToClass,
const ProjectedInputPlanMap& projectedPlans) {
CommunicationPlan::build(Operation* anchor,
ArrayRef<CommunicationClassInfo> classes,
const DenseMap<CpuId, ClassId>& cpuToClass,
const ProducerSourceClassMap& producerSourceClasses,
const DenseMap<ProducerKey, Type, ProducerKeyInfo>& producerPayloadTypes,
const DenseMap<ProducerKey, SmallVector<ClassId, 4>, ProducerKeyInfo>& producerDestClasses,
const DenseMap<ProducerKey, SmallVector<ClassId, 4>, ProducerKeyInfo>& ordinaryProducerDestClasses,
ProjectedInputPlanMap& projectedPlans,
const DenseMap<Value, ClassId>& hostOutputOwners,
const DenseMap<Value, bool>& liveExternalUseCache,
int64_t& nextChannelId) {
CommunicationPlan plan;
DenseMap<ClassId, CommunicationClassInfo> classById;
for (CommunicationClassInfo klass : classes)
classById[klass.classId] = klass;
classById[klass.classId] = std::move(klass);
DenseSet<int64_t> remoteChannels;
DenseSet<const ProjectedInputTransferFragment*> seenFragments;
DenseMap<GroupSignature, unsigned, GroupSignatureInfo> groupBySignature;
unsigned nextPlaceholder = 0;
auto appendExchange = [&](CommunicationExchangeKind kind,
ProducerKey producer,
ClassId sourceClass,
ClassId targetClass,
Type payloadType,
int64_t sourceCore,
int64_t targetCore,
unsigned sourceLane,
unsigned targetLane,
unsigned ordinal,
Value originalHostOutput = Value(),
const ProjectedInputTransferFragment* fragment = nullptr) -> FailureOr<CommunicationExchangeId> {
if (sourceCore == targetCore)
return failure();
for (const auto& extractEntry : projectedPlans) {
for (const auto& classEntry : extractEntry.second) {
ClassId targetClass = classEntry.first;
const ProjectedInputTransferPlan& inputPlan = classEntry.second;
auto sourceInfoIt = classById.find(sourceClass);
auto targetInfoIt = classById.find(targetClass);
if (sourceInfoIt == classById.end() || targetInfoIt == classById.end())
return failure();
CommunicationExchangeId exchangeId {plan.exchanges.size()};
plan.exchanges.push_back(CommunicationExchange {
exchangeId,
kind,
PlaceholderId {static_cast<unsigned>(plan.demands.size())},
producer,
sourceClass,
targetClass,
sourceInfoIt->second.rank,
targetInfoIt->second.rank,
payloadType,
nextChannelId++,
sourceCore,
targetCore,
sourceLane,
targetLane,
ordinal,
exchangeId.value,
originalHostOutput,
fragment,
});
return exchangeId;
};
auto getPayloadType = [&](ProducerKey producer) -> Type {
auto typeIt = producerPayloadTypes.find(producer);
if (typeIt != producerPayloadTypes.end())
return typeIt->second;
if (!producer.instance.op || producer.resultIndex >= producer.instance.op->getNumResults())
return Type();
return producer.instance.op->getResult(producer.resultIndex).getType();
};
for (const auto& entry : producerDestClasses) {
ProducerKey producer = entry.first;
auto sourceClassIt = producerSourceClasses.find(producer);
if (sourceClassIt == producerSourceClasses.end()) {
emitPlanError(anchor, "communication plan could not find producer source class");
return failure();
}
ClassId sourceClass = sourceClassIt->second;
auto sourceInfoIt = classById.find(sourceClass);
if (sourceInfoIt == classById.end()) {
emitPlanError(anchor, "communication plan references an unknown producer source class");
return failure();
}
const CommunicationClassInfo& sourceInfo = sourceInfoIt->second;
Type payloadType = getPayloadType(producer);
if (!payloadType) {
emitPlanError(anchor, "communication plan could not determine producer payload type");
return failure();
}
for (ClassId targetClass : entry.second) {
if (targetClass == sourceClass)
continue;
auto targetInfoIt = classById.find(targetClass);
if (targetInfoIt == classById.end()) {
@@ -91,7 +260,190 @@ CommunicationPlan::buildProjectedInputPlan(Operation* anchor,
}
const CommunicationClassInfo& targetInfo = targetInfoIt->second;
for (const ProjectedInputTransferFragment& fragment : inputPlan.fragments) {
CommunicationExchangeKind kind = CommunicationExchangeKind::OrdinaryValue;
if (sourceInfo.isBatch && !targetInfo.isBatch)
kind = CommunicationExchangeKind::PackedScalarRun;
else if (sourceInfo.isBatch && targetInfo.isBatch)
kind = CommunicationExchangeKind::IndexedBatchRun;
else if (!llvm::is_contained(ordinaryProducerDestClasses.lookup(producer), targetClass))
continue;
SmallVector<CommunicationExchangeId, 8>* bucket = nullptr;
ProducerTargetKey lookupKey {producer, targetClass};
if (kind == CommunicationExchangeKind::OrdinaryValue)
bucket = &plan.ordinaryExchangesByProducerTarget[lookupKey];
else if (kind == CommunicationExchangeKind::PackedScalarRun)
bucket = &plan.packedScalarRunExchangesByProducerTarget[lookupKey];
else if (kind == CommunicationExchangeKind::IndexedBatchRun)
bucket = &plan.indexedBatchRunExchangesByProducerTarget[lookupKey];
if (!bucket)
continue;
if (!sourceInfo.isBatch) {
if (sourceInfo.cpus.empty()) {
emitPlanError(anchor, "communication plan source class has no CPU");
return failure();
}
int64_t sourceCore = static_cast<int64_t>(sourceInfo.cpus.front());
unsigned ordinal = 0;
for (auto [targetLane, targetCpu] : llvm::enumerate(targetInfo.cpus)) {
auto exchangeId = appendExchange(kind,
producer,
sourceClass,
targetClass,
payloadType,
sourceCore,
static_cast<int64_t>(targetCpu),
0,
static_cast<unsigned>(targetLane),
ordinal++);
if (failed(exchangeId)) {
emitPlanError(anchor, "communication plan failed to append scalar source exchange");
return failure();
}
bucket->push_back(*exchangeId);
if (!targetInfo.isBatch)
break;
}
continue;
}
if (sourceInfo.cpus.empty()) {
emitPlanError(anchor, "communication plan batch source class has no CPUs");
return failure();
}
if (targetInfo.isBatch && sourceInfo.cpus.size() != targetInfo.cpus.size()) {
emitPlanError(anchor, "communication plan cannot map batch classes of different sizes");
return failure();
}
for (auto [sourceLane, sourceCpu] : llvm::enumerate(sourceInfo.cpus)) {
CpuId targetCpu = targetInfo.isBatch ? targetInfo.cpus[sourceLane] : targetInfo.cpus.front();
auto exchangeId = appendExchange(kind,
producer,
sourceClass,
targetClass,
payloadType,
static_cast<int64_t>(sourceCpu),
static_cast<int64_t>(targetCpu),
static_cast<unsigned>(sourceLane),
targetInfo.isBatch ? static_cast<unsigned>(sourceLane) : 0,
static_cast<unsigned>(bucket->size()));
if (failed(exchangeId)) {
emitPlanError(anchor, "communication plan failed to append batch source exchange");
return failure();
}
bucket->push_back(*exchangeId);
}
}
}
for (const auto& entry : producerSourceClasses) {
ProducerKey producer = entry.first;
ClassId sourceClass = entry.second;
if (!producer.instance.op || producer.resultIndex >= producer.instance.op->getNumResults())
continue;
Value originalOutput = producer.instance.op->getResult(producer.resultIndex);
auto liveIt = liveExternalUseCache.find(originalOutput);
if (liveIt == liveExternalUseCache.end() || !liveIt->second)
continue;
auto ownerIt = hostOutputOwners.find(originalOutput);
if (ownerIt == hostOutputOwners.end())
continue;
ClassId ownerClass = ownerIt->second;
if (ownerClass == sourceClass)
continue;
auto sourceInfoIt = classById.find(sourceClass);
auto ownerInfoIt = classById.find(ownerClass);
if (sourceInfoIt == classById.end() || ownerInfoIt == classById.end()) {
emitPlanError(anchor, "communication plan references an unknown host-forward class");
return failure();
}
const CommunicationClassInfo& sourceInfo = sourceInfoIt->second;
const CommunicationClassInfo& ownerInfo = ownerInfoIt->second;
if (sourceInfo.isBatch != ownerInfo.isBatch) {
emitPlanError(anchor, "communication plan does not support mixed scalar/batch host forwarding");
return failure();
}
if (sourceInfo.isBatch && sourceInfo.cpus.size() != ownerInfo.cpus.size()) {
emitPlanError(anchor, "communication plan cannot host-forward between batch classes of different sizes");
return failure();
}
Type payloadType = getPayloadType(producer);
if (!payloadType) {
emitPlanError(anchor, "communication plan could not determine host-forward payload type");
return failure();
}
HostForwardKey lookupKey {originalOutput, sourceClass, ownerClass};
SmallVector<CommunicationExchangeId, 8>& bucket = plan.hostForwardExchangesByOutput[lookupKey];
if (!sourceInfo.isBatch) {
if (!bucket.empty())
continue;
auto exchangeId = appendExchange(CommunicationExchangeKind::HostForward,
producer,
sourceClass,
ownerClass,
payloadType,
static_cast<int64_t>(sourceInfo.cpus.front()),
static_cast<int64_t>(ownerInfo.cpus.front()),
0,
0,
0,
originalOutput);
if (failed(exchangeId)) {
emitPlanError(anchor, "communication plan failed to append scalar host-forward exchange");
return failure();
}
bucket.push_back(*exchangeId);
continue;
}
if (bucket.size() >= sourceInfo.cpus.size())
continue;
unsigned ordinal = static_cast<unsigned>(bucket.size());
if (producer.instance.laneStart >= sourceInfo.cpus.size()) {
emitPlanError(anchor, "communication plan host-forward producer lane is out of range");
return failure();
}
unsigned lane = producer.instance.laneStart;
auto exchangeId = appendExchange(CommunicationExchangeKind::HostForward,
producer,
sourceClass,
ownerClass,
payloadType,
static_cast<int64_t>(sourceInfo.cpus[lane]),
static_cast<int64_t>(ownerInfo.cpus[lane]),
lane,
lane,
ordinal,
originalOutput);
if (failed(exchangeId)) {
emitPlanError(anchor, "communication plan failed to append batch host-forward exchange");
return failure();
}
bucket.push_back(*exchangeId);
}
DenseSet<const ProjectedInputTransferFragment*> seenFragments;
for (auto& extractEntry : projectedPlans) {
for (auto& classEntry : extractEntry.second) {
ClassId targetClass = classEntry.first;
ProjectedInputTransferPlan& inputPlan = classEntry.second;
auto targetInfoIt = classById.find(targetClass);
if (targetInfoIt == classById.end()) {
emitPlanError(anchor, "communication plan references an unknown target class");
return failure();
}
const CommunicationClassInfo& targetInfo = targetInfoIt->second;
for (ProjectedInputTransferFragment& fragment : inputPlan.fragments) {
if (!seenFragments.insert(&fragment).second) {
emitPlanError(anchor, "communication plan found the same projected fragment twice");
return failure();
@@ -111,129 +463,193 @@ CommunicationPlan::buildProjectedInputPlan(Operation* anchor,
}
const CommunicationClassInfo& sourceInfo = sourceInfoIt->second;
PlaceholderId placeholder {nextPlaceholder++};
plan.demands.push_back(InputDemand {placeholder, fragment.producer, targetClass, inputPlan.layout.fragmentType});
plan.publications.push_back(
ProducerPublication {fragment.producer, sourceClass, inputPlan.layout.fragmentType});
plan.demands.push_back(InputDemand {{static_cast<unsigned>(plan.demands.size())},
fragment.producer,
targetClass,
inputPlan.layout.fragmentType});
plan.publications.push_back(ProducerPublication {fragment.producer, sourceClass, inputPlan.layout.fragmentType});
if (fragment.sourceCoreId == fragment.targetCoreId) {
plan.phaseByFragment[&fragment] = CommunicationPhase::Local;
if (fragment.sourceCoreId == fragment.targetCoreId)
continue;
}
if (sourceInfo.rank == targetInfo.rank && sourceClass != targetClass) {
emitPlanError(anchor, "communication plan found a same-rank inter-class projected exchange");
return failure();
}
if (!remoteChannels.insert(fragment.channelId).second) {
emitPlanError(anchor, "communication plan found a duplicate remote projected channel id");
return failure();
}
TransferKind kind = sourceClass == targetClass ? TransferKind::SameClassForward : TransferKind::RemoteChannel;
CommunicationPhase phase =
sourceInfo.rank == targetInfo.rank
? (fragment.sourceCoreId < fragment.targetCoreId ? CommunicationPhase::LowToHigh
: CommunicationPhase::HighToLow)
: (sourceInfo.rank < targetInfo.rank ? CommunicationPhase::LowToHigh : CommunicationPhase::HighToLow);
plan.phaseByFragment[&fragment] = phase;
size_t exchangeId = plan.exchanges.size();
plan.exchangeByFragment[&fragment] = exchangeId;
plan.exchanges.push_back(ExchangeDescriptor {
exchangeId,
kind,
placeholder,
FailureOr<CommunicationExchangeId> exchangeId = appendExchange(
CommunicationExchangeKind::ProjectedInput,
fragment.producer,
sourceClass,
targetClass,
sourceInfo.rank,
targetInfo.rank,
inputPlan.layout.fragmentType,
fragment.channelId,
fragment.sourceCoreId,
fragment.targetCoreId,
0,
fragment.targetLane,
fragment.ordinal,
phase,
&fragment,
});
GroupSignature signature {
phase,
sourceClass,
targetClass,
inputPlan.layout.fragmentType,
sourceInfo.isBatch,
targetInfo.isBatch,
};
auto groupIt = groupBySignature.find(signature);
if (groupIt == groupBySignature.end()) {
unsigned groupIndex = static_cast<unsigned>(plan.batchGroups.size());
groupBySignature[signature] = groupIndex;
plan.batchGroups.push_back(CommunicationBatchGroup {
phase,
sourceClass,
targetClass,
inputPlan.layout.fragmentType,
sourceInfo.isBatch,
targetInfo.isBatch,
{},
});
groupIt = groupBySignature.find(signature);
Value(),
&fragment);
if (failed(exchangeId)) {
emitPlanError(anchor, "communication plan failed to append projected input exchange");
return failure();
}
plan.batchGroups[groupIt->second].exchanges.push_back(exchangeId);
fragment.channelId = plan.exchanges[exchangeId->value].channelId;
plan.projectedExchangesByFragment[&fragment].push_back(*exchangeId);
}
}
}
for (const ExchangeDescriptor& exchange : plan.exchanges) {
if (!exchange.payloadType) {
emitPlanError(anchor, "communication plan has an exchange without payload type");
return failure();
}
if (exchange.phase == CommunicationPhase::Local) {
emitPlanError(anchor, "communication plan classified a remote exchange as local");
return failure();
}
if (exchange.kind == TransferKind::SameClassForward && exchange.sourceClass != exchange.targetClass) {
emitPlanError(anchor, "communication plan same-class exchange crosses class ids");
return failure();
}
if (exchange.kind == TransferKind::RemoteChannel && exchange.sourceRank == exchange.targetRank) {
emitPlanError(anchor, "communication plan remote exchange has equal source and target rank");
return failure();
}
if (exchange.sourceRank < exchange.targetRank && exchange.phase != CommunicationPhase::LowToHigh) {
emitPlanError(anchor, "communication plan phase does not match low-to-high rank direction");
return failure();
}
if (exchange.sourceRank > exchange.targetRank && exchange.phase != CommunicationPhase::HighToLow) {
emitPlanError(anchor, "communication plan phase does not match high-to-low rank direction");
return failure();
}
if (!exchange.fragment) {
emitPlanError(anchor, "communication plan exchange is not bound to a projected fragment");
return failure();
}
}
buildEventStreams(plan, plan.exchanges, plan.eventsByClass);
if (failed(plan.verify(anchor)))
return failure();
return plan;
}
std::optional<CommunicationPhase>
CommunicationPlan::getPhase(const ProjectedInputTransferFragment& fragment) const {
auto it = phaseByFragment.find(&fragment);
if (it == phaseByFragment.end())
return std::nullopt;
LogicalResult CommunicationPlan::verify(Operation* anchor) const {
DenseSet<int64_t> channelIds;
SmallVector<const CommunicationExchange*, 8> malformed;
size_t malformedCount = 0;
auto recordMalformed = [&](const CommunicationExchange& exchange) {
++malformedCount;
if (malformed.size() < 8)
malformed.push_back(&exchange);
};
for (auto [index, exchange] : llvm::enumerate(exchanges)) {
bool bad = false;
if (exchange.id.value != index)
bad = true;
if (exchange.sourceCore == exchange.targetCore)
bad = true;
if (!exchange.payloadType)
bad = true;
if (!channelIds.insert(exchange.channelId).second)
bad = true;
if (bad)
recordMalformed(exchange);
}
DenseMap<size_t, unsigned> sendCounts;
DenseMap<size_t, unsigned> receiveCounts;
for (const auto& classEvents : eventsByClass) {
std::optional<unsigned> previousOrder;
DenseSet<unsigned> classOrders;
for (const CommunicationEvent& event : classEvents.second) {
if (event.exchangeId.value >= exchanges.size()) {
if (anchor)
anchor->emitError("communication plan event references an invalid exchange id");
return failure();
}
const CommunicationExchange& exchange = exchanges[event.exchangeId.value];
if (previousOrder && event.order < *previousOrder) {
if (anchor)
anchor->emitError("communication plan event stream is not sorted");
return failure();
}
previousOrder = event.order;
if (!classOrders.insert(event.order).second) {
if (anchor)
anchor->emitError("communication plan event stream contains duplicate event order");
return failure();
}
if (event.kind == CommunicationEventKind::Send) {
++sendCounts[event.exchangeId.value];
if (event.classId != exchange.sourceClass || event.core != exchange.sourceCore)
recordMalformed(exchange);
}
else {
++receiveCounts[event.exchangeId.value];
if (event.classId != exchange.targetClass || event.core != exchange.targetCore)
recordMalformed(exchange);
}
}
}
for (const CommunicationExchange& exchange : exchanges) {
if (sendCounts[exchange.id.value] != 1 || receiveCounts[exchange.id.value] != 1)
recordMalformed(exchange);
}
auto verifyLookup = [&](ArrayRef<CommunicationExchangeId> ids) -> LogicalResult {
for (CommunicationExchangeId id : ids)
if (id.value >= exchanges.size())
return failure();
return success();
};
for (const auto& entry : projectedExchangesByFragment)
if (failed(verifyLookup(entry.second))) {
if (anchor)
anchor->emitError("communication plan projected lookup references an invalid exchange id");
return failure();
}
if (malformedCount != 0) {
if (anchor) {
InFlightDiagnostic diag = anchor->emitError("communication plan contains malformed exchanges");
for (const CommunicationExchange* exchange : malformed) {
diag << " [exchange=" << exchange->id.value << " kind=" << stringifyExchangeKind(exchange->kind)
<< " sourceClass=" << exchange->sourceClass << " sourceCore=" << exchange->sourceCore
<< " targetClass=" << exchange->targetClass << " targetCore=" << exchange->targetCore
<< " channelId=" << exchange->channelId << " payloadType=" << exchange->payloadType << "]";
}
if (malformedCount > malformed.size())
diag << " additionalMalformed=" << (malformedCount - malformed.size());
}
return failure();
}
return success();
}
ArrayRef<CommunicationEvent> CommunicationPlan::getEventsForClass(ClassId classId) const {
auto it = eventsByClass.find(classId);
if (it == eventsByClass.end())
return {};
return it->second;
}
std::optional<size_t> CommunicationPlan::getExchangeId(const ProjectedInputTransferFragment& fragment) const {
auto it = exchangeByFragment.find(&fragment);
if (it == exchangeByFragment.end())
return std::nullopt;
ArrayRef<CommunicationExchangeId>
CommunicationPlan::getOrdinaryValueExchanges(ProducerKey producer, ClassId targetClass) const {
auto it = ordinaryExchangesByProducerTarget.find({producer, targetClass});
if (it == ordinaryExchangesByProducerTarget.end())
return {};
return it->second;
}
ArrayRef<CommunicationExchangeId>
CommunicationPlan::getProjectedInputExchanges(const ProjectedInputTransferFragment& fragment) const {
auto it = projectedExchangesByFragment.find(&fragment);
if (it == projectedExchangesByFragment.end())
return {};
return it->second;
}
ArrayRef<CommunicationExchangeId>
CommunicationPlan::getPackedScalarRunExchanges(ProducerKey producer, ClassId targetClass) const {
auto it = packedScalarRunExchangesByProducerTarget.find({producer, targetClass});
if (it == packedScalarRunExchangesByProducerTarget.end())
return {};
return it->second;
}
ArrayRef<CommunicationExchangeId>
CommunicationPlan::getIndexedBatchRunExchanges(ProducerKey producer, ClassId targetClass) const {
auto it = indexedBatchRunExchangesByProducerTarget.find({producer, targetClass});
if (it == indexedBatchRunExchangesByProducerTarget.end())
return {};
return it->second;
}
ArrayRef<CommunicationExchangeId>
CommunicationPlan::getHostForwardExchanges(Value originalOutput, ClassId sourceClass, ClassId ownerClass) const {
auto it = hostForwardExchangesByOutput.find({originalOutput, sourceClass, ownerClass});
if (it == hostForwardExchangesByOutput.end())
return {};
return it->second;
}
@@ -5,6 +5,7 @@
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include <cstddef>
@@ -24,10 +25,31 @@ enum class TransferKind {
WholeTensorBarrier
};
enum class CommunicationPhase {
Local,
LowToHigh,
HighToLow
enum class CommunicationExchangeKind {
OrdinaryValue,
ProjectedInput,
PackedScalarRun,
IndexedBatchRun,
HostForward
};
enum class CommunicationEventKind {
Send,
Receive
};
struct CommunicationExchangeId {
size_t value = 0;
bool operator==(const CommunicationExchangeId& other) const { return value == other.value; }
operator size_t() const { return value; }
};
struct CommunicationExchangeIdInfo {
static CommunicationExchangeId getEmptyKey() { return {llvm::DenseMapInfo<size_t>::getEmptyKey()}; }
static CommunicationExchangeId getTombstoneKey() { return {llvm::DenseMapInfo<size_t>::getTombstoneKey()}; }
static unsigned getHashValue(CommunicationExchangeId id) { return llvm::hash_value(id.value); }
static bool isEqual(CommunicationExchangeId lhs, CommunicationExchangeId rhs) { return lhs == rhs; }
};
struct PlaceholderId {
@@ -61,61 +83,147 @@ struct CommunicationClassInfo {
size_t rank = 0;
bool isBatch = false;
mlir::Operation* op = nullptr;
llvm::SmallVector<CpuId, 8> cpus;
};
struct ExchangeDescriptor {
size_t id = 0;
TransferKind kind = TransferKind::RemoteChannel;
struct CommunicationExchange {
CommunicationExchangeId id;
CommunicationExchangeKind kind = CommunicationExchangeKind::OrdinaryValue;
PlaceholderId placeholder;
ProducerKey producer;
ClassId sourceClass = 0;
ClassId targetClass = 0;
size_t sourceRank = 0;
size_t targetRank = 0;
mlir::Type payloadType;
int64_t channelId = 0;
int64_t sourceCore = 0;
int64_t targetCore = 0;
unsigned sourceLane = 0;
unsigned targetLane = 0;
unsigned ordinal = 0;
CommunicationPhase phase = CommunicationPhase::Local;
uint64_t producerOrder = 0;
mlir::Value originalHostOutput;
const ProjectedInputTransferFragment* fragment = nullptr;
};
struct CommunicationBatchGroup {
CommunicationPhase phase = CommunicationPhase::Local;
ClassId sourceClass = 0;
ClassId targetClass = 0;
mlir::Type payloadType;
bool sourceIsBatch = false;
bool targetIsBatch = false;
llvm::SmallVector<size_t, 8> exchanges;
using ExchangeDescriptor = CommunicationExchange;
struct CommunicationEvent {
CommunicationExchangeId exchangeId;
CommunicationEventKind kind = CommunicationEventKind::Send;
ClassId classId = 0;
int64_t core = 0;
unsigned order = 0;
};
class CommunicationPlan {
public:
using ProjectedInputPlanMap =
llvm::DenseMap<mlir::Operation*, llvm::DenseMap<ClassId, ProjectedInputTransferPlan>>;
using ProducerSourceClassMap = llvm::DenseMap<ProducerKey, ClassId, ProducerKeyInfo>;
static mlir::FailureOr<CommunicationPlan>
buildProjectedInputPlan(mlir::Operation* anchor,
llvm::ArrayRef<CommunicationClassInfo> classes,
const llvm::DenseMap<CpuId, ClassId>& cpuToClass,
const ProjectedInputPlanMap& projectedPlans);
build(mlir::Operation* anchor,
llvm::ArrayRef<CommunicationClassInfo> classes,
const llvm::DenseMap<CpuId, ClassId>& cpuToClass,
const ProducerSourceClassMap& producerSourceClasses,
const llvm::DenseMap<ProducerKey, mlir::Type, ProducerKeyInfo>& producerPayloadTypes,
const llvm::DenseMap<ProducerKey, llvm::SmallVector<ClassId, 4>, ProducerKeyInfo>& producerDestClasses,
const llvm::DenseMap<ProducerKey, llvm::SmallVector<ClassId, 4>, ProducerKeyInfo>& ordinaryProducerDestClasses,
ProjectedInputPlanMap& projectedPlans,
const llvm::DenseMap<mlir::Value, ClassId>& hostOutputOwners,
const llvm::DenseMap<mlir::Value, bool>& liveExternalUseCache,
int64_t& nextChannelId);
std::optional<CommunicationPhase> getPhase(const ProjectedInputTransferFragment& fragment) const;
std::optional<size_t> getExchangeId(const ProjectedInputTransferFragment& fragment) const;
const ExchangeDescriptor& getExchange(size_t exchangeId) const { return exchanges[exchangeId]; }
llvm::ArrayRef<ExchangeDescriptor> getExchanges() const { return exchanges; }
llvm::ArrayRef<CommunicationBatchGroup> getBatchGroups() const { return batchGroups; }
mlir::LogicalResult verify(mlir::Operation* anchor) const;
llvm::ArrayRef<CommunicationEvent> getEventsForClass(ClassId classId) const;
llvm::ArrayRef<CommunicationExchange> getExchanges() const { return exchanges; }
const CommunicationExchange& getExchange(CommunicationExchangeId id) const { return exchanges[id.value]; }
const CommunicationExchange& getExchange(size_t id) const { return exchanges[id]; }
llvm::ArrayRef<CommunicationExchangeId>
getOrdinaryValueExchanges(ProducerKey producer, ClassId targetClass) const;
llvm::ArrayRef<CommunicationExchangeId>
getProjectedInputExchanges(const ProjectedInputTransferFragment& fragment) const;
llvm::ArrayRef<CommunicationExchangeId>
getPackedScalarRunExchanges(ProducerKey producer, ClassId targetClass) const;
llvm::ArrayRef<CommunicationExchangeId>
getIndexedBatchRunExchanges(ProducerKey producer, ClassId targetClass) const;
llvm::ArrayRef<CommunicationExchangeId>
getHostForwardExchanges(mlir::Value originalOutput, ClassId sourceClass, ClassId ownerClass) const;
private:
llvm::DenseMap<const ProjectedInputTransferFragment*, CommunicationPhase> phaseByFragment;
llvm::DenseMap<const ProjectedInputTransferFragment*, size_t> exchangeByFragment;
struct ProducerTargetKey {
ProducerKey producer;
ClassId targetClass = 0;
bool operator==(const ProducerTargetKey& other) const {
return producer == other.producer && targetClass == other.targetClass;
}
};
struct ProducerTargetKeyInfo {
static ProducerTargetKey getEmptyKey() {
return {ProducerKeyInfo::getEmptyKey(), llvm::DenseMapInfo<ClassId>::getEmptyKey()};
}
static ProducerTargetKey getTombstoneKey() {
return {ProducerKeyInfo::getTombstoneKey(), llvm::DenseMapInfo<ClassId>::getTombstoneKey()};
}
static unsigned getHashValue(const ProducerTargetKey& key) {
return llvm::hash_combine(ProducerKeyInfo::getHashValue(key.producer), key.targetClass);
}
static bool isEqual(const ProducerTargetKey& lhs, const ProducerTargetKey& rhs) { return lhs == rhs; }
};
struct HostForwardKey {
mlir::Value originalOutput;
ClassId sourceClass = 0;
ClassId ownerClass = 0;
bool operator==(const HostForwardKey& other) const {
return originalOutput == other.originalOutput && sourceClass == other.sourceClass
&& ownerClass == other.ownerClass;
}
};
struct HostForwardKeyInfo {
static HostForwardKey getEmptyKey() {
return {llvm::DenseMapInfo<mlir::Value>::getEmptyKey(),
llvm::DenseMapInfo<ClassId>::getEmptyKey(),
llvm::DenseMapInfo<ClassId>::getEmptyKey()};
}
static HostForwardKey getTombstoneKey() {
return {llvm::DenseMapInfo<mlir::Value>::getTombstoneKey(),
llvm::DenseMapInfo<ClassId>::getTombstoneKey(),
llvm::DenseMapInfo<ClassId>::getTombstoneKey()};
}
static unsigned getHashValue(const HostForwardKey& key) {
return llvm::hash_combine(key.originalOutput, key.sourceClass, key.ownerClass);
}
static bool isEqual(const HostForwardKey& lhs, const HostForwardKey& rhs) { return lhs == rhs; }
};
llvm::SmallVector<InputDemand, 16> demands;
llvm::SmallVector<ProducerPublication, 16> publications;
llvm::SmallVector<ExchangeDescriptor, 16> exchanges;
llvm::SmallVector<CommunicationBatchGroup, 8> batchGroups;
llvm::SmallVector<CommunicationExchange, 16> exchanges;
llvm::DenseMap<ClassId, llvm::SmallVector<CommunicationEvent, 16>> eventsByClass;
llvm::DenseMap<const ProjectedInputTransferFragment*, llvm::SmallVector<CommunicationExchangeId, 1>>
projectedExchangesByFragment;
llvm::DenseMap<ProducerTargetKey, llvm::SmallVector<CommunicationExchangeId, 8>, ProducerTargetKeyInfo>
ordinaryExchangesByProducerTarget;
llvm::DenseMap<ProducerTargetKey, llvm::SmallVector<CommunicationExchangeId, 8>, ProducerTargetKeyInfo>
packedScalarRunExchangesByProducerTarget;
llvm::DenseMap<ProducerTargetKey, llvm::SmallVector<CommunicationExchangeId, 8>, ProducerTargetKeyInfo>
indexedBatchRunExchangesByProducerTarget;
llvm::DenseMap<HostForwardKey, llvm::SmallVector<CommunicationExchangeId, 8>, HostForwardKeyInfo>
hostForwardExchangesByOutput;
};
} // namespace onnx_mlir::spatial
File diff suppressed because it is too large Load Diff
@@ -58,7 +58,7 @@ struct PackedScalarRunValue {
mlir::RankedTensorType fragmentType;
llvm::SmallVector<PackedScalarRunSlot, 8> slots;
MessageVector messages;
llvm::SmallVector<CommunicationExchangeId, 8> exchangeIds;
};
struct IndexedBatchRunValue {
@@ -68,7 +68,7 @@ struct IndexedBatchRunValue {
mlir::Value packed;
mlir::RankedTensorType fragmentType;
llvm::SmallVector<PackedScalarRunSlot, 8> slots;
MessageVector messages;
llvm::SmallVector<CommunicationExchangeId, 8> exchangeIds;
};
struct LogicalSlotRange {
@@ -90,12 +90,13 @@ struct OutputDestinationGroup {
struct BatchRunSendPlan {
size_t resultIndex = 0;
ClassId destinationClass = 0;
MessageVector messages;
llvm::SmallVector<CommunicationExchangeId, 8> exchangeIds;
};
enum class TensorDemandActionKind {
DestinationFanout,
SameClassIndexedFragment,
ProjectedInputPublication,
TerminalBlueprintPublication,
WholeTensorBarrier
};
@@ -122,44 +123,6 @@ struct CompactRunPlan {
llvm::SmallVector<RunOutputDemand, 4> outputs;
};
struct ScalarPeerEdgeKey {
int64_t sourceCore = 0;
int64_t targetCore = 0;
mlir::Type payloadType;
};
struct ScalarPeerReceiveKey {
int64_t sourceCore = 0;
int64_t targetCore = 0;
mlir::Type payloadType;
std::optional<int64_t> channelId;
};
struct PendingScalarSend {
ClassId sourceClass = 0;
int64_t sourceCore = 0;
int64_t targetCore = 0;
mlir::Type payloadType;
ScalarPeerEdgeKey waitForReceive;
mlir::Operation* payloadAnchor = nullptr;
mlir::Value payload;
MessageVector messages;
mlir::Location loc;
};
struct PendingProjectedScalarSend {
ClassId sourceClass = 0;
int64_t sourceCore = 0;
int64_t targetCore = 0;
mlir::Type payloadType;
ScalarPeerEdgeKey waitForReceive;
mlir::Operation* payloadAnchor = nullptr;
mlir::Value payload;
MessageVector messages;
ProjectedTransferDescriptor descriptor;
mlir::Location loc;
};
enum class BatchInputDemandKind {
LaneFragment,
ProjectedFragment,
@@ -171,11 +134,17 @@ struct BatchInputDemand {
std::optional<ProducerKey> wholeTensorProducer;
};
struct ProjectedExchangeEmissionState {
struct CommunicationExchangeEmissionState {
bool producerBound = false;
bool sendEmitted = false;
bool receiveEmitted = false;
mlir::Value producerPayload;
mlir::Value receiveValue;
mlir::Operation* producerAnchor = nullptr;
mlir::Operation* sendAnchor = nullptr;
mlir::Operation* receiveAnchor = nullptr;
std::optional<mlir::Location> loc;
};
@@ -220,6 +189,24 @@ public:
}
void recordIndexedBatchRun(IndexedBatchRunValue run) { indexedBatchRuns.push_back(std::move(run)); }
void recordDeferredExact(ProducerKey key, ClassId classId, llvm::ArrayRef<CommunicationExchangeId> exchangeIds) {
llvm::SmallVector<CommunicationExchangeId, 4>& bucket = exactExchangeIds[key][classId];
for (CommunicationExchangeId id : exchangeIds)
if (!llvm::is_contained(bucket, id))
bucket.push_back(id);
}
llvm::ArrayRef<CommunicationExchangeId> getDeferredExactExchangeIds(ProducerKey key, ClassId classId) const {
auto producerIt = exactExchangeIds.find(key);
if (producerIt == exactExchangeIds.end())
return {};
auto classIt = producerIt->second.find(classId);
if (classIt == producerIt->second.end())
return {};
return classIt->second;
}
bool hasDeferredExact(ProducerKey key, ClassId classId) const {
return !getDeferredExactExchangeIds(key, classId).empty();
}
std::optional<mlir::Value> lookupExact(ProducerKey key, ClassId classId) const;
std::optional<mlir::Value> lookup(MaterializerState& state, ProducerKey key, ClassId classId);
@@ -245,6 +232,10 @@ private:
std::optional<mlir::Value> lookupPackedRun(MaterializerState& state, ProducerKey key, ClassId classId);
llvm::DenseMap<ProducerKey, llvm::DenseMap<ClassId, mlir::Value>, ProducerKeyInfo> exactValues;
llvm::DenseMap<ProducerKey,
llvm::DenseMap<ClassId, llvm::SmallVector<CommunicationExchangeId, 4>>,
ProducerKeyInfo>
exactExchangeIds;
llvm::SmallVector<PackedScalarRunValue, 8> packedScalarRuns;
llvm::SmallVector<IndexedBatchRunValue, 8> indexedBatchRuns;
llvm::DenseMap<WholeBatchAssemblyLookupKey,
@@ -285,19 +276,13 @@ struct MaterializerState {
projectedExtractReplacements;
llvm::DenseMap<mlir::Operation*, llvm::DenseMap<ClassId, ProjectedInputTransferPlan>>
projectedInputTransferPlans;
CommunicationPlan projectedInputCommunicationPlan;
llvm::SmallVector<ProjectedExchangeEmissionState, 16> projectedExchangeStates;
CommunicationPhase currentProjectedCommunicationPhase = CommunicationPhase::LowToHigh;
llvm::SmallVector<size_t, 8> deferredProjectedExchanges;
CommunicationPlan communicationPlan;
llvm::SmallVector<CommunicationExchangeEmissionState, 16> communicationExchangeStates;
llvm::SmallVector<ProducerKey, 8> deferredProducerValues;
AvailableValueStore availableValues;
llvm::DenseMap<mlir::Value, mlir::Value> hostReplacements;
llvm::DenseMap<mlir::Value, ClassId> hostOutputOwners;
llvm::SmallVector<PendingProjectedHostOutputFragment, 32> pendingProjectedHostOutputFragments;
llvm::SmallVector<ScalarPeerEdgeKey, 16> plannedScalarPeerReceives;
llvm::SmallVector<ScalarPeerEdgeKey, 8> materializedScalarPeerReceives;
llvm::SmallVector<PendingScalarSend, 8> pendingScalarSends;
llvm::SmallVector<PendingProjectedScalarSend, 8> pendingProjectedScalarSends;
llvm::DenseSet<mlir::Operation*> oldComputeOps;
MaterializerState(mlir::func::FuncOp func, const MergeScheduleResult& schedule, int64_t& nextChannelId)