97 lines
5.4 KiB
C++
97 lines
5.4 KiB
C++
#include "src/Accelerators/PIM/Common/IR/CopyAccessPlan.hpp"
|
|
#include "src/Accelerators/PIM/Common/IR/ShapeUtils.hpp"
|
|
#include "src/Accelerators/PIM/Dialect/Spatial/Passes/Transforms/MergeComputeNodes/Scheduling/SchedulingTarget.hpp"
|
|
#include "TestCheck.hpp"
|
|
#include <limits>
|
|
#include <random>
|
|
|
|
using namespace onnx_mlir;
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc > 1 && std::string(argv[1]) == "--stride-overflow") {
|
|
computeRowMajorStrides({2, std::numeric_limits<int64_t>::max(), 2});
|
|
return 0;
|
|
}
|
|
if (argc > 1) TEST_CHECK(false);
|
|
auto dense = CopyAccessPlan::build({3, 4}, {4, 1}, {4, 1});
|
|
TEST_CHECK(mlir::succeeded(dense) && dense->getRunCount() == 1 && dense->getRunElements() == 12);
|
|
auto strided = CopyAccessPlan::build({3, 4}, {8, 1}, {4, 1});
|
|
TEST_CHECK(mlir::succeeded(strided) && strided->getRunCount() == 3 && strided->getRunElements() == 4);
|
|
TEST_CHECK(*strided->getRunOffsets(2) == std::make_pair(int64_t{16}, int64_t{8}));
|
|
TEST_CHECK(mlir::failed(strided->getRunOffsets(-1)) && mlir::failed(strided->getRunOffsets(3)));
|
|
auto large = CopyAccessPlan::build({1000000000, 16}, {32, 1}, {16, 1});
|
|
TEST_CHECK(mlir::succeeded(large) && large->getOuterShape().size() == 1);
|
|
TEST_CHECK(large->getRunCount() == 1000000000 && large->getRunElements() == 16);
|
|
TEST_CHECK(*large->getRunOffsets(999999999) == std::make_pair(int64_t{31999999968}, int64_t{15999999984}));
|
|
auto scalar = CopyAccessPlan::build({}, {}, {});
|
|
TEST_CHECK(mlir::succeeded(scalar) && scalar->getRunCount() == 1 && scalar->getRunElements() == 1);
|
|
// Observed input planes and channel-wise publication slices, without PIM IR.
|
|
auto input = CopyAccessPlan::build({3, 1, 224}, {50176, 224, 1}, {224, 224, 1});
|
|
TEST_CHECK(mlir::succeeded(input) && input->getRunCount() == 3 && input->getRunElements() == 224);
|
|
TEST_CHECK(*input->getRunOffsets(1) == std::make_pair(int64_t{50176}, int64_t{224}));
|
|
for (int64_t channels : {32, 96}) {
|
|
auto publication = CopyAccessPlan::build({1, channels, 1, 80},
|
|
{channels * 6400, 6400, 80, 1}, {channels * 80, 80, 80, 1});
|
|
TEST_CHECK(mlir::succeeded(publication) && publication->getRunCount() == channels);
|
|
TEST_CHECK(publication->getRunElements() == 80);
|
|
TEST_CHECK(*publication->getRunOffsets(1) == std::make_pair(int64_t{6400}, int64_t{80}));
|
|
}
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({2}, {}, {1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({0}, {1}, {1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({-1}, {1}, {1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({2}, {-1}, {1})));
|
|
int64_t limit = std::numeric_limits<int64_t>::max();
|
|
TEST_CHECK(mlir::failed(computeRowMajorStridesChecked({2, limit, 2})));
|
|
auto limitStrides = computeRowMajorStridesChecked({2, limit});
|
|
TEST_CHECK(mlir::succeeded(limitStrides) && (*limitStrides)[0] == limit);
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({limit, 2}, {2, 1}, {2, 1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({limit, 2}, {3, 1}, {3, 1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({3}, {limit}, {1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({3}, {1}, {limit})));
|
|
// A run start can fit even when its final element does not.
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({2, 2}, {limit, 1}, {2, 1})));
|
|
TEST_CHECK(mlir::failed(CopyAccessPlan::build({2, 2}, {2, 1}, {limit, 1})));
|
|
auto boundary = CopyAccessPlan::build({2}, {limit}, {1});
|
|
TEST_CHECK(mlir::succeeded(boundary) && boundary->getRunOffsets(1)->first == limit);
|
|
auto unit = CopyAccessPlan::build({1, 2}, {limit, 1}, {-1, 1});
|
|
TEST_CHECK(mlir::succeeded(unit) && unit->getRunElements() == 2);
|
|
|
|
// Independent element-by-element reference checks ordered offsets, not totals.
|
|
std::mt19937 random(123);
|
|
for (unsigned trial = 0; trial < 2000; ++trial) {
|
|
size_t rank = random() % 4 + 1;
|
|
llvm::SmallVector<int64_t> shape(rank), source(rank), target(rank);
|
|
for (auto &size : shape) size = random() % 4 + 1;
|
|
auto rowMajor = computeRowMajorStrides(shape);
|
|
for (size_t dim = 0; dim < rank; ++dim) {
|
|
source[dim] = random() % 2 ? rowMajor[dim] : random() % 20;
|
|
target[dim] = random() % 2 ? rowMajor[dim] : random() % 20;
|
|
}
|
|
auto plan = CopyAccessPlan::build(shape, source, target);
|
|
TEST_CHECK(mlir::succeeded(plan));
|
|
int64_t linear = 0;
|
|
for (int64_t run = 0; run < plan->getRunCount(); ++run) {
|
|
auto offsets = plan->getRunOffsets(run);
|
|
TEST_CHECK(mlir::succeeded(offsets));
|
|
for (int64_t element = 0; element < plan->getRunElements(); ++element, ++linear) {
|
|
auto indices = delinearizeIndex(linear, shape, rowMajor);
|
|
TEST_CHECK(offsets->first + element == linearizeIndex(indices, source));
|
|
TEST_CHECK(offsets->second + element == linearizeIndex(indices, target));
|
|
}
|
|
}
|
|
TEST_CHECK(linear == getNumElements(shape));
|
|
}
|
|
|
|
// The cost caller and lowering use the same geometry; target timing stays separate.
|
|
for (int64_t width : {8, 16}) {
|
|
spatial::SchedulingTarget target;
|
|
target.sharedMemoryWidthBytes = width;
|
|
target.sharedMemoryReadLatencyNs = width == 8 ? 3 : 8;
|
|
auto access = CopyAccessPlan::build({3, 5}, {8, 1}, {5, 1});
|
|
TEST_CHECK(mlir::succeeded(access));
|
|
auto demand = target.getSharedMemoryDemand(access->getRunElements() * 4, access->getRunCount(), false);
|
|
TEST_CHECK(demand.readBytes == 60 && demand.readRequests == 3);
|
|
TEST_CHECK(demand.serviceNs == 3 * ((20 + width - 1) / width) * target.sharedMemoryReadLatencyNs);
|
|
}
|
|
}
|