finally fast googlenet with correct latency artifacts for fair comparison
Validate Operations / validate-operations (push) Has been cancelled

This commit is contained in:
NiccoloN
2026-07-29 18:20:44 +02:00
parent 060a21172e
commit 1b4f070bef
74 changed files with 2773 additions and 1311 deletions
+264 -2
View File
@@ -1,9 +1,21 @@
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
#include "mlir/Transforms/Passes.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/ADT/SmallString.h"
#include <cmath>
#include <limits>
#include <tuple>
#include "src/Accelerators/PIM/Compiler/PimCompilerOptions.hpp"
#include "src/Accelerators/PIM/Compiler/PimCompilerUtils.hpp"
#include "src/Accelerators/PIM/Dialect/Pim/PimOps.hpp"
#include "src/Accelerators/PIM/Dialect/Spatial/Transforms/MergeComputeNodes/Scheduling/SchedulingTarget.hpp"
#include "src/Accelerators/PIM/Pass/PIMPasses.h"
#include "src/Compiler/CompilerPasses.hpp"
@@ -14,6 +26,254 @@ using namespace onnx_mlir;
namespace onnx_mlir {
namespace {
void setDefaultPimInterProcessorLatencies(
spatial::SchedulingTarget& target) {
size_t rows = static_cast<size_t>(
std::sqrt(static_cast<long double>(target.processorCount)));
while (rows > 1 && target.processorCount % rows != 0)
--rows;
size_t columns = (target.processorCount + rows - 1) / rows;
target.interProcessorLatencyNs.assign(
target.processorCount * target.processorCount, 0);
Cost latencySum = 0;
size_t pairCount = 0;
for (size_t source = 0; source < target.processorCount; ++source) {
for (size_t destination = 0;
destination < target.processorCount; ++destination) {
if (source == destination)
continue;
size_t sourceRow = source / columns;
size_t sourceColumn = source % columns;
size_t destinationRow = destination / columns;
size_t destinationColumn = destination % columns;
size_t rowDistance = sourceRow > destinationRow
? sourceRow - destinationRow
: destinationRow - sourceRow;
size_t columnDistance = sourceColumn > destinationColumn
? sourceColumn - destinationColumn
: destinationColumn - sourceColumn;
Cost latency = static_cast<Cost>(2 + rowDistance + columnDistance);
target.interProcessorLatencyNs[
source * target.processorCount + destination] = latency;
latencySum = checkedAdd(latencySum, latency);
++pairCount;
}
}
target.averageInterProcessorLatencyNs =
pairCount == 0
? 0
: (latencySum + static_cast<Cost>(pairCount) - 1)
/ static_cast<Cost>(pairCount);
}
spatial::SchedulingTarget getDefaultPimSchedulingTarget() {
spatial::SchedulingTarget target;
target.processorCount = static_cast<size_t>(coresCount.getValue());
target.residentWeightCapacity = crossbarCountInCore.getValue();
target.matrixRows = crossbarSize.getValue();
target.matrixColumns = crossbarSize.getValue();
setDefaultPimInterProcessorLatencies(target);
return target;
}
const llvm::json::Object& requireObject(const llvm::json::Object& object,
llvm::StringRef key,
llvm::StringRef path) {
const llvm::json::Object* nested = object.getObject(key);
if (!nested)
llvm::report_fatal_error("PIM target config is missing object '" + path + "." + key + "'");
return *nested;
}
Cost getConfigCost(const llvm::json::Object& object,
llvm::StringRef key,
Cost fallback,
bool allowZero = false) {
std::optional<double> number = object.getNumber(key);
if (!number)
return fallback;
if (!std::isfinite(*number) || *number < 0.0 || (!allowZero && *number == 0.0)
|| *number > static_cast<double>(std::numeric_limits<Cost>::max()))
llvm::report_fatal_error("PIM target config field '" + key + "' must be a valid positive number");
return static_cast<Cost>(std::ceil(*number));
}
std::pair<size_t, size_t> getConfigPair(const llvm::json::Object& object,
llvm::StringRef key) {
const llvm::json::Array* values = object.getArray(key);
if (!values || values->size() != 2)
llvm::report_fatal_error("PIM target config field '" + key + "' must contain two integers");
std::optional<int64_t> first = (*values)[0].getAsInteger();
std::optional<int64_t> second = (*values)[1].getAsInteger();
if (!first || !second || *first <= 0 || *second <= 0)
llvm::report_fatal_error("PIM target config field '" + key + "' must contain two positive integers");
return {static_cast<size_t>(*first), static_cast<size_t>(*second)};
}
void loadPimInterProcessorLatencies(
spatial::SchedulingTarget& target,
const llvm::json::Object& network) {
std::optional<llvm::StringRef> filename =
network.getString("net_config_file_path");
if (!filename)
llvm::report_fatal_error(
"PIM target config is missing network latency file path");
llvm::SmallString<256> networkPath(*filename);
if (!llvm::sys::path::is_absolute(networkPath)) {
llvm::SmallString<256> configDirectory(pimTargetConfig.getValue());
llvm::sys::path::remove_filename(configDirectory);
llvm::sys::path::append(configDirectory, networkPath);
networkPath = configDirectory;
}
auto buffer = llvm::MemoryBuffer::getFile(networkPath);
if (!buffer)
llvm::report_fatal_error(
llvm::Twine("failed to read PIM network config '")
+ networkPath + "': " + buffer.getError().message());
auto parsed = llvm::json::parse(buffer.get()->getBuffer());
if (!parsed)
llvm::report_fatal_error(
llvm::Twine("failed to parse PIM network config '")
+ networkPath + "': " + llvm::toString(parsed.takeError()));
const llvm::json::Object* root = parsed->getAsObject();
const llvm::json::Object* latencies =
root ? root->getObject("latency") : nullptr;
if (!latencies)
llvm::report_fatal_error(
"PIM network config is missing its latency matrix");
target.interProcessorLatencyNs.assign(
target.processorCount * target.processorCount, 0);
Cost latencySum = 0;
size_t pairCount = 0;
for (size_t source = 0; source < target.processorCount; ++source) {
std::string sourceKey = std::to_string(source);
const llvm::json::Object* row = latencies->getObject(sourceKey);
if (!row)
llvm::report_fatal_error(
llvm::Twine("PIM network config is missing latency row ")
+ sourceKey);
for (size_t destination = 0;
destination < target.processorCount; ++destination) {
if (source == destination)
continue;
std::string destinationKey = std::to_string(destination);
std::optional<double> latency = row->getNumber(destinationKey);
if (!latency || !std::isfinite(*latency) || *latency <= 0.0)
llvm::report_fatal_error(
llvm::Twine("PIM network config is missing latency ")
+ sourceKey + " -> " + destinationKey);
Cost roundedLatency = static_cast<Cost>(std::ceil(*latency));
target.interProcessorLatencyNs[
source * target.processorCount + destination] = roundedLatency;
latencySum = checkedAdd(latencySum, roundedLatency);
++pairCount;
}
}
target.averageInterProcessorLatencyNs =
pairCount == 0
? 0
: (latencySum + static_cast<Cost>(pairCount) - 1)
/ static_cast<Cost>(pairCount);
}
spatial::SchedulingTarget getPimSchedulingTarget() {
spatial::SchedulingTarget target = getDefaultPimSchedulingTarget();
if (pimTargetConfig.empty())
return target;
auto buffer = llvm::MemoryBuffer::getFile(pimTargetConfig);
if (!buffer)
llvm::report_fatal_error(
llvm::Twine("failed to read PIM target config '")
+ pimTargetConfig.getValue() + "': " + buffer.getError().message());
auto parsed = llvm::json::parse(buffer.get()->getBuffer());
if (!parsed)
llvm::report_fatal_error(
llvm::Twine("failed to parse PIM target config '")
+ pimTargetConfig.getValue() + "': "
+ llvm::toString(parsed.takeError()));
const llvm::json::Object* root = parsed->getAsObject();
if (!root)
llvm::report_fatal_error("PIM target config must contain a JSON object");
const llvm::json::Object& chip = requireObject(*root, "chip_config", "root");
const llvm::json::Object& core = requireObject(chip, "core_config", "chip_config");
const llvm::json::Object& matrix =
requireObject(core, "matrix_config", "chip_config.core_config");
const llvm::json::Object& localMemory =
requireObject(core, "local_memory_config", "chip_config.core_config");
const llvm::json::Object& network =
requireObject(chip, "network_config", "chip_config");
std::optional<int64_t> coreCount = chip.getInteger("core_cnt");
if (!coreCount || *coreCount <= 0)
llvm::report_fatal_error("PIM target config field 'core_cnt' must be a positive integer");
target.processorCount = static_cast<size_t>(*coreCount);
target.residentWeightCapacity =
getConfigCost(matrix, "xbar_array_count", target.residentWeightCapacity);
std::tie(target.matrixRows, target.matrixColumns) =
getConfigPair(matrix, "xbar_size");
if (target.processorCount != static_cast<size_t>(coresCount.getValue())
|| target.residentWeightCapacity != crossbarCountInCore.getValue()
|| target.matrixRows != crossbarSize.getValue()
|| target.matrixColumns != crossbarSize.getValue())
llvm::report_fatal_error("PIM target config resources do not match --core-count, "
"--crossbar-count, and --crossbar-size");
loadPimInterProcessorLatencies(target, network);
target.processorPeriodNs =
getConfigCost(core, "period", target.processorPeriodNs);
target.localMemoryWidthBytes =
getConfigCost(localMemory, "data_width", target.localMemoryWidthBytes);
target.localMemoryReadLatencyCycles =
getConfigCost(localMemory, "read_latency_cycle", target.localMemoryReadLatencyCycles);
target.localMemoryWriteLatencyCycles =
getConfigCost(localMemory, "write_latency_cycle", target.localMemoryWriteLatencyCycles);
target.transferWidthBytes =
getConfigCost(network, "bus_width", target.transferWidthBytes);
target.vectorWidth = getConfigCost(core, "vector_width", target.vectorWidth);
target.vectorLatencyCycles =
getConfigCost(core, "vector_latency_cycle", target.vectorLatencyCycles);
target.matrixPeriodNs =
getConfigCost(matrix, "period", target.matrixPeriodNs);
target.matrixInputResolutionBits =
getConfigCost(matrix, "dac_resolution", target.matrixInputResolutionBits);
target.matrixInputLatencyCycles =
getConfigCost(matrix, "dac_latency_cycle", target.matrixInputLatencyCycles);
target.matrixInputParallelism =
getConfigCost(matrix, "dac_count", target.matrixInputParallelism);
target.matrixReadLatencyNs =
getConfigCost(matrix, "xbar_latency", target.matrixReadLatencyNs);
target.matrixSampleLatencyCycles =
getConfigCost(matrix, "sample_hold_latency_cycle", target.matrixSampleLatencyCycles);
target.matrixOutputLatencyCycles =
getConfigCost(matrix, "adc_latency_cycle", target.matrixOutputLatencyCycles);
target.matrixOutputParallelism =
getConfigCost(matrix, "adc_count", target.matrixOutputParallelism);
target.matrixShiftLatencyCycles =
getConfigCost(matrix, "shift_adder_latency_cycle", target.matrixShiftLatencyCycles);
target.matrixBufferLatencyCycles =
getConfigCost(matrix, "output_buffer_latency_cycle", target.matrixBufferLatencyCycles);
target.matrixInputBufferLatencyCycles =
getConfigCost(matrix,
"input_buffer_latency_cycle",
target.matrixInputBufferLatencyCycles,
/*allowZero=*/true);
target.matrixPipeline = matrix.getBoolean("pipeline_mode").value_or(target.matrixPipeline);
return target;
}
} // namespace
void addPassesPim(OwningOpRef<ModuleOp>& module,
PassManager& pm,
EmissionTargetType& emissionTarget,
@@ -31,11 +291,13 @@ void addPassesPim(OwningOpRef<ModuleOp>& module,
addONNXToMLIRPasses(pm, /*target CPU*/ false);
if (pimEmissionTarget >= EmitSpatial) {
spatial::SchedulingTarget schedulingTarget = getPimSchedulingTarget();
pm.addPass(createONNXToSpatialPass());
pm.addPass(createSpatialLayoutPlanningPass());
pm.addPass(createLowerSpatialPlansPass());
pm.addPass(createTrivialGraphComputeMergePass());
pm.addPass(createMergeComputeNodesPass());
pm.addPass(createTrivialGraphComputeMergePass(
schedulingTarget.residentWeightCapacity));
pm.addPass(createMergeComputeNodesPass(schedulingTarget));
pm.addPass(createMessagePass("Onnx lowered to Spatial"));
}