update results_comparison.csv
This commit is contained in:
@@ -149,7 +149,8 @@ static void testBatchedReceiveForwarding() {
|
||||
|
||||
static void testResidentWeightViewCost() {
|
||||
mlir::MLIRContext context(mlir::MLIRContext::Threading::DISABLED);
|
||||
context.loadDialect<SpatialDialect, mlir::tensor::TensorDialect>();
|
||||
context.loadDialect<SpatialDialect, mlir::tensor::TensorDialect,
|
||||
mlir::arith::ArithDialect, mlir::scf::SCFDialect>();
|
||||
mlir::Block operands;
|
||||
mlir::OwningOpRef<mlir::ModuleOp> owner =
|
||||
mlir::ModuleOp::create(mlir::UnknownLoc::get(&context));
|
||||
@@ -161,7 +162,7 @@ static void testResidentWeightViewCost() {
|
||||
auto input = operands.addArgument(inputType, loc);
|
||||
auto wideType = mlir::RankedTensorType::get({1, 256}, builder.getF32Type());
|
||||
auto wideInput = operands.addArgument(wideType, loc);
|
||||
auto makeCompute = [&](bool weightView, unsigned inputView) {
|
||||
auto makeCompute = [&](bool weightView, unsigned inputView, int64_t iterations = 0) {
|
||||
builder.setInsertionPointToEnd(owner->getBody());
|
||||
auto compute = SpatGraphCompute::create(builder, loc,
|
||||
mlir::TypeRange{inputType}, mlir::ValueRange{weight},
|
||||
@@ -186,14 +187,42 @@ static void testResidentWeightViewCost() {
|
||||
w = slice(w, weightType);
|
||||
if (inputView)
|
||||
x = slice(x, inputType, inputView == 2);
|
||||
auto result = SpatVMMOp::create(builder, loc, inputType, w, x);
|
||||
SpatYieldOp::create(builder, loc, mlir::ValueRange{result.getResult()});
|
||||
mlir::scf::ForOp loop;
|
||||
if (iterations) {
|
||||
auto lower = mlir::arith::ConstantIndexOp::create(builder, loc, 0);
|
||||
auto upper = mlir::arith::ConstantIndexOp::create(builder, loc, iterations);
|
||||
auto step = mlir::arith::ConstantIndexOp::create(builder, loc, 1);
|
||||
loop = mlir::scf::ForOp::create(builder, loc, lower, upper, step, mlir::ValueRange{x});
|
||||
builder.setInsertionPointToStart(loop.getBody());
|
||||
x = loop.getRegionIterArg(0);
|
||||
}
|
||||
mlir::Value result = SpatVMMOp::create(builder, loc, inputType, w, x);
|
||||
if (loop) {
|
||||
mlir::scf::YieldOp::create(builder, loc, mlir::ValueRange{result});
|
||||
builder.setInsertionPointAfter(loop);
|
||||
result = loop.getResult(0);
|
||||
}
|
||||
SpatYieldOp::create(builder, loc, mlir::ValueRange{result});
|
||||
return ComputeInstance{compute, 0, 1};
|
||||
};
|
||||
auto direct = makeCompute(false, false);
|
||||
auto weightView = makeCompute(true, false);
|
||||
auto inputView = makeCompute(false, true);
|
||||
auto stridedInput = makeCompute(false, 2);
|
||||
auto invariantLoop = makeCompute(false, false, 1000000000);
|
||||
assert(collectDistinctResidentWeights(invariantLoop.op, 0).size() == 1);
|
||||
Cost loopBodyCost = getComputeInstanceCost(invariantLoop, SchedulingTarget{}) / 1000000000;
|
||||
auto loop = *mlir::cast<SpatGraphCompute>(invariantLoop.op)
|
||||
.getBody().front().getOps<mlir::scf::ForOp>().begin();
|
||||
builder.setInsertionPoint(loop);
|
||||
auto lower = mlir::arith::ConstantIndexOp::create(builder, loc, std::numeric_limits<int64_t>::min());
|
||||
auto upper = mlir::arith::ConstantIndexOp::create(builder, loc, std::numeric_limits<int64_t>::max());
|
||||
auto step = mlir::arith::ConstantIndexOp::create(builder, loc, std::numeric_limits<int64_t>::max());
|
||||
loop.getLowerBoundMutable().assign(lower.getResult());
|
||||
loop.getUpperBoundMutable().assign(upper.getResult());
|
||||
loop.getStepMutable().assign(step.getResult());
|
||||
assert(getComputeInstanceCost(invariantLoop, SchedulingTarget{})
|
||||
== 3 * loopBodyCost);
|
||||
for (Cost readLatency : {1, 20}) {
|
||||
SchedulingTarget target;
|
||||
target.localMemoryReadLatencyCycles = readLatency;
|
||||
@@ -373,6 +402,53 @@ static void testScalarProjectedTransferCost() {
|
||||
}
|
||||
}
|
||||
|
||||
static void testStridedPublicationMemory() {
|
||||
using namespace mlir;
|
||||
MLIRContext context(MLIRContext::Threading::DISABLED);
|
||||
context.loadDialect<SpatialDialect, func::FuncDialect, tensor::TensorDialect>();
|
||||
auto module = ModuleOp::create(UnknownLoc::get(&context));
|
||||
OpBuilder builder(module.getBodyRegion());
|
||||
auto loc = module.getLoc();
|
||||
auto image = RankedTensorType::get({2, 16}, builder.getF32Type());
|
||||
auto fragment = RankedTensorType::get({2, 8}, builder.getF32Type());
|
||||
auto function = func::FuncOp::create(builder, loc, "strided_publication",
|
||||
builder.getFunctionType({image}, {}));
|
||||
builder.setInsertionPointToStart(function.addEntryBlock());
|
||||
auto producer = onnx_mlir::createEmptySpatGraphCompute(builder, loc,
|
||||
TypeRange{image}, ValueRange{}, ValueRange{function.getArgument(0)});
|
||||
builder.setInsertionPointToEnd(&producer.getBody().front());
|
||||
Value value = SpatReluOp::create(builder, loc, image, *producer.getInputArgument(0));
|
||||
SpatYieldOp::create(builder, loc, ValueRange{value});
|
||||
builder.setInsertionPointAfter(producer);
|
||||
for (unsigned reader = 0; reader < 2; ++reader) {
|
||||
auto consumer = onnx_mlir::createEmptySpatGraphCompute(builder, loc,
|
||||
TypeRange{fragment}, ValueRange{}, ValueRange{producer.getResult(0)});
|
||||
builder.setInsertionPointToEnd(&consumer.getBody().front());
|
||||
value = tensor::ExtractSliceOp::create(builder, loc, fragment,
|
||||
*consumer.getInputArgument(0),
|
||||
SmallVector<OpFoldResult>(2, builder.getIndexAttr(0)),
|
||||
SmallVector<OpFoldResult>{builder.getIndexAttr(2), builder.getIndexAttr(8)},
|
||||
SmallVector<OpFoldResult>(2, builder.getIndexAttr(1)));
|
||||
value = SpatReluOp::create(builder, loc, fragment, value);
|
||||
SpatYieldOp::create(builder, loc, ValueRange{value});
|
||||
builder.setInsertionPointAfter(consumer);
|
||||
}
|
||||
func::ReturnOp::create(builder, loc);
|
||||
for (Cost width : {4, 32}) {
|
||||
SchedulingTarget target;
|
||||
target.processorCount = 3;
|
||||
target.sharedMemoryWidthBytes = width;
|
||||
target.sharedMemoryReadLatencyNs = 7;
|
||||
target.sharedMemoryWriteLatencyNs = 11;
|
||||
auto graph = buildCheckedComputeGraph(function, target);
|
||||
auto demand = getPipelineBoundaryMemoryDemand(graph, {0, 1, 1}, target);
|
||||
assert(demand.readBytes == 32 && demand.writeBytes == 32);
|
||||
assert(demand.readRequests == 2 && demand.writeRequests == 4);
|
||||
assert(demand.serviceNs == (width == 4 ? 144 : 58));
|
||||
}
|
||||
module.erase();
|
||||
}
|
||||
|
||||
static void testProducerChunkTransferCost() {
|
||||
using namespace mlir;
|
||||
MLIRContext context(MLIRContext::Threading::DISABLED);
|
||||
@@ -908,6 +984,100 @@ static void testMaterializedPeftGapOrder() {
|
||||
}
|
||||
}
|
||||
|
||||
static void checkPublicationMemoryTraffic(mlir::func::FuncOp function,
|
||||
const ComputeGraph &graph, const SchedulingTarget &target,
|
||||
mlir::PatternRewriter &builder) {
|
||||
MergeScheduleResult schedule;
|
||||
schedule.processorCount = target.processorCount;
|
||||
schedule.processorStages = {0, 0, 1, 1};
|
||||
std::vector<std::vector<size_t>> tasks(4);
|
||||
std::vector<size_t> stages;
|
||||
for (auto [task, node] : llvm::enumerate(graph.nodes)) {
|
||||
bool producer = node.instance.op == graph.nodes.front().instance.op;
|
||||
size_t core = producer ? node.instance.laneStart % 2 : 2 + task % 2;
|
||||
stages.push_back(producer ? 0 : 1);
|
||||
schedule.dominanceOrderCompute.push_back(node.instance);
|
||||
schedule.computeToCpuMap[node.instance] = core;
|
||||
schedule.computeToCpuSlotMap[node.instance] = tasks[core].size();
|
||||
tasks[core].push_back(task);
|
||||
}
|
||||
auto expected = getPipelineBoundaryMemoryDemand(graph, stages, target);
|
||||
populateScheduleEquivalenceClasses(graph, tasks, schedule);
|
||||
auto materialized = materializeScheduledCompute(function, schedule, builder);
|
||||
assert(mlir::succeeded(materialized));
|
||||
auto plan = buildDeferredTransferPlan(function, *materialized, 2, 4);
|
||||
assert(mlir::succeeded(plan));
|
||||
SharedMemoryDemand actual;
|
||||
llvm::DenseSet<int64_t> stored;
|
||||
for (const auto &exchange : plan->exchanges)
|
||||
for (const auto &transfer : exchange->external)
|
||||
if (transfer.hostRouted) {
|
||||
auto type = mlir::cast<mlir::ShapedType>(transfer.requirement->publicationFragmentType);
|
||||
Cost bytes = (type.getNumElements() * target.computeBitwidth + 7) / 8;
|
||||
Cost requests = transfer.targetLanes.size();
|
||||
actual += target.getSharedMemoryDemand(bytes, requests, false);
|
||||
for (size_t index = 0; index < requests; ++index)
|
||||
if (stored.insert(transfer.hostOffsets.valueAt(index)).second)
|
||||
actual += target.getSharedMemoryDemand(bytes, 1, true);
|
||||
}
|
||||
assert(actual.readBytes == expected.readBytes && actual.writeBytes == expected.writeBytes);
|
||||
assert(actual.readRequests == expected.readRequests && actual.writeRequests == expected.writeRequests);
|
||||
assert(actual.serviceNs == expected.serviceNs);
|
||||
for (unsigned registers : {3u, 16u}) {
|
||||
auto shared = buildDeferredTransferPlan(function, *materialized, 2, 4);
|
||||
assert(mlir::succeeded(shared));
|
||||
auto communication = scheduleDeferredCommunication(function, *shared);
|
||||
assert(mlir::succeeded(communication));
|
||||
auto boundaries = buildDeferredBoundaryPlan(*shared, *communication, registers);
|
||||
assert(mlir::succeeded(boundaries));
|
||||
size_t stores = 0, signals = 0, readyWaits = 0, freeWaits = 0, releases = 0;
|
||||
llvm::DenseSet<int64_t> published;
|
||||
for (const auto &boundary : boundaries->boundaries)
|
||||
for (const auto &instruction : boundary.instructions) {
|
||||
if (auto send = std::get_if<EmitSendRun>(&instruction))
|
||||
for (const auto &slice : send->slices)
|
||||
if (slice.family->hostRouted)
|
||||
for (size_t offset = 0; offset < slice.transferCount; ++offset) {
|
||||
size_t index = slice.familyOffset + offset;
|
||||
bool first = published.insert(slice.family->hostOffsets.valueAt(index)).second;
|
||||
assert(bool(slice.family->hostStoreFlags.valueAt(index)) == first);
|
||||
stores += first;
|
||||
++signals;
|
||||
}
|
||||
if (auto wait = std::get_if<EmitHostReuseWait>(&instruction))
|
||||
for (auto interval : wait->lanes.intervals())
|
||||
for (unsigned lane = interval.begin; lane < interval.end; ++lane) {
|
||||
assert(wait->eventRegisters.valueAt(lane) < registers);
|
||||
freeWaits += wait->waitValues.valueAt(lane);
|
||||
}
|
||||
}
|
||||
for (const auto &exchange : shared->exchanges)
|
||||
for (const auto &family : exchange->external)
|
||||
if (family.hostRouted)
|
||||
for (size_t index = 0; index < family.targetCores.size(); ++index) {
|
||||
readyWaits += family.waitValues.valueAt(index);
|
||||
assert(family.eventRegisters.valueAt(index) < registers);
|
||||
}
|
||||
for (const auto &[reader, releaseSignals] : shared->hostReleaseSignals) {
|
||||
llvm::DenseSet<int64_t> writers;
|
||||
for (const auto &release : releaseSignals) {
|
||||
assert(writers.insert(release.writerCore).second);
|
||||
assert(release.eventRegister < registers);
|
||||
++releases;
|
||||
}
|
||||
}
|
||||
assert(stores == expected.writeRequests && signals == expected.readRequests);
|
||||
assert(readyWaits == signals && freeWaits == releases && releases == 4);
|
||||
}
|
||||
auto insufficient = buildDeferredTransferPlan(function, *materialized, 2, 4);
|
||||
assert(mlir::succeeded(insufficient));
|
||||
auto communication = scheduleDeferredCommunication(function, *insufficient);
|
||||
assert(mlir::succeeded(communication));
|
||||
mlir::ScopedDiagnosticHandler suppress(function.getContext(),
|
||||
[](mlir::Diagnostic &) { return mlir::success(); });
|
||||
assert(mlir::failed(buildDeferredBoundaryPlan(*insufficient, *communication, 2)));
|
||||
}
|
||||
|
||||
static void testPhysicalPublicationDependencies() {
|
||||
using namespace mlir;
|
||||
MLIRContext context(MLIRContext::Threading::DISABLED);
|
||||
@@ -923,7 +1093,7 @@ static void testPhysicalPublicationDependencies() {
|
||||
Block *body = function.addEntryBlock();
|
||||
builder.setInsertionPointToStart(body);
|
||||
auto makeBatch = [&](Value input, uint32_t lanes, int64_t projectionSize,
|
||||
bool reverse) {
|
||||
bool reverse, int64_t iterations = 0, bool invariantProjection = false) {
|
||||
auto inputType = cast<RankedTensorType>(input.getType());
|
||||
auto fragment = projectionSize
|
||||
? RankedTensorType::get({projectionSize, 1}, builder.getF32Type())
|
||||
@@ -934,6 +1104,16 @@ static void testPhysicalPublicationDependencies() {
|
||||
assert(succeeded(batch));
|
||||
Value lane = *batch->getLaneArgument();
|
||||
Value value = *batch->getInputArgument(0);
|
||||
scf::ForOp loop;
|
||||
if (iterations) {
|
||||
Value lower = arith::ConstantIndexOp::create(builder, loc, 0);
|
||||
Value upper = arith::ConstantIndexOp::create(builder, loc, iterations);
|
||||
Value step = arith::ConstantIndexOp::create(builder, loc, 1);
|
||||
Value empty = tensor::EmptyOp::create(builder, loc, fragment.getShape(), builder.getF32Type());
|
||||
loop = scf::ForOp::create(builder, loc, lower, upper, step, ValueRange{empty});
|
||||
builder.setInsertionPointToStart(loop.getBody());
|
||||
lane = invariantProjection ? lower : loop.getInductionVar();
|
||||
}
|
||||
if (projectionSize) {
|
||||
Value size = arith::ConstantIndexOp::create(builder, loc, projectionSize);
|
||||
Value offset = arith::MulIOp::create(builder, loc, lane, size);
|
||||
@@ -944,6 +1124,12 @@ static void testPhysicalPublicationDependencies() {
|
||||
SmallVector<OpFoldResult>(2, builder.getIndexAttr(1)));
|
||||
}
|
||||
value = SpatReluOp::create(builder, loc, fragment, value);
|
||||
if (loop) {
|
||||
scf::YieldOp::create(builder, loc, ValueRange{value});
|
||||
builder.setInsertionPointAfter(loop);
|
||||
value = loop.getResult(0);
|
||||
lane = *batch->getLaneArgument();
|
||||
}
|
||||
Value slot = lane;
|
||||
if (reverse) {
|
||||
Value last = arith::ConstantIndexOp::create(builder, loc, lanes - 1);
|
||||
@@ -965,6 +1151,8 @@ static void testPhysicalPublicationDependencies() {
|
||||
auto producer = makeBatch(body->getArgument(0), 4, 0, true);
|
||||
auto projected = makeBatch(producer.getResult(0), 2, 2, false);
|
||||
auto conservative = makeBatch(producer.getResult(0), 2, 0, false);
|
||||
auto looped = makeBatch(producer.getResult(0), 1, 2, false, 2);
|
||||
auto repeated = makeBatch(producer.getResult(0), 1, 2, false, 2, true);
|
||||
SmallVector<onnx_mlir::FragmentAssemblyEntry> entries{
|
||||
{0, 0, {0}, {1}}, {2, 0, {1}, {1}}};
|
||||
auto logicalType = RankedTensorType::get({2}, builder.getF32Type());
|
||||
@@ -1004,7 +1192,19 @@ static void testPhysicalPublicationDependencies() {
|
||||
SchedulingTarget target;
|
||||
target.processorCount = 4;
|
||||
target.processorPeriodNs = period;
|
||||
target.sharedMemoryWidthBytes = 32;
|
||||
target.sharedMemoryReadLatencyNs = 7;
|
||||
target.sharedMemoryWriteLatencyNs = 11;
|
||||
ComputeGraph graph = buildCheckedComputeGraph(function, target);
|
||||
std::vector<size_t> memoryStages(graph.nodes.size(), 1);
|
||||
for (size_t task = 0; task < graph.nodes.size(); ++task)
|
||||
if (graph.nodes[task].instance.op == producer)
|
||||
memoryStages[task] = 0;
|
||||
auto memory = getPipelineBoundaryMemoryDemand(graph, memoryStages, target);
|
||||
assert(memory.readBytes == 26 && memory.writeBytes == 4);
|
||||
assert(memory.readRequests == 26 && memory.writeRequests == 4);
|
||||
assert(memory.serviceNs == 26 * 7 + 4 * 11);
|
||||
assert(graph.baselineMemory.readBytes == 4 && graph.baselineMemory.readRequests == 4);
|
||||
auto predecessorLanes = [&](ComputeInstance instance) {
|
||||
std::vector<uint32_t> lanes;
|
||||
for (const auto &[task, cost] : graph.predecessors[graph.instanceToIndex.lookup(instance)])
|
||||
@@ -1016,6 +1216,8 @@ static void testPhysicalPublicationDependencies() {
|
||||
assert((predecessorLanes({projected, 1, 1}) == std::vector<uint32_t>{0, 1}));
|
||||
for (uint32_t lane : {0, 1})
|
||||
assert((predecessorLanes({conservative, lane, 1}) == std::vector<uint32_t>{0, 1, 2, 3}));
|
||||
assert((predecessorLanes({looped, 0, 1}) == std::vector<uint32_t>{0, 1, 2, 3}));
|
||||
assert((predecessorLanes({repeated, 0, 1}) == std::vector<uint32_t>{2, 3}));
|
||||
assert((predecessorLanes({scalar, 0, 1}) == std::vector<uint32_t>{1, 3}));
|
||||
// Two local fragment copies and the vector operation use the same ns unit.
|
||||
assert(graph.nodes[graph.instanceToIndex.lookup({scalar, 0, 1})].cost == 10 * period);
|
||||
@@ -1031,8 +1233,24 @@ static void testPhysicalPublicationDependencies() {
|
||||
assert(dependencies.size() == 1);
|
||||
assert(dependencies.front().second.fixed == 8 * period);
|
||||
assert(dependencies.front().second.networkFlits == 12);
|
||||
if (period == 3)
|
||||
checkPublicationMemoryTraffic(function, graph, target, builder);
|
||||
}
|
||||
|
||||
builder.setInsertionPointToEnd(owner->getBody());
|
||||
auto largeLoop = func::FuncOp::create(builder, loc, "invariant_loop",
|
||||
builder.getFunctionType({fragmentType}, {}));
|
||||
builder.setInsertionPointToStart(largeLoop.addEntryBlock());
|
||||
auto loopProducer = makeBatch(largeLoop.getArgument(0), 4, 0, false);
|
||||
makeBatch(loopProducer.getResult(0), 1, 2, false, 1000000000, true);
|
||||
func::ReturnOp::create(builder, loc);
|
||||
SchedulingTarget loopTarget;
|
||||
loopTarget.processorCount = 4;
|
||||
auto compactGraph = buildCheckedComputeGraph(largeLoop, loopTarget);
|
||||
assert(compactGraph.publicationTransfers.size() == 2);
|
||||
for (const auto &transfer : compactGraph.publicationTransfers)
|
||||
assert(transfer.count == 1000000000);
|
||||
|
||||
builder.setInsertionPointToEnd(owner->getBody());
|
||||
auto independent = func::FuncOp::create(builder, loc, "batch_order",
|
||||
builder.getFunctionType({fragmentType}, {}));
|
||||
@@ -1303,6 +1521,152 @@ static void testPipelineInputArrival() {
|
||||
}
|
||||
}
|
||||
|
||||
static void testBaselineConstantMemory() {
|
||||
using namespace mlir;
|
||||
MLIRContext context(MLIRContext::Threading::DISABLED);
|
||||
context.loadDialect<SpatialDialect, func::FuncDialect, arith::ArithDialect,
|
||||
tensor::TensorDialect, scf::SCFDialect>();
|
||||
OwningOpRef<ModuleOp> module = ModuleOp::create(UnknownLoc::get(&context));
|
||||
OpBuilder builder(module->getBodyRegion());
|
||||
auto loc = module->getLoc();
|
||||
auto type = RankedTensorType::get({16}, builder.getF32Type());
|
||||
auto function = func::FuncOp::create(builder, loc, "constant_memory",
|
||||
builder.getFunctionType({type}, {type}));
|
||||
builder.setInsertionPointToStart(function.addEntryBlock());
|
||||
Value external = arith::ConstantOp::create(builder, loc, type,
|
||||
DenseElementsAttr::get(type, builder.getF32FloatAttr(2.5)));
|
||||
auto compute = onnx_mlir::createEmptySpatGraphCompute(builder, loc,
|
||||
TypeRange{type}, ValueRange{}, ValueRange{function.getArgument(0), external});
|
||||
Value start = arith::ConstantIndexOp::create(builder, loc, 0);
|
||||
Value end = arith::ConstantIndexOp::create(builder, loc, 3);
|
||||
Value step = arith::ConstantIndexOp::create(builder, loc, 1);
|
||||
Value captured = arith::ConstantOp::create(builder, loc, type,
|
||||
DenseElementsAttr::get(type, builder.getF32FloatAttr(1.5)));
|
||||
auto loop = scf::ForOp::create(builder, loc, start, end, step,
|
||||
ValueRange{*compute.getInputArgument(0)});
|
||||
builder.setInsertionPointToStart(loop.getBody());
|
||||
Value result = SpatVAddOp::create(builder, loc, type,
|
||||
loop.getRegionIterArg(0), captured);
|
||||
scf::YieldOp::create(builder, loc, ValueRange{result});
|
||||
builder.setInsertionPointAfter(loop);
|
||||
result = SpatVAddOp::create(builder, loc, type, loop.getResult(0), *compute.getInputArgument(1));
|
||||
SpatYieldOp::create(builder, loc, ValueRange{result});
|
||||
builder.setInsertionPointAfter(compute);
|
||||
func::ReturnOp::create(builder, loc, compute.getResults());
|
||||
assert(succeeded(verify(function)));
|
||||
SchedulingTarget target;
|
||||
target.processorCount = 1;
|
||||
target.sharedMemoryWidthBytes = 32;
|
||||
target.sharedMemoryReadLatencyNs = 7;
|
||||
target.sharedMemoryWriteLatencyNs = 11;
|
||||
auto graph = buildCheckedComputeGraph(function, target);
|
||||
assert(graph.baselineMemory.readBytes == 80);
|
||||
assert(graph.baselineMemory.readRequests == 5);
|
||||
assert(graph.baselineMemory.writeBytes == 16 && graph.baselineMemory.writeRequests == 1);
|
||||
assert(graph.baselineMemory.serviceNs == 46);
|
||||
}
|
||||
|
||||
static void testPipelineSharedMemory() {
|
||||
mlir::MLIRContext context(mlir::MLIRContext::Threading::DISABLED);
|
||||
auto fragment = mlir::RankedTensorType::get({33}, mlir::IntegerType::get(&context, 8));
|
||||
mlir::Block generations;
|
||||
auto firstGeneration = generations.addArgument(fragment, mlir::UnknownLoc::get(&context));
|
||||
auto nextGeneration = generations.addArgument(fragment, mlir::UnknownLoc::get(&context));
|
||||
auto shared = getHostPublicationIdentity(firstGeneration.getAsOpaquePointer(), 0, fragment, true);
|
||||
assert(shared == getHostPublicationIdentity(firstGeneration.getAsOpaquePointer(), 0, fragment, true));
|
||||
assert(shared != getHostPublicationIdentity(firstGeneration.getAsOpaquePointer(), 1, fragment, true));
|
||||
assert(shared != getHostPublicationIdentity(nextGeneration.getAsOpaquePointer(), 0, fragment, true));
|
||||
assert(!getHostPublicationIdentity(firstGeneration.getAsOpaquePointer(), 0, fragment, false));
|
||||
ScheduleCostSummary previous, candidate;
|
||||
previous.stageSpanNs = {25000};
|
||||
previous.maximumCoreWorkNs = 15000;
|
||||
previous.baselineMemory.serviceNs = 50000;
|
||||
candidate.stageSpanNs = {32000};
|
||||
candidate.maximumCoreWorkNs = 27000;
|
||||
candidate.baselineMemory.serviceNs = 40000;
|
||||
// Reduced memory and stage demand must not hide concentrated core work.
|
||||
assert(!isBetterPipelineSchedule(candidate, previous));
|
||||
candidate.maximumCoreWorkNs = 24000;
|
||||
assert(isBetterPipelineSchedule(candidate, previous));
|
||||
candidate.stageSpanNs = {36000};
|
||||
assert(!isBetterPipelineSchedule(candidate, previous));
|
||||
assert(!isBetterPipelineSchedule(previous, previous));
|
||||
ComputeGraph graph;
|
||||
graph.predecessors.resize(4);
|
||||
graph.successors.resize(4);
|
||||
for (uint32_t task = 0; task < 4; ++task) {
|
||||
graph.nodes.push_back({{nullptr, task, 1}, 1000, {}, task});
|
||||
if (task) {
|
||||
graph.predecessors[task].push_back({task - 1, {}});
|
||||
graph.successors[task - 1].push_back({task, {}});
|
||||
graph.publicationTransfers.push_back({task - 1, task, {}, 0, {},
|
||||
task == 2 ? Cost{16384} : Cost{32}, 1});
|
||||
}
|
||||
}
|
||||
SchedulingTarget target;
|
||||
target.processorCount = 4;
|
||||
target.residentWeightCapacity = 4;
|
||||
target.interProcessorLatencyNs.assign(16, 0);
|
||||
target.sharedMemoryWidthBytes = 32;
|
||||
auto stages = [&](const MergeScheduleResult &schedule) {
|
||||
std::vector<size_t> result;
|
||||
for (const auto &node : graph.nodes)
|
||||
result.push_back(schedule.processorStages[schedule.computeToCpuMap.lookup(node.instance)]);
|
||||
return result;
|
||||
};
|
||||
for (Cost latency : {Cost{1}, Cost{10}}) {
|
||||
target.sharedMemoryReadLatencyNs = latency;
|
||||
target.sharedMemoryWriteLatencyNs = latency;
|
||||
graph.baselineMemory = target.getSharedMemoryDemand(32, 16, false);
|
||||
MergeScheduleResult schedule;
|
||||
std::string error;
|
||||
assert(mlir::succeeded(runPipelineScheduler(graph, schedule, 2, target, error)));
|
||||
auto assignment = stages(schedule);
|
||||
assert(assignment.front() == 0 && assignment.back() == 1);
|
||||
for (size_t task = 1; task < 4; ++task)
|
||||
assert(assignment[task] >= assignment[task - 1]
|
||||
&& assignment[task] <= assignment[task - 1] + 1);
|
||||
auto memory = getPipelineBoundaryMemoryDemand(graph, assignment, target);
|
||||
assert(memory.readRequests == 1 && memory.writeRequests == 1);
|
||||
assert(memory.readBytes == (latency == 1 ? 16384 : 32));
|
||||
assert(memory.writeBytes == memory.readBytes);
|
||||
assert(schedule.costs.boundaryMemory.serviceNs == memory.serviceNs);
|
||||
}
|
||||
// Per-fragment rounding and fan-out must precede aggregation.
|
||||
auto rounded = target.getSharedMemoryDemand(33, 3, true);
|
||||
assert(rounded.writeBytes == 99 && rounded.writeRequests == 3);
|
||||
assert(rounded.serviceNs == 60);
|
||||
graph.publicationTransfers = {{0, 1, {}, 3, {}, 33, 2}, {0, 2, {}, 3, {}, 33, 1}};
|
||||
auto fanout = getPipelineBoundaryMemoryDemand(graph, {0, 1, 1, 1}, target);
|
||||
assert(fanout.readBytes == 99 && fanout.writeBytes == 99);
|
||||
assert(fanout.readRequests == 3 && fanout.writeRequests == 3);
|
||||
assert(fanout.serviceNs == 120);
|
||||
graph.publicationTransfers = {
|
||||
{0, 1, firstGeneration, 0, {}, 33, 2, fragment},
|
||||
{0, 2, firstGeneration, 0, {}, 33, 1, fragment}};
|
||||
fanout = getPipelineBoundaryMemoryDemand(graph, {0, 1, 1, 1}, target);
|
||||
assert(fanout.readBytes == 99 && fanout.writeBytes == 33);
|
||||
assert(fanout.readRequests == 3 && fanout.writeRequests == 1);
|
||||
assert(fanout.serviceNs == 80);
|
||||
graph.publicationTransfers.push_back({0, 3, nextGeneration, 0, {}, 33, 1, fragment});
|
||||
graph.publicationTransfers.push_back({0, 3, firstGeneration, 1, {}, 33, 1, fragment});
|
||||
fanout = getPipelineBoundaryMemoryDemand(graph, {0, 1, 1, 1}, target);
|
||||
assert(fanout.readRequests == 5 && fanout.writeRequests == 3);
|
||||
assert(getPipelineBoundaryMemoryDemand(graph, {0, 0, 0, 0}, target).serviceNs == 0);
|
||||
graph.predecessors[3].push_back({0, {}});
|
||||
graph.successors[0].push_back({3, {}});
|
||||
graph.publicationTransfers.push_back({0, 3, {}, 0, {}, 4096, 1});
|
||||
MergeScheduleResult constrained;
|
||||
std::string error;
|
||||
assert(mlir::succeeded(runPipelineScheduler(graph, constrained, 4, target, error)));
|
||||
auto assignment = stages(constrained);
|
||||
for (size_t task = 0; task < graph.nodes.size(); ++task)
|
||||
for (const auto &[predecessor, transfer] : graph.predecessors[task])
|
||||
assert(assignment[task] >= assignment[predecessor]
|
||||
&& assignment[task] <= assignment[predecessor] + 1);
|
||||
assert(mlir::failed(runPipelineScheduler(graph, constrained, 5, target, error)));
|
||||
}
|
||||
|
||||
static void testReadyCommunication() {
|
||||
mlir::MLIRContext context(mlir::MLIRContext::Threading::DISABLED);
|
||||
mlir::OwningOpRef<mlir::ModuleOp> owner =
|
||||
@@ -1372,6 +1736,9 @@ static void testPipelineRestartOrder() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
testStridedPublicationMemory();
|
||||
testPipelineSharedMemory();
|
||||
testBaselineConstantMemory();
|
||||
testResidentWeightPackingRepair();
|
||||
testProducerChunkTransferCost();
|
||||
testPeftReservationPassFailure();
|
||||
|
||||
Reference in New Issue
Block a user