From f6319b937d77671efcdc9f6ab8dd9b6b2c1dead9 Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Sun, 20 Jul 2025 13:20:05 +0200 Subject: [PATCH 1/8] add automatic tiling with myrtle --- .gitmodules | 3 + .../Conversion/ConvertSnitchToLLVM.cpp | 29 + .../Quidditch/Conversion/ConvertToRISCV.cpp | 112 ++++ .../Dialect/Snitch/IR/QuidditchSnitchOps.cpp | 9 + .../Dialect/Snitch/IR/QuidditchSnitchOps.td | 39 ++ .../Dialect/Snitch/Transforms/Passes.td | 9 + .../Snitch/Transforms/SpecializeDMACode.cpp | 92 ++++ .../src/Quidditch/Target/CMakeLists.txt | 3 + .../src/Quidditch/Target/ConfigureTiles.cpp | 191 +++++++ .../compiler/src/Quidditch/Target/Passes.td | 23 + .../src/Quidditch/Target/QuidditchTarget.cpp | 51 +- .../src/Quidditch/Target/TilingScheme.cpp | 244 ++++++++ .../src/Quidditch/Target/TilingScheme.h | 64 +++ comparing-tile-sizes/.gitignore | 42 ++ comparing-tile-sizes/README.md | 145 +++++ comparing-tile-sizes/cmakelist-epilogue.txt | 42 ++ .../cmakelist-middle-original.txt | 2 + comparing-tile-sizes/cmakelist-prologue.txt | 2 + comparing-tile-sizes/compileGrapefruits.sh | 123 +++++ comparing-tile-sizes/context.png | Bin 0 -> 71949 bytes .../ex_1x600x600wm-n-k_case1_searchSpace.csv | 48 ++ comparing-tile-sizes/ex_mini.csv | 2 + .../generateTileSizeJSONFiles.py | 55 ++ comparing-tile-sizes/merge.py | 13 + .../old-tiling-schemes/0-40-100.json | 27 + comparing-tile-sizes/runGrapefruits.sh | 48 ++ comparing-tile-sizes/run_experiment.sh | 53 ++ comparing-tile-sizes/scrapeGrapefruits.sh | 102 ++++ comparing-tile-sizes/scrutinizeGrapefruits.sh | 88 +++ myrtle/.gitignore | 4 + myrtle/LICENSE | 201 +++++++ myrtle/README.md | 25 + myrtle/myrtle/__init__.py | 0 myrtle/myrtle/graphing/__init__.py | 0 myrtle/myrtle/graphing/context2.png | Bin 0 -> 71949 bytes myrtle/myrtle/graphing/graph_utils.py | 519 ++++++++++++++++++ myrtle/myrtle/graphing/graphing-refactored.py | 257 +++++++++ myrtle/myrtle/myrtle.py | 115 ++++ myrtle/myrtle/tile_SA/__init__.py | 0 .../myrtle/tile_SA/quidditch_load_counting.py | 160 ++++++ myrtle/myrtle/tile_SA/utils.py | 54 ++ myrtle/myrtle/tile_gen/__init__.py | 0 .../tile_gen/peek_at_snitch_assembly.py | 229 ++++++++ myrtle/myrtle/tile_gen/tile_size_generator.py | 246 +++++++++ myrtle/myrtle/tile_sel/README.md | 3 + myrtle/myrtle/tile_sel/dispatch-8-svr.pickle | Bin 0 -> 2775 bytes myrtle/myrtle/tile_sel/linesOfBestFit.pickle | Bin 0 -> 4660 bytes myrtle/pyproject.toml | 20 + myrtle/requirements.txt | 5 + .../holistic-data/1x1200x400wm-n-k-timed.csv | 48 ++ .../holistic-data/1x400x161wm-n-k-timed.csv | 4 + .../holistic-data/1x600x400wm-n-k-timed.csv | 32 ++ .../holistic-data/1x600x600wm-n-k-timed.csv | 48 ++ .../dispatch_1_case1_everything.csv | 18 + .../dispatch_1_case2_everything.csv | 42 ++ .../dispatch_7_case1_everything.csv | 15 + .../dispatch_7_case2_everything.csv | 34 ++ .../dispatch_8_case1_everything.csv | 31 ++ .../dispatch_8_case2_everything.csv | 35 ++ .../pivoted.cost_model_30_thru_201.csv | 316 +++++++++++ myrtle/test_output.json | 1 + requirements.txt | 1 + runtime/cmake/quidditch_module.cmake | 6 +- runtime/runtime/src/Quidditch/CMakeLists.txt | 1 + .../Quidditch/time_dispatch/CMakeLists.txt | 11 + .../Quidditch/time_dispatch/time_dispatch.c | 12 + .../Quidditch/time_dispatch/time_dispatch.h | 7 + runtime/samples/.gitignore | 1 + runtime/samples/CMakeLists.txt | 5 + runtime/samples/fakeNN/CMakeLists.txt | 46 ++ runtime/samples/fakeNN/fakeNN.py | 157 ++++++ runtime/samples/fakeNN/fakeNN_util.c | 77 +++ runtime/samples/fakeNN/fakeNN_util.h | 11 + runtime/samples/fakeNN/requirements.txt | 2 + runtime/samples/fakeNN/ts-answer.json | 1 + runtime/samples/grapeFruit/CMakeLists.txt | 46 ++ .../samples/grapeFruit/disp-9-bandaid.json | 30 + runtime/samples/grapeFruit/grapeFruit.py | 110 ++++ runtime/samples/grapeFruit/grapeFruit_util.c | 81 +++ runtime/samples/grapeFruit/grapeFruit_util.h | 7 + runtime/samples/grapeFruit/requirements.txt | 2 + runtime/samples/grapeFruit/ts-answer.json | 1 + runtime/samples/nsnet2/0-40-100.json | 27 + runtime/samples/nsnet2/CMakeLists.txt | 7 +- runtime/samples/nsnet2/NsNet2.py | 3 + runtime/samples/nsnet2/nsnet2_util.c | 10 + runtime/snitch_cluster/CMakeLists.txt | 1 + runtime/tests/CMakeLists.txt | 4 + 88 files changed, 4783 insertions(+), 7 deletions(-) create mode 100644 codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp create mode 100644 codegen/compiler/src/Quidditch/Target/TilingScheme.cpp create mode 100644 codegen/compiler/src/Quidditch/Target/TilingScheme.h create mode 100644 comparing-tile-sizes/.gitignore create mode 100644 comparing-tile-sizes/README.md create mode 100644 comparing-tile-sizes/cmakelist-epilogue.txt create mode 100644 comparing-tile-sizes/cmakelist-middle-original.txt create mode 100644 comparing-tile-sizes/cmakelist-prologue.txt create mode 100644 comparing-tile-sizes/compileGrapefruits.sh create mode 100644 comparing-tile-sizes/context.png create mode 100644 comparing-tile-sizes/ex_1x600x600wm-n-k_case1_searchSpace.csv create mode 100644 comparing-tile-sizes/ex_mini.csv create mode 100644 comparing-tile-sizes/generateTileSizeJSONFiles.py create mode 100644 comparing-tile-sizes/merge.py create mode 100644 comparing-tile-sizes/old-tiling-schemes/0-40-100.json create mode 100644 comparing-tile-sizes/runGrapefruits.sh create mode 100644 comparing-tile-sizes/run_experiment.sh create mode 100644 comparing-tile-sizes/scrapeGrapefruits.sh create mode 100644 comparing-tile-sizes/scrutinizeGrapefruits.sh create mode 100644 myrtle/.gitignore create mode 100644 myrtle/LICENSE create mode 100644 myrtle/README.md create mode 100644 myrtle/myrtle/__init__.py create mode 100644 myrtle/myrtle/graphing/__init__.py create mode 100644 myrtle/myrtle/graphing/context2.png create mode 100644 myrtle/myrtle/graphing/graph_utils.py create mode 100644 myrtle/myrtle/graphing/graphing-refactored.py create mode 100644 myrtle/myrtle/myrtle.py create mode 100644 myrtle/myrtle/tile_SA/__init__.py create mode 100644 myrtle/myrtle/tile_SA/quidditch_load_counting.py create mode 100644 myrtle/myrtle/tile_SA/utils.py create mode 100644 myrtle/myrtle/tile_gen/__init__.py create mode 100644 myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py create mode 100644 myrtle/myrtle/tile_gen/tile_size_generator.py create mode 100644 myrtle/myrtle/tile_sel/README.md create mode 100644 myrtle/myrtle/tile_sel/dispatch-8-svr.pickle create mode 100644 myrtle/myrtle/tile_sel/linesOfBestFit.pickle create mode 100644 myrtle/pyproject.toml create mode 100644 myrtle/requirements.txt create mode 100644 myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv create mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv create mode 100644 myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv create mode 100644 myrtle/test_output.json create mode 100644 runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt create mode 100644 runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c create mode 100644 runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h create mode 100644 runtime/samples/.gitignore create mode 100644 runtime/samples/fakeNN/CMakeLists.txt create mode 100644 runtime/samples/fakeNN/fakeNN.py create mode 100644 runtime/samples/fakeNN/fakeNN_util.c create mode 100644 runtime/samples/fakeNN/fakeNN_util.h create mode 100644 runtime/samples/fakeNN/requirements.txt create mode 100644 runtime/samples/fakeNN/ts-answer.json create mode 100644 runtime/samples/grapeFruit/CMakeLists.txt create mode 100644 runtime/samples/grapeFruit/disp-9-bandaid.json create mode 100644 runtime/samples/grapeFruit/grapeFruit.py create mode 100644 runtime/samples/grapeFruit/grapeFruit_util.c create mode 100644 runtime/samples/grapeFruit/grapeFruit_util.h create mode 100644 runtime/samples/grapeFruit/requirements.txt create mode 100644 runtime/samples/grapeFruit/ts-answer.json create mode 100644 runtime/samples/nsnet2/0-40-100.json diff --git a/.gitmodules b/.gitmodules index a711236e..975b8b13 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "xdsl"] path = xdsl url = https://github.com/xdslproject/xdsl +[submodule "myrtle"] + path = myrtle + url = https://github.com/CAPS-UMU/myrtle.git diff --git a/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp b/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp index 063bae69..c076a58f 100644 --- a/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp +++ b/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp @@ -176,6 +176,24 @@ struct ComputeCoreIndexOpLowering : ConvertOpToLLVMPattern { } }; +struct MyrtleRecordCyclesOpLowering : ConvertOpToLLVMPattern { + + LLVM::LLVMFuncOp myrtleRecordCyclesFunc; + + MyrtleRecordCyclesOpLowering(LLVM::LLVMFuncOp myrtleRecordCyclesFunc, + const LLVMTypeConverter &converter) + : ConvertOpToLLVMPattern(converter), + myrtleRecordCyclesFunc(myrtleRecordCyclesFunc) {} + LogicalResult + matchAndRewrite(MyrtleRecordCyclesOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(op, myrtleRecordCyclesFunc, + ValueRange({op.getI(), op.getJ()})); + return success(); + } +}; + + } // namespace void quidditch::populateSnitchToLLVMConversionPatterns( @@ -183,15 +201,26 @@ void quidditch::populateSnitchToLLVMConversionPatterns( RewritePatternSet &patterns) { auto builder = OpBuilder::atBlockEnd(moduleOp.getBody()); + // snrt cluster core idx IntegerType i32 = builder.getI32Type(); auto computeCoreIndex = builder.create( builder.getUnknownLoc(), "snrt_cluster_core_idx", LLVM::LLVMFunctionType::get(i32, ArrayRef{})); computeCoreIndex->setAttr("hal.import.bitcode", builder.getUnitAttr()); + // myrtle record cycles + auto myrtleFnType = LLVM::LLVMFunctionType::get( + builder.getType(), + {i32, i32}, + /*isVarArg=*/false); + auto myrtleRecordCycles = builder.create( + builder.getUnknownLoc(), "myrtle_record_cycles", + myrtleFnType); + myrtleRecordCycles->setAttr("hal.import.bitcode", builder.getUnitAttr()); patterns.insert(typeConverter); patterns.insert(computeCoreIndex, typeConverter); + patterns.insert(myrtleRecordCycles, typeConverter); patterns.insert(SymbolTable(moduleOp), typeConverter); } diff --git a/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp b/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp index 2ecd87ac..b66c959b 100644 --- a/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp +++ b/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp @@ -31,6 +31,8 @@ class ConvertToRISCV private: FailureOr convertToRISCVAssembly(MemRefMicrokernelOp kernelOp, StringAttr kernelName); + FailureOr convertToRISCVAssemblyMyrtle(MemRefMicrokernelOp kernelOp, + StringAttr kernelName); }; } // namespace @@ -53,6 +55,97 @@ static Type transformType(Type type) { strideReplacement, memRefType.getMemorySpace()); } +FailureOr +ConvertToRISCV::convertToRISCVAssemblyMyrtle(MemRefMicrokernelOp kernelOp, + StringAttr kernelName) { + if (!llvm::all_of(kernelOp.getBody().getArgumentTypes(), + CallMicrokernelOp::supportsArgumentType)) { + auto emit = assertCompiled ? &MemRefMicrokernelOp::emitError + : &MemRefMicrokernelOp::emitWarning; + + (kernelOp.*emit)("function inputs ") + << kernelOp.getBody().getArgumentTypes() + << " do not support bare-pointer calling convention required by " + "xDSL."; + return failure(); + } + + SmallVector argumentTypes = + llvm::map_to_vector(kernelOp.getBody().getArgumentTypes(), transformType); + + OpBuilder builder(&getContext()); + OwningOpRef tempFuncOp = + builder.create(kernelOp.getLoc(), kernelName, + builder.getFunctionType(argumentTypes, {})); + IRMapping mapping; + kernelOp.getBody().cloneInto(&tempFuncOp->getBody(), mapping); + for (BlockArgument argument : tempFuncOp->getArguments()) + argument.setType(transformType(argument.getType())); + + builder.setInsertionPointToEnd(&tempFuncOp->getBody().back()); + + builder.create(kernelOp.getLoc()); + + SmallString<64> stdinFile; + int stdinFd; + if (llvm::sys::fs::createTemporaryFile("xdsl-in", "mlir", stdinFd, stdinFile)) + return failure(); + + llvm::FileRemover stdinFileRemove(stdinFile); + { + llvm::raw_fd_ostream ss(stdinFd, /*shouldClose=*/true); + tempFuncOp->print(ss, OpPrintingFlags().useLocalScope()); + } + + SmallString<64> stdoutFile; + if (llvm::sys::fs::createTemporaryFile("xdsl-out", "S", stdoutFile)) + return failure(); + + llvm::FileRemover stdoutFileRemove(stdoutFile); + + SmallString<64> stderrFile; + if (llvm::sys::fs::createTemporaryFile("xdsl-diag", "S", stderrFile)) + return failure(); + + llvm::FileRemover stderrFileRemove(stderrFile); + + std::optional redirects[3] = {/*stdin=*/stdinFile.str(), + /*stdout=*/stdoutFile.str(), + /*stderr=*/stderrFile.str()}; + int ret = llvm::sys::ExecuteAndWait( + xDSLOptPath, + {xDSLOptPath, "-p", + "arith-add-fastmath," + "convert-linalg-to-memref-stream," + "test-optimise-memref-stream," // NOLINT(*-suspicious-missing-comma) + "test-lower-memref-stream-to-snitch-stream," + "test-lower-snitch-stream-to-asm", + "-t", "riscv-asm"}, + std::nullopt, redirects); + + + if (ret != 0) { + auto diagEmit = + assertCompiled ? &Operation::emitError : &Operation::emitWarning; + + InFlightDiagnostic diag = + ((kernelOp)->*diagEmit)("Failed to translate kernel with xDSL"); + + if (llvm::ErrorOr> buffer = + llvm::MemoryBuffer::getFile(stderrFile, /*IsText=*/true)) + diag.attachNote() << "stderr:\n" << buffer.get()->getBuffer(); + + return diag; + } + + llvm::ErrorOr> buffer = + llvm::MemoryBuffer::getFile(stdoutFile, /*IsText=*/true); + if (!buffer) + return kernelOp.emitError("failed to open ") << stdoutFile; + + return StringAttr::get(&getContext(), (*buffer)->getBuffer()); +} + FailureOr ConvertToRISCV::convertToRISCVAssembly(MemRefMicrokernelOp kernelOp, StringAttr kernelName) { @@ -145,6 +238,7 @@ ConvertToRISCV::convertToRISCVAssembly(MemRefMicrokernelOp kernelOp, void ConvertToRISCV::runOnOperation() { ModuleOp module = getOperation(); SymbolTable symbolTable(module); + // module.emitWarning() << "Trying to see if my snitch instruction survived! TURKEY\n"; std::size_t kernelIndex = 0; module.walk([&](MemRefMicrokernelOp kernelOp) { @@ -156,8 +250,12 @@ void ConvertToRISCV::runOnOperation() { FailureOr riscvAssembly = convertToRISCVAssembly(kernelOp, kernelName); + // FailureOr riscvAssembly = + // convertToRISCVAssemblyMyrtle(kernelOp, kernelName); // uncomment this line to see the streaming region and repeat value if (failed(riscvAssembly)) { + // module->emitWarning()<<"\nRADDISH: (convertToRISCV) convert to RISCV assembly failed\n"; if (assertCompiled) { + // module->emitWarning()<<"\nRADDISH: (convertToRISCV) assertion that I compiled ALSO failed\n"; signalPassFailure(); return WalkResult::interrupt(); } @@ -165,6 +263,7 @@ void ConvertToRISCV::runOnOperation() { auto builder = IRRewriter(kernelOp); builder.inlineBlockBefore(&kernelOp.getBody().front(), kernelOp, kernelOp.getInputs()); + //module->emitWarning()<<"\nRADDISH: (convertToRISCV) erasing the kernel op and continuing on...\n"; kernelOp.erase(); return WalkResult::advance(); } @@ -174,6 +273,19 @@ void ConvertToRISCV::runOnOperation() { builder.create(kernelOp.getLoc(), kernelName, kernelOp.getInputs(), *riscvAssembly); kernelOp.erase(); + //module.emitWarning() << "\nTURKEY\n does this show the riscv?"; // delete later return WalkResult::advance(); }); + + // uncomment this module walk to get the riscv for the operation vvvvvvvvvv + // module.walk([&](mlir::func::FuncOp funcOp) { // delete later + // if(funcOp.getName() == + // "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64"){ + + // funcOp->emitWarning() << "\nIs this the riscv we're looking for?"; + // // This is the rewritten kernel!!!!!\n"; + + // } + // }); //delete later + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp index 143cabf6..50fa4ff2 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp @@ -410,6 +410,15 @@ void ComputeCoreIndexOp::replaceWithNoop(RewriterBase &rewriter) { rewriter.replaceOpWithNewOp(*this, 0); } +//===----------------------------------------------------------------------===// +// MyrtleRecordCyclesOp::DMACoreSpecializationOpInterface +//===----------------------------------------------------------------------===// + +void MyrtleRecordCyclesOp::replaceWithNoop(RewriterBase &rewriter) { + // Not sure this is how I should implement this function for MyrtleRecordCycles + rewriter.eraseOp(*this); +} + //===----------------------------------------------------------------------===// // PipelineOp //===----------------------------------------------------------------------===// diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td index 86122955..20265060 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td @@ -214,6 +214,45 @@ def QuidditchSnitch_ComputeCoreIndexOp }]; } +// AllTypesMatch<["init_args", "results"] +// let arguments = (ins +// Index:$lower_bound, +// Index:$upper_bound, +// Index:$step, +// Variadic:$init_args +// ); +//attr-dict $tensor_type `->` type($results) + // let assemblyFormat = [{ + // $lhs `,` $rhs `,` $acc attr-dict + // `:` type($lhs) `,` type($rhs) `into` type($acc) + // }]; + +def QuidditchSnitch_MyrtleRecordCyclesOp + : QuidditchSnitch_Op<"myrtle_record_cycles", [AllTypesMatch<["i", "j"]>, + QuidditchSnitch_DMACoreSpecializationOpInterface]> { + + let description = [{ + Saves current number of cycles in global array myrtle_actual_cycles[i][j], + where i represents a specific kernel, and j represents whether we are + at the start or end of that kernel. + }]; + + let arguments = (ins + I32:$i, + I32:$j + ); + + // let results = (outs Index:$result); + + let assemblyFormat = [{ + $i `,` $j attr-dict + }]; + + let extraClassDeclaration = [{ + void replaceWithNoop(mlir::RewriterBase& rewriter); + }]; +} + def QuidditchSnitch_PipelineOp : QuidditchSnitch_Op<"pipeline", [AllTypesMatch<["init_args", "results"]>, RecursivelySpeculatable, RecursiveMemoryEffects, diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td index 015ae863..0da881eb 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td @@ -77,7 +77,16 @@ def SpecializeDMACodePass : Pass<"quidditch-specialize-dma-code", removed while the original version has all DMA transfer operations removed. Barriers are inserted where data dependencies require either transfers or computations to have finished. + + If the timeDispatch option is enabled, calls to myrtle_record_cycles will be + inserted at the beginning and end of the specified "dma" version. }]; + + let options = [ + Option<"timeDispatch", "time-dispatch", "std::string", /*default=*/"", + "Flag to time a dispatch. \'grapeFruit\' for every NsNet2 dispatch," + " or \'fakennMxNxK\' where M,N,K are integers"> + ]; } def LowerForallOpPass : Pass<"quidditch-lower-forall-op"> { diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp index b7926ee0..fdfcf260 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp @@ -16,11 +16,15 @@ class SpecializeDMACode SpecializeDMACode> { public: using Base::Base; + SpecializeDMACode(const quidditch::Snitch::SpecializeDMACodePassOptions &options) { + this->timeDispatch = options.timeDispatch; + } protected: void runOnOperation() override; private: +std::string timeDispatch = ""; }; } // namespace @@ -55,6 +59,89 @@ static void insertBarriers(FunctionOpInterface function) { }); } +static int myrtleKernelIndex(FunctionOpInterface funcOp, std::string timeDispatch) { + // check if timeDispatch setting is set to fakennMxNxK where M, N, K, are integers + if(timeDispatch.substr (0,6) == "fakenn"){ + std::string splittable = timeDispatch.substr(6,std::string::npos); + std::string onlyDisp = "main$async_dispatch_0_matmul_transpose_b_"+splittable+"_f64$dma"; + if (funcOp.getName() == + onlyDisp) { + return 0; + } + return -1; + + } + // use the below version for grapeFruit setting (timing 5 nsnet kernels) VVV + if(timeDispatch == "grapeFruit"){ + if (funcOp.getName() == + "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64$dma") { + return 0; + } + if (funcOp.getName() == + "main$async_dispatch_0_matmul_transpose_b_1x400x161_f64$dma") { + return 1; + } + if (funcOp.getName() == + "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64$dma") { + return 2; + } + if (funcOp.getName() == + "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64$dma") { + return 3; + } + if (funcOp.getName() == + "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64$dma") { + return 4; + } + return -1; + } + // use the above version for grapeFruit (timing 5 nsnet kernels) ^^^ + // otherwise return failure + return -1; +} + +static bool insertMyrtleRecordCycles(FunctionOpInterface function,std::string timeDispatch) { + // only time functions that we care about + int kernelIndex = myrtleKernelIndex(function, timeDispatch); + if (kernelIndex == -1) { + return false; + } + int first = 0; + // find the first operation and insert record_cycles + for (auto &block : function) { + for (auto &operation : block) { + first++; + if (first == 1) { + OpBuilder builder(operation.getContext()); + builder.setInsertionPoint(&operation); + Type i32Type = builder.getIntegerType(32); + Value i = builder.create( + operation.getLoc(), builder.getIntegerAttr(i32Type, kernelIndex)); + Value j = builder.create( + operation.getLoc(), builder.getIntegerAttr(i32Type, 0)); + builder.create(operation.getLoc(), i, j); + } + } + } + // find the last operation, and insert record_cycles + for (FunctionOpInterface::reverse_iterator it = function.rbegin(), + e = function.rend(); + it != e; ++it) { + OpBuilder builder(it->back().getContext()); + + builder.setInsertionPoint(&it->back()); + Type i32Type = builder.getIntegerType(32); + Value i = builder.create( + it->back().getLoc(), builder.getIntegerAttr(i32Type, kernelIndex)); + Value j = builder.create( + it->back().getLoc(), builder.getIntegerAttr(i32Type, 1)); + builder.create(it->back().getLoc(), i, j); + + break; + } + return true; +} + void SpecializeDMACode::runOnOperation() { auto *dialect = getContext().getLoadedDialect(); SymbolTable table(getOperation()); @@ -68,6 +155,11 @@ void SpecializeDMACode::runOnOperation() { FunctionOpInterface clone = function.clone(); clone.setName((clone.getName() + "$dma").str()); + // try to insert a call to our new myrtle_record_cycles function + // only insert timing functions if time-dispatch option has been enabled + if (timeDispatch != ""){ + insertMyrtleRecordCycles(clone,timeDispatch); + } table.insert(clone, std::next(function->getIterator())); dialect->getDmaSpecializationAttrHelper().setAttr( function, FlatSymbolRefAttr::get(clone)); diff --git a/codegen/compiler/src/Quidditch/Target/CMakeLists.txt b/codegen/compiler/src/Quidditch/Target/CMakeLists.txt index b3f93569..030c8cf0 100644 --- a/codegen/compiler/src/Quidditch/Target/CMakeLists.txt +++ b/codegen/compiler/src/Quidditch/Target/CMakeLists.txt @@ -20,15 +20,18 @@ iree_cc_library( HDRS "Passes.h" "Passes.h.inc" + "TilingScheme.h" SRCS "ConvertToLLVM.cpp" "ConfigureForSnitch.cpp" + "ConfigureTiles.cpp" "DisableQuidditchVariant.cpp" "LinkExecutables.cpp" "PadToTilingConfig.cpp" "ReluToMax.cpp" "RemoveTrivialLoops.cpp" "TensorTile.cpp" + "TilingScheme.cpp" DEPS ::PassesIncGen Quidditch::Conversion::ConvertSnitchToLLVM diff --git a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp new file mode 100644 index 00000000..c3657f73 --- /dev/null +++ b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp @@ -0,0 +1,191 @@ +#include "Passes.h" + +#include +#include +#include "Myrtle.h" +#include "Quidditch/Dialect/Snitch/IR/QuidditchSnitchAttrs.h" +#include "TilingScheme.h" +#include "iree/compiler/Codegen/Dialect/Codegen/IR/IREECodegenAttrs.h" +#include "iree/compiler/Codegen/Utils/CPUUtils.h" +#include "iree/compiler/Codegen/Utils/Utils.h" +#include "iree/compiler/Dialect/HAL/IR/HALOps.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/Dialect/MemRef/Transforms/Transforms.h" +#include "mlir/Interfaces/FunctionInterfaces.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" + +namespace quidditch { +#define GEN_PASS_DEF_CONFIGURETILES +#include "Quidditch/Target/Passes.h.inc" +} // namespace quidditch + +using namespace mlir; +using namespace mlir::iree_compiler; + +namespace { + +class ConfigureTiles + : public quidditch::impl::ConfigureTilesBase { +public: + using Base::Base; + ConfigureTiles(const quidditch::ConfigureTilesOptions &options) { + this->importTiles = options.importTiles; + this->myrtleOut = options.myrtleOut; + this->myrtleMode = options.myrtleMode; + this->myrtlePath = options.myrtlePath; + this->tbl = (quidditch::TileInfoTbl *)options.tablePointer; + } + std::string errs = ""; + +protected: + void runOnOperation() override; + +private: + std::string importTiles = ""; + std::string myrtlePath = ""; + std::string myrtleMode = ""; + std::string myrtleOut = ""; + int acc = 0; + quidditch::TileInfoTbl *tbl; +}; +} // namespace + +static LogicalResult setTranslationInfo(FunctionOpInterface funcOp) { + return setTranslationInfo( + funcOp, + IREE::Codegen::TranslationInfoAttr::get( + funcOp.getContext(), + IREE::Codegen::DispatchLoweringPassPipeline::None, SymbolRefAttr())); +} + +static LogicalResult +setRootConfig(FunctionOpInterface funcOp, Operation *rootOp, + quidditch::TileInfoTbl *tbl, std::string myrtlePath, + std::string myrtleMode, std::string myrtleOut) { + return TypeSwitch(rootOp) + .Case([&](linalg::LinalgOp op) { + std::string tileSizesPath = myrtleOut; + std::string dispatchName = funcOp.getName().str(); + // if myrtle enabled, automatically generate tile sizes + if (myrtlePath != "") { + pid_t pid = fork(); + if (pid == 0) { + char *intrepreter = (char *)"python3"; + char *pythonPath = (char *)myrtlePath.c_str(); + char *pythonArgs[] = {intrepreter, + pythonPath, + (char *)dispatchName.c_str(), + (char *)myrtleMode.c_str(), + (char *)tileSizesPath.c_str(), + NULL}; + execvp(intrepreter, pythonArgs); + } + int status; + wait(&status); + if(status != 0){ + funcOp.emitWarning() << "\nMyrtle Failed with exit status "< workgroupTiles(3, 0); + SmallVector l1Tiles(3, 0); + SmallVector l1Interchange = {2, 0, 1}; + bool dualBuffer = false; + // if table of tiling schemes is invalid, throw an error + if (tbl == 0) { + funcOp.emitWarning() << "\nConfigureTiles: Table pointer is zero!!"; + return failure(); + } + // look up the tile size, interchange, and double buffering settings + // from table + auto search = tbl->find(funcOp.getName().str()); + if (search == tbl->end()) { + funcOp.emitWarning() + << "\nConfigureTiles: Root operation of this function " + "is missing tiling scheme!"; + return failure(); + } + quidditch::TilingScheme &ts = search->second; + if (!ts.getTiles_flat(l1Tiles)) { + funcOp.emitWarning() << "\nConfigureTiles: Found tiling scheme, but " + "couldn't get l1 tile list!"; + return failure(); + } + if (!ts.getOrder_flat(l1Interchange)) { + funcOp.emitWarning() << "\nConfigureTiles: Found tiling scheme, but " + "couldn't get l1 interchange!"; + return failure(); + } + dualBuffer = ts.getDualBuffer(); + // set lowering config according to info in table + setLoweringConfig(rootOp, quidditch::Snitch::LoweringConfigAttr::get( + rootOp->getContext(), workgroupTiles, + l1Tiles, l1Interchange, dualBuffer)); + return success(); + }) + .Default(success()); +} + +void ConfigureTiles::runOnOperation() { + if (importTiles == "" && myrtlePath == "") { + // skip this pass when no arguments passed + return; + } + + FunctionOpInterface funcOp = getOperation(); + + // TODO: un-comment out check for translationInfo, instead of blindly + // overwriting it. + if (getTranslationInfo(funcOp)) + return; + + SmallVector computeOps = getComputeOps(funcOp); + FailureOr rootOp = getRootOperation(computeOps); + + if (failed(rootOp)) { + return signalPassFailure(); + } + + Operation *rootOperation = rootOp.value(); + if (!rootOperation) { + return; + } + + // Set the same translation info for all functions right now. + // This should move into 'setRootConfig' if we gain different pass pipelines + // for different kernels. + if (failed(setTranslationInfo(funcOp))) { + return signalPassFailure(); + } + + // annotate linalg ops with tile sizes + auto loweringConfig = + getLoweringConfig(rootOperation); + // only add the lowering config if one does not exist already + if (!loweringConfig) { + if (failed(setRootConfig(funcOp, rootOperation, tbl, myrtlePath, myrtleMode, + myrtleOut))) { + funcOp.emitWarning() + << "\nConfigureTiles: set root config failed\n"; + return signalPassFailure(); + } + } + + // The root configuration setting introduces `tensor.dim` operations. + // Resolve those away. + RewritePatternSet patterns(funcOp.getContext()); + memref::populateResolveRankedShapedTypeResultDimsPatterns(patterns); + if (failed(applyPatternsAndFoldGreedily(funcOp, std::move(patterns)))) { + funcOp.emitWarning() << "\nConfigureTiles: apply patterns and " + "fold greedily failed\n"; + signalPassFailure(); + } +} \ No newline at end of file diff --git a/codegen/compiler/src/Quidditch/Target/Passes.td b/codegen/compiler/src/Quidditch/Target/Passes.td index bb6e7aa8..51f2cdba 100644 --- a/codegen/compiler/src/Quidditch/Target/Passes.td +++ b/codegen/compiler/src/Quidditch/Target/Passes.td @@ -3,6 +3,7 @@ include "mlir/Pass/PassBase.td" + def LinkExecutablesPass : Pass<"quidditch-link-executables", "mlir::ModuleOp"> { let description = [{ Combines all `hal.executable.variant`s of the same target into a single @@ -29,6 +30,28 @@ def ConfigureForSnitchPass : InterfacePass<"quidditch-configure-for-snitch", "mlir::FunctionOpInterface">; +def ConfigureTiles : InterfacePass<"quidditch-configure-tiles", "mlir::FunctionOpInterface"> { + let summary = "Annotate linalg operations with tile sizes"; + let description = [{ + Within each iree dispatch, annotate the root linalg operation with a tiling scheme (tile sizes + loop interchange). + Caveat: only tiles linalg operations of type matmul_transpose_b (for now) + Set the importTiles option if you would like to specify tile sizes for some (or all) of the dispatches. + Set the myrtlePath, myrtleMode, and myrtleOut options if you would like myrtle to automatically pick the tile sizes. + }]; + let options = [ + Option<"importTiles", "import-tiles", "std::string", /*default=*/"", + "Name of a JSON file specifying loop bounds and order for each root linalg operation.">, + Option<"myrtleMode", "myrtle-mode", "std::string", /*default=*/"", + "Tile selection mode for myrtle: sflt, scyc, or svrcyc">, + Option<"myrtlePath", "myrtle", "std::string", /*default=*/"", + "Path to Myrtle executable">, + Option<"myrtleOut", "myrtle-out", "std::string", /*default=*/"", + "Path to Myrtle output json file">, + Option<"tablePointer", "NeverPassAValueHere", "std::uintptr_t", /*default=*/"0", + "Hacky way to prevent opening input file multiple times. Never pass a value to this option.">, + ]; +} + def TensorTilePass : InterfacePass<"quidditch-tensor-tile", "mlir::FunctionOpInterface"> { let options = [ diff --git a/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp b/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp index f6735592..7a2fc620 100644 --- a/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp +++ b/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp @@ -50,6 +50,9 @@ #include "LibraryBuilder.h" #include "Passes.h" +#include "TilingScheme.h" +#include "llvm/Support/ErrorHandling.h" + using namespace mlir; using namespace mlir::iree_compiler; using namespace quidditch::Snitch; @@ -81,6 +84,14 @@ struct QuidditchTargetOptions { std::string xDSLOptPath; std::string toolChainRoot; bool assertCompiled = false; + std::string timeDispatch = ""; // added for Specialize DMA Code Pass + std::string importTiles = ""; // added for Configure Tiles Pass + std::string myrtleMode = ""; // added for Configure Tiles Pass + std::string myrtlePath = ""; // added for Configure Tiles Pass + std::string myrtleOut = ""; + quidditch::TileInfoTbl tileInfo = + quidditch::TileInfoTbl(); // added for Configure Tiles Pass + std::string tableInfoErrs = "FROG "; // TODO: This should actually be 112640 but DMA stack overflows. Ooopsie! unsigned l1MemoryBytes = 100000; @@ -108,6 +119,26 @@ struct QuidditchTargetOptions { "iree-quidditch-toolchain-root", toolChainRoot, llvm::cl::cat(category), llvm::cl::desc("Path to the root directory of the Quidditch toolchain " "(containing the toolchain file)")); + // added for SpecializeDMACode Pass (to record dispatch cycles for myrtle) + binder.opt( + "iree-quidditch-time-disp", timeDispatch, llvm::cl::cat(category), + llvm::cl::desc("Flag to enable timing dispatches for myrtle")); + // added for Configure Tiles Pass + binder.opt( + "iree-quidditch-import-tiles", importTiles, llvm::cl::cat(category), + llvm::cl::desc( + "Path to a JSON file from which we import tiling schemes")); + binder.opt( + "iree-quidditch-myrtle-mode", myrtleMode, llvm::cl::cat(category), + llvm::cl::desc( + "Choose tile selection method with sflt, scyc, or svrcyc.")); + binder.opt("iree-quidditch-myrtle", myrtlePath, + llvm::cl::cat(category), + llvm::cl::desc("Complete path to myrtle script")); + binder.opt( + "iree-quidditch-myrtle-out", myrtleOut, llvm::cl::cat(category), + llvm::cl::desc( + "Path to json in which myrtle stores its chosen tile sizes.")); binder.opt( "iree-quidditch-assert-compiled", assertCompiled, llvm::cl::cat(category), @@ -173,7 +204,22 @@ class QuidditchTargetBackend final : public IREE::HAL::TargetBackend { } modulePassManager.addPass(createMaterializeUserConfigsPass()); FunctionLikeNest funcPassManager(modulePassManager); - funcPassManager.addPass(quidditch::createConfigureForSnitchPass); + + // import any manually supplied tile sizes + if (targetOptions.importTiles != "") { + std::string errs; + quidditch::fillTileInfoTable(&targetOptions.tileInfo, + targetOptions.importTiles, errs); + } + + // automatically tile the rest of the dispatches + funcPassManager.addPass([&] { + auto thePass = quidditch::createConfigureTiles( + {targetOptions.importTiles, targetOptions.myrtleMode, + targetOptions.myrtlePath, targetOptions.myrtleOut, + (std::uintptr_t)&targetOptions.tileInfo}); + return thePass; + }); } void buildTranslationPassPipeline(IREE::HAL::ExecutableTargetAttr targetAttr, @@ -256,7 +302,8 @@ class QuidditchTargetBackend final : public IREE::HAL::TargetBackend { .addPass(createLinalgGeneralizeNamedOpsPass) .addPass(quidditch::createRemoveTrivialLoopsPass); - modulePassManager.addPass(quidditch::Snitch::createSpecializeDMACodePass()); + modulePassManager.addPass(quidditch::Snitch::createSpecializeDMACodePass( + {targetOptions.timeDispatch})); FunctionLikeNest(modulePassManager) .addPass(quidditch::SnitchDMA::createLegalizeDMAOperationsPass) .addPass(createCanonicalizerPass) diff --git a/codegen/compiler/src/Quidditch/Target/TilingScheme.cpp b/codegen/compiler/src/Quidditch/Target/TilingScheme.cpp new file mode 100644 index 00000000..fb8cff35 --- /dev/null +++ b/codegen/compiler/src/Quidditch/Target/TilingScheme.cpp @@ -0,0 +1,244 @@ +#include "TilingScheme.h" + +// using namespace quidditch; +using namespace mlir; +using namespace mlir::iree_compiler; + +// Tiling Scheme Functions defined below +namespace quidditch { + +TileInfoTbl *fillTileInfoTable(TileInfoTbl *tbl, const std::string &filePath, + std::string &errs) { + TileInfoTbl *result = tbl; + // try to open file + std::ifstream ifs(filePath); + if (!ifs.is_open()) { + std::stringstream ss; + ss << "\nTiling Scheme File does not exist or cannot be opened.\n" + << "Troublesome file path is " << filePath << "\n"; + errs = ss.str(); + return 0; + } + // try to read file + std::stringstream ss; + ss << ifs.rdbuf(); + if (ss.str().length() == 0) { + errs = "\nTiling Scheme file cannot have content length of 0\n"; + ifs.close(); + return 0; + } + // try to parse list of schemes + if (!parseTilingSchemes(tbl, StringRef(ss.str()), errs)) { + result = 0; + } + ifs.close(); + return result; +} + +bool parseTilingSchemes(TileInfoTbl *tbl, llvm::StringRef fileContent, + std::string &errs) { + // try to parse + llvm::Expected maybeParsed = + llvm::json::parse(fileContent); + if (!maybeParsed) { + std::stringstream ss; + ss << "\nError when parsing JSON file contents: " + << llvm::toString(maybeParsed.takeError()) << "\n"; + errs = ss.str(); + return false; + } + // try to get the top level json object + if (!maybeParsed->getAsObject()) { + errs = "\nError: top-level value is not a JSON object\n"; + return false; + } + llvm::json::Object *O = maybeParsed->getAsObject(); + // make sure object has at least one field + if (O->empty()) { + errs = "\nError: top-level JSON object is empty\n"; + return false; + } + // try to parse each tiling scheme from function name key + std::stringstream ss; + for (const auto &func : *O) { + struct TilingScheme ts = parseTilingScheme(func.getSecond(), errs); + if (!ts.valid) { + return false; + } else { + // only insert the tiling scheme if an entry does not exist already + // (NO OVERRIDING KEY-VALUE pairs in the table!!) + auto search = tbl->find(func.getFirst().str()); + if (search == tbl->end()) { + tbl->insert(std::pair(func.getFirst().str(), ts)); + ss << func.getFirst().str() << ":\n"; + ss << ts; + } + } + } + errs = ss.str(); + return true; +} + +struct TilingScheme parseTilingScheme(llvm::json::Value v, std::string &errs) { + + struct TilingScheme ts; + auto O = v.getAsObject(); + if (!O) { + errs = "RHS of key_value pair is not a JSON object!"; + return ts; + } + bool read_tile_sizes = parseListOfListOfInts(O, "tile-sizes", ts.tiles, errs); + bool read_loop_order = parseListOfListOfInts(O, "loop-order", ts.order, errs); + bool read_dual_buffer = parseBool(O, "dual-buffer", ts.dualBuffer, errs); + ts.valid = read_tile_sizes && read_loop_order && read_dual_buffer; + return ts; +} + +// TODO: call parseListOfInts inside parseListOfListOfInts +bool parseListOfListOfInts(llvm::json::Object *obj, std::string listName, + std::vector> &out, + std::string &errs) { + llvm::json::Value *bnds = obj->get(StringRef(listName)); + if (!bnds) { + std::stringstream ss; + ss << "\nError: field labeled '" << listName << "' does not exist \n "; + errs = ss.str(); + return false; + } + + if (!bnds->getAsArray()) { // getAsArray returns a (const json::Array *) + std::stringstream ss; + ss << "\nError: field labeled '" << listName << "' is not a JSON array \n "; + errs = ss.str(); + return false; + } + llvm::json::Path::Root Root("Try-to-parse-integer"); + for (const auto &Item : + *(bnds->getAsArray())) { // loop over a json::Array type + if (!Item.getAsArray()) { + std::stringstream ss; + ss << "\nError: elt of '" << listName << "' is not also a JSON array \n "; + errs = ss.str(); + return false; + } + std::vector sublist; + int bound; + for (const auto &elt : + *(Item.getAsArray())) { // loop over a json::Array type + if (!fromJSON(elt, bound, Root)) { + std::stringstream ss; + ss << llvm::toString(Root.getError()) << "\n"; + errs = ss.str(); + return false; + } + sublist.push_back(bound); + } + out.push_back(sublist); + } + return true; +} + +bool parseListOfInts(llvm::json::Object *obj, std::string listName, + std::vector &out, std::string &errs) { + llvm::json::Value *bnds = obj->get(StringRef(listName)); + if (!bnds) { + std::stringstream ss; + ss << "\nError: field labeled '" << listName << "' does not exist \n "; + errs = ss.str(); + return false; + } + if (!bnds->getAsArray()) { // getAsArray returns a (const json::Array *) + std::stringstream ss; + ss << "\nError: field labeled '" << listName << "' is not a JSON array \n "; + errs = ss.str(); + return false; + } + llvm::json::Path::Root Root("Try-to-parse-integer"); + int theNumber; + for (const auto &elt : + *(bnds->getAsArray())) { // loop over a json::Array type + if (!fromJSON(elt, theNumber, Root)) { + std::stringstream ss; + ss << llvm::toString(Root.getError()) << "\n"; + errs = ss.str(); + return false; + } + out.push_back(theNumber); + } + return true; +} + +bool parseBool(llvm::json::Object *obj, std::string boolName, bool &out, + std::string &errs) { + llvm::json::Value *theBool = obj->get(StringRef(boolName)); + if (!theBool) { + std::stringstream ss; + ss << "\nError: field labeled '" << boolName << "' does not exist \n "; + errs = ss.str(); + return false; + } + if (!theBool->getAsBoolean()) { // getAsBoolean returns an optional boolean + std::stringstream ss; + ss << "\nError: field labeled '" << boolName << "' is not a boolean \n "; + errs = ss.str(); + return false; + } + out = *(theBool->getAsBoolean()); + return true; +} + +bool TilingScheme::getTiles_flat(llvm::SmallVector &out) { + if (out.size() != tiles.size()) { + return false; + } else { + for (size_t i = 0; i < tiles.size(); i++) { + out[i] = (int64_t)tiles[i][0]; + } + } + return true; +} + +bool TilingScheme::getOrder_flat(llvm::SmallVector &out) { + if (out.size() != order.size()) { + return false; + } else { + for (size_t i = 0; i < order.size(); i++) { + out[i] = (int64_t)order[i][0]; + } + } + + return true; +} + +std::string TilingScheme::str() { + std::stringstream ts_ss; + ts_ss << *this; + return ts_ss.str(); +} + +std::stringstream &operator<<(std::stringstream &ss, + const struct TilingScheme &ts) { + ss << "tiling scheme: {\nbounds: [ "; + for (const auto &sublist : ts.tiles) { + ss << "[ "; + for (const auto &tile : sublist) { + ss << " " << tile << " "; + } + ss << "] "; + } + ss << "]\n"; + ss << "order: [ "; + for (const auto &sublist : ts.order) { + ss << "[ "; + for (const auto &pos : sublist) { + ss << " " << pos << " "; + } + ss << "] "; + } + ss << "]\n"; + ss << "dual buffer: " << ts.dualBuffer << "\n"; + ss << "]\n}"; + return ss; +} + +} // namespace quidditch diff --git a/codegen/compiler/src/Quidditch/Target/TilingScheme.h b/codegen/compiler/src/Quidditch/Target/TilingScheme.h new file mode 100644 index 00000000..3ffe3829 --- /dev/null +++ b/codegen/compiler/src/Quidditch/Target/TilingScheme.h @@ -0,0 +1,64 @@ + + +#include +#include +#include // to open tiling scheme file +#include +#include // for string compare +#include // to store parsed tiling schemes in a hash table +#include "Quidditch/Dialect/Snitch/IR/QuidditchSnitchAttrs.h" +#include "iree/compiler/Codegen/Dialect/Codegen/IR/IREECodegenAttrs.h" +#include "iree/compiler/Codegen/Transforms/Transforms.h" +#include "iree/compiler/Codegen/Utils/Utils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" // to parse tiling scheme +#include "mlir/Dialect/Arith/Utils/Utils.h" +#include "mlir/Dialect/Linalg/IR/Linalg.h" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Linalg/Transforms/Transforms.h" +#include "mlir/Dialect/Linalg/Utils/Utils.h" +#include "mlir/Dialect/SCF/Transforms/TileUsingInterface.h" +#include "mlir/IR/Attributes.h" +#include "mlir/Interfaces/FunctionInterfaces.h" +#include "mlir/Transforms/DialectConversion.h" + +namespace quidditch { +// define a struct that stores +// tile size and loop interchange information +// for an iree dispatch's root operation +struct TilingScheme { + bool valid = false; + std::vector> tiles; + std::vector> order; + bool dualBuffer = false; + std::string errs = ""; + // member funcs + TilingScheme() = default; + std::string str(); + bool getTiles_flat(llvm::SmallVector &out); + bool getOrder_flat(llvm::SmallVector &out); + bool getDualBuffer() { return dualBuffer; } + // overloaded output operator + friend std::stringstream &operator<<(std::stringstream &ss, + const struct TilingScheme &ts); +}; + +// define a table that maps +// each iree dispatch function name to its tiling scheme. +typedef std::unordered_map + TileInfoTbl; +// function to read a json file and puts its contents in a TileInfo Table +TileInfoTbl *fillTileInfoTable(TileInfoTbl *tbl, const std::string &filePath, + std::string &errs); +// json parsing helper functions +struct TilingScheme parseTilingScheme(llvm::json::Value v, std::string &errs); +bool parseTilingSchemes(TileInfoTbl *tbl, llvm::StringRef fileContent, + std::string &errs); +bool parseListOfListOfInts(llvm::json::Object *obj, std::string listName, + std::vector> &out, + std::string &errs); +bool parseListOfInts(llvm::json::Object *obj, std::string listName, + std::vector &out, std::string &errs); +bool parseBool(llvm::json::Object *obj, std::string listName, bool &out, + std::string &errs); +} // namespace quidditch \ No newline at end of file diff --git a/comparing-tile-sizes/.gitignore b/comparing-tile-sizes/.gitignore new file mode 100644 index 00000000..ed021232 --- /dev/null +++ b/comparing-tile-sizes/.gitignore @@ -0,0 +1,42 @@ +./*/logs/ +./1x*x*wm-n-k/*/* +./*/cmakeOutput.txt +./*/buildOutput.txt +0-40-100/GrapeFruit +0-40-200/GrapeFruit +0-80-100/GrapeFruit +0-40-120/GrapeFruit +0-48-100/GrapeFruit +0-80-80/GrapeFruit +*/GrapeFruit +tile-sizes-to-test/* +0-24-100/* +0-24-120/* +0-32-100/* +0-40-100/* +0-40-100/* +0-40-80/* +0-48-80/* +0-56-50/* +0-64-50/* +0-64-80/* +0-72-50/* +0-72-80/* +0-80-40/* +0-80-50/* +0-80-80/* +0-80-80/* +0-88-80/* +0-96-40/* +emily-tile-sizes/* +emily-tile-sizes/* +ndb-0-40-100/* +ndb-0-40-120/* +ndb-0-40-200/* +ndb-0-48-100/* +ndb-0-80-100/* +ndb-0-80-80/* +rejected-0-24-200/* +rejected-0-32-120/* +rejected-0-48-100/* +rejected-0-56-80/* diff --git a/comparing-tile-sizes/README.md b/comparing-tile-sizes/README.md new file mode 100644 index 00000000..9aade6f3 --- /dev/null +++ b/comparing-tile-sizes/README.md @@ -0,0 +1,145 @@ +# Specify Tile Sizes for a Particular Linear Layer of NsNet2 + +TODO: Better Diagram + +![](context.png) + +Each PyTorch Linear Layer inside NsNet2 eventually compiles to an IREE dispatch containing two linalg operations: a `linalg.matmul_tranpose_b` and a `linalg.generic` representing an element wise addition. In the case of these dispatches, the root operation is always the `linalg.matmul_tranpose_b` operation. + +**This guide explains how to *automatically*** + +- **COMPILE NsNet2 with a one of these five IREE dispatches customized with your requested tile sizes for tiling its root operation** +- **RUN this uniquely tiled NsNet2 on a verilator simulation of the Snitch Cluster** +- **EXPORT cycle count for each dispatch and the total cycle count to a CSV** +- **REPEAT these steps for many tiling configurations en masse** + +## I. Run NsNet2 with a Dispatch M-N-K using an m-n-k tiling scheme + +Navigate to the `Quidditch/comparing-tile-sizes directory` and do + +``` +. run_experiment.sh +``` + +where + +- `` is the name of the csv file assumed to be located inside directory `Quidditch/comparing-tiles-sizes/`. + + ``` + JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim + 0-24-8,24,8,3136,192,82464,1,45000,720000,360000,120000,240000,1,3,8,15000,3,8 + 0-24-10,24,10,3920,240,81680,1,36000,720000,360000,120000,240000,1,3,10,12000,3,10 + 0-24-12,24,12,4704,288,80896,1,30000,720000,360000,120000,240000,1,3,12,10000,3,12 + ``` + + ^^ example input csv file ^^ + + Each row of the spreadsheet represents a different tiling scheme for the same NsNet2 layer specified by ``. The rest of the layers inside NsNet2 will use a the default scheme defined in `generateTileSizeJSONFiles.py`. See section IV for more details. + + If you only want to run NsNet2 once, just provide a CSV with a single row like in `ex_mini.csv`. + +- `` is a flag to select whether to generate a JSON file for each tiling schemes specified in `searchSpace.csv` + + - `genJsons` enables this step + - `no` skips this step + +- `` is a flag to select whether to compile the fakeNN for each tiling scheme specified + + - `compile` enables this step + - `no` skips this step + - `status` checks for compilation errors + +- `` is a flag to select whether to run the fakeNN executable for each tiling scheme specified + + - `run` enables this step + - `no` skips this step + +- `` is a flag to select whether to export the results of each fakeNN to a csv file + + - `export` enables this step + - `no` skips this step + - `correctness` checks the output of each fakeNN run with the its corresponding golden output file. + +- `caseNo` is a legacy option that should always be passed a value of `1`. + +- `dispatchName` refers to the exact name of the IREE dispatch you would like to tile. There are only 5 dispatches in NsNet2 we can tile. + + | Possible dispatchName Values | + | ------------------------------------------------------------ | + | `"main\$async_dispatch_9_matmul_transpose_b_1x161x600_f64"` | + | `"main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64"` | + | `"main\$async_dispatch_0_matmul_transpose_b_1x400x161_f64"` | + | `"main\$async_dispatch_1_matmul_transpose_b_1x1200x400_f64"` | + | `"main\$async_dispatch_7_matmul_transpose_b_1x600x400_f64"` | + +## II. Example Runs + +It's recommended to perform one of the four steps, check the results, and then proceed to the next step. + +1. generate jsons + + ``` + . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" genJsons no no no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" + ``` + +2. compile + + ``` + . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no compile no no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" + ``` + +3. status (check for compilation errors) + + ``` + . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no status no no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" + ``` + +4. run + + ``` + . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no no run no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" + ``` + +5. export results + + ``` + . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no no no export 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" + ``` + +## IV. Excerpt: Default Tiling Schemes for each Dispatch + +Excerpt from `generateTileSizeJSONFiles.py`: + +``` + # dispatch 1 + node = {} + node["tile-sizes"] = [[0], [40], [100]] + node["loop-order"] = [[2,0], [0,0], [1,0]] + node["dual-buffer"] = True + data["main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64"]=node + # dispatch 0 + node = {} + node["tile-sizes"] = [[0], [40], [0]] + node["loop-order"] = [[2,0], [0,0], [1,0]] + node["dual-buffer"] = False + data["main$async_dispatch_0_matmul_transpose_b_1x400x161_f64"]=node + # dispatch 7 + node = {} + node["tile-sizes"] = [[0], [40], [100]] + node["loop-order"] = [[2,0], [0,0], [1,0]] + node["dual-buffer"] = True + data["main$async_dispatch_7_matmul_transpose_b_1x600x400_f64"]=node + # dispatch 8 + node = {} + node["tile-sizes"] = [[0], [40], [100]] + node["loop-order"] = [[2,0], [0,0], [1,0]] + node["dual-buffer"] = True + data["main$async_dispatch_8_matmul_transpose_b_1x600x600_f64"]=node + # dispatch 9 + node = {} + node["tile-sizes"] = [[0], [56], [100]] + node["loop-order"] = [[2,0], [0,0], [1,0]] + node["dual-buffer"] = True + data["main$async_dispatch_9_matmul_transpose_b_1x161x600_f64"]=node +``` + diff --git a/comparing-tile-sizes/cmakelist-epilogue.txt b/comparing-tile-sizes/cmakelist-epilogue.txt new file mode 100644 index 00000000..d9c979a7 --- /dev/null +++ b/comparing-tile-sizes/cmakelist-epilogue.txt @@ -0,0 +1,42 @@ +add_library(grapeFruit_util grapeFruit_util.c) +target_link_libraries(grapeFruit_util + PRIVATE + samples_util + snRuntimeInterface + Quidditch::dispatch::dispatch + Quidditch::time_dispatch::time_dispatch +) +target_include_directories(grapeFruit_util INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +macro(create_experiment_variant) + cmake_parse_arguments(_RULE "PRECOMMIT;NIGHTLY" "TARGET;IREE_MODULE;QUERY_FUNC" "" ${ARGN}) + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c "\ +#include <${_RULE_IREE_MODULE}.h> + +#include \"grapeFruit_util.h\" + +int main() { + return run_grapeFruit_experiment(${_RULE_QUERY_FUNC}); +} +") + add_executable(${_RULE_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c) + target_link_libraries( + ${_RULE_TARGET} + PRIVATE + grapeFruit_util + ${_RULE_IREE_MODULE} + snRuntime + ) +endmacro() + +create_experiment_variant( + TARGET GrapeFruit + IREE_MODULE grapeFruit + QUERY_FUNC "quidditch_compiled_ns_net2_linked_quidditch_library_query" +) +create_experiment_variant( + TARGET GrapeFruitLLVM + IREE_MODULE grapeFruit_llvm + QUERY_FUNC "compiled_ns_net2_linked_llvm_cpu_library_query" +) \ No newline at end of file diff --git a/comparing-tile-sizes/cmakelist-middle-original.txt b/comparing-tile-sizes/cmakelist-middle-original.txt new file mode 100644 index 00000000..284af60a --- /dev/null +++ b/comparing-tile-sizes/cmakelist-middle-original.txt @@ -0,0 +1,2 @@ +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DST grapeFruit FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc LLVM DST grapeFruit_llvm FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) diff --git a/comparing-tile-sizes/cmakelist-prologue.txt b/comparing-tile-sizes/cmakelist-prologue.txt new file mode 100644 index 00000000..827968d9 --- /dev/null +++ b/comparing-tile-sizes/cmakelist-prologue.txt @@ -0,0 +1,2 @@ +set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle.py) +iree_turbine(SRC grapeFruit.py DST ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DTYPE "f64" M 5 N 6 K 7) diff --git a/comparing-tile-sizes/compileGrapefruits.sh b/comparing-tile-sizes/compileGrapefruits.sh new file mode 100644 index 00000000..98cb7523 --- /dev/null +++ b/comparing-tile-sizes/compileGrapefruits.sh @@ -0,0 +1,123 @@ +echo -e "compileGrapefruits.sh: ATTN: This should only be run by run_experiment.sh" +here=$(pwd) # save current directory so we can return to it +# script-specific constants" +quidditchDir="/home/hoppip/Quidditch" +tileSizes="$quidditchDir/comparing-tile-sizes/tile-sizes-to-test/*.json" +prologueFile="$quidditchDir/comparing-tile-sizes/cmakelist-prologue.txt" +middleFile="$quidditchDir/comparing-tile-sizes/cmakelist-middle-original.txt" +epilogueFile="$quidditchDir/comparing-tile-sizes/cmakelist-epilogue.txt" +searchSpaceCSV="$here/$1" +echo "searchSpaceCSV is $searchSpaceCSV" +compileOutputDirectory="$here/$2" +tileSizesToTest="$here/$2/tile-sizes-to-test" +# build-specific constants +grapefruitDir="$quidditchDir/runtime/samples/grapeFruit" +buildDir="$quidditchDir/build" +grapefruitExec="$buildDir/runtime/samples/grapeFruit/GrapeFruit" +verilator="$quidditchDir/toolchain/bin" + +# debugging +function_name(){ + echo "yohoho $1 $2" +} + +# generate cmakelists.txt file given +# 1. the tile sizes json (as form basename.json) +# 2. directory in which to save the cmakelists.txt file (do NOT use a relative path!) +gen_cmakelists(){ + # echo "" + tile="$tileSizesToTest/$1" + # echo "directory to save in is $2" + if [[ "$1" == "original" ]]; + then + cat $prologueFile > "$2/CMakeLists.txt" + cat $middleFile >> "$2/CMakeLists.txt" + cat $epilogueFile >> "$2/CMakeLists.txt" + else + cat $prologueFile > "$2/CMakeLists.txt" + echo "quidditch_module(SRC \${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DST grapeFruit FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-import-tiles=$tile)" >> "$2/CMakeLists.txt" + echo "quidditch_module(SRC \${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc LLVM DST grapeFruit_llvm FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-import-tiles=$tile)" >> "$2/CMakeLists.txt" + cat $epilogueFile >> "$2/CMakeLists.txt" + fi +} + +## this script requires a search space csv file +res=$(ls $searchSpaceCSV 2>/dev/null) +if [[ $searchSpaceCSV != $res ]]; + then + echo "ERROR: search space file $searchSpaceCSV not found!" + exit 1 +fi + + +if [[ "$3" == "status" ]]; + then + ## check whether each build was successful + for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) + do + basename=$ts # TODO: rename basname as ts everywhere + echo "checking $basename.json build..." # inform user we are checking build associated with $basename.json + grep "kernel does not fit into L1 memory and cannot be compiled" "$compileOutputDirectory/$basename/buildOutput.txt" + grep "Troublesome file path is" "$compileOutputDirectory/$basename/buildOutput.txt" + grep "FAILED: runtime-prefix/src/runtime-stamp/runtime-build" "$compileOutputDirectory/$basename/buildOutput.txt" + cd $here + done + gen_cmakelists "original" $grapefruitDir # generate original CMakeLists.txt + else + echo "compileGrapefruits.sh: generating the cmake files and compiling..." + for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) + do + basename=$ts # TODO: rename basname as ts everywhere + mkdir -p "$compileOutputDirectory/$basename" # create a local subfolder for this set of tile sizes + echo "$basename.json" # inform user we are about to start processing $basename.json + #exportedCostFile="$compileOutputDirectory/$basename/tilingCosts.json" # using full path here + gen_cmakelists "$ts.json" $grapefruitDir # generate basename-specific CMakeLists.txt + gen_cmakelists "$ts.json" "$compileOutputDirectory/$basename" # save a copy of it in our local subfolder + cd $buildDir + cmake .. -GNinja \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DQUIDDITCH_TOOLCHAIN_FILE=../toolchain/ToolchainFile.cmake &> "$compileOutputDirectory/$basename/cmakeOutput.txt" + ninja -j 20 &> "$compileOutputDirectory/$basename/buildOutput.txt" + grep "kernel does not fit into L1 memory and cannot be compiled" "$compileOutputDirectory/$basename/buildOutput.txt" + grep "Troublesome file path is" "$compileOutputDirectory/$basename/buildOutput.txt" + cd $here + # copy generated executable to local folder + cp $grapefruitExec "$compileOutputDirectory/$basename/GrapeFruit" # copy SRC to DST + done + gen_cmakelists "original" $grapefruitDir # generate original CMakeLists.txt + +fi + + + + + + +# old notes below... + +# clear;sh scrapeGrapefruits.sh case1_searchSpace.csv "case_1" +# clear; sh compileGrapefruits.sh case1_searchSpace.csv "case_1" +# sh runGrapefruits.sh case1_searchSpace.csv "case_1" + +# existingExperiments=() +# missingExperiments=() + +# for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) +# do +# experimentResults="$here/$ts/run_output.txt" +# res=$(ls $experimentResults 2>/dev/null) +# if [[ $experimentResults == $res ]]; +# then +# existingExperiments+=("$experimentResults") +# else +# missingExperiments+=("$experimentResults") +# fi +# done +# echo "compileGrapefruits.sh: generating json files from the search space..." +# python generateTileSizeJSONFiles.py $searchSpaceCSV + +# rm --f -R "$compileOutputDirectory" # delete previous outputs +# mkdir "$compileOutputDirectory" # create fresh output folder \ No newline at end of file diff --git a/comparing-tile-sizes/context.png b/comparing-tile-sizes/context.png new file mode 100644 index 0000000000000000000000000000000000000000..0c0b400cb555963b67a77e772db394ae9c8c7735 GIT binary patch literal 71949 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�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/dev/null) +if [[ $searchSpaceCSV != $res ]]; + then + echo "ERROR: search space file $searchSpaceCSV not found!" + exit 1 +fi + + +echo "runGrapefruits.sh: running executables with verilator..." +batchSize=10 +counter=0 + for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) + do + counter=$((counter+1)) + basename=$ts # TODO: rename basname as ts everywhere + myExecutable="$compileOutputDirectory/$basename/GrapeFruit" + echo $myExecutable + cd $verilator + rm -R "$compileOutputDirectory/$basename/logs/" + nohup ./snitch_cluster.vlt $myExecutable &> "$compileOutputDirectory/$basename/run_output.txt" & + cp -r logs "$compileOutputDirectory/$basename/logs" + cd $here + if (( $counter % $batchSize == 0 )); then + wait + echo "starting new batch..." + fi + done +wait &> /dev/null diff --git a/comparing-tile-sizes/run_experiment.sh b/comparing-tile-sizes/run_experiment.sh new file mode 100644 index 00000000..b621e4f3 --- /dev/null +++ b/comparing-tile-sizes/run_experiment.sh @@ -0,0 +1,53 @@ +echo "run_experiment.sh: ATTN: Run this script INSIDE directory Quidditch/comparing-tile-sizes/" +here=$(pwd) # save current directory so we can return to it +# script-specific constants +searchSpaceCSV="$here/$1" +experimentName="$2" +finalOutputDirectory="$here/$experimentName" +jsonOutputDirectory="$here/$experimentName/tile-sizes-to-test" +## this script requires a search space csv file +res=$(ls $searchSpaceCSV 2>/dev/null) +if [[ $searchSpaceCSV != $res ]]; + then + echo "ERROR: search space file $searchSpaceCSV not found!" + exit 1 +fi + +## generate json files +if [[ "$3" == "genJsons" ]]; + then + echo "run_experiment.sh: generating json files from the search space..." + mkdir -p $jsonOutputDirectory + python generateTileSizeJSONFiles.py $1 $8 $jsonOutputDirectory +fi + +## compile +if [[ "$4" == "compile" ]]; + then + ## compile + . compileGrapefruits.sh $1 $experimentName + ## check compilation results + else if [[ "$4" == "status" ]]; + then + . compileGrapefruits.sh $1 $experimentName status + fi +fi + +## run +if [[ "$5" == "run" ]]; + then + . runGrapefruits.sh $1 $experimentName +fi + + +## export +if [[ "$6" == "correctness" ]]; + then + . scrutinizeGrapefruits.sh $1 $experimentName $7 $8 +fi +if [[ "$6" == "export" ]]; + then + . scrapeGrapefruits.sh $1 $experimentName $7 $8 +fi + + diff --git a/comparing-tile-sizes/scrapeGrapefruits.sh b/comparing-tile-sizes/scrapeGrapefruits.sh new file mode 100644 index 00000000..fbbc4850 --- /dev/null +++ b/comparing-tile-sizes/scrapeGrapefruits.sh @@ -0,0 +1,102 @@ +echo "scrapeGrapefruits.sh: ATTN: NEVER run this script directly; instead, call it from run_experiment.sh" +here=$(pwd) # save current directory so we can return to it +# script-specific constants +quidditchDir="/home/hoppip/Quidditch" +tileSizes="$quidditchDir/comparing-tile-sizes/tile-sizes-to-test/*.json" +prologueFile="$quidditchDir/comparing-tile-sizes/cmakelist-prologue.txt" +middleFile="$quidditchDir/comparing-tile-sizes/cmakelist-middle-original.txt" +epilogueFile="$quidditchDir/comparing-tile-sizes/cmakelist-epilogue.txt" +scrapeName="$2" +parsedResultsCSV="$here/$scrapeName/csv_experiment_results.csv" +searchSpaceCSV="$here/$1" +# build-specific constants +grapefruitDir="$quidditchDir/runtime/samples/grapeFruit" +buildDir="$quidditchDir/build" +grapefruitExec="$buildDir/runtime/samples/grapeFruit/GrapeFruit" +verilator="$quidditchDir/toolchain/bin" + +## debugging +function_name(){ + echo "yohoho $1" +} + +## helper function +parse_exp_result(){ + filePath=$1 + dispatchNo=$2 + dispatchName=$3 + basename=`basename $(echo $filePath | sed 's/run_output.txt//') | sed 's/[.][^.]*$//'` + kernelTime=$(grep -E "^(dispatch) $dispatchNo: ([0-9]*) - ([0-9]*) = ([0-9]*)" "$filePath" | grep -oE '[^[:space:]]+$') + totalTime=$(grep -E "cycles ([0-9]*)" "$filePath" | grep -oE '[^[:space:]]+$') + echo "$basename,$dispatchName,$kernelTime,$totalTime" >> $parsedResultsCSV +} + +## this script requires a search space csv file +res=$(ls $searchSpaceCSV 2>/dev/null) +if [[ $searchSpaceCSV != $res ]]; + then + echo "ERROR: search space file $searchSpaceCSV not found!" + exit 1 +fi + +existingExperiments=() +missingExperiments=() + +## scrape experiments from the search space +for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) + do + experimentResults="$here/$scrapeName/$ts/run_output.txt" + res=$(ls $experimentResults 2>/dev/null) + if [[ $experimentResults == $res ]]; + then + existingExperiments+=("$experimentResults") + else + missingExperiments+=("$experimentResults") + fi +done + + +echo "we will skip the following missing experiments:" +for element in "${missingExperiments[@]}" +do + echo $element +done +echo "we will export the following experiment results to a csv:" +for element in "${existingExperiments[@]}" +do + echo $element +done + +## generate fresh CSV output file +rm "$parsedResultsCSV" +rm "$here/$scrapeName/graphing.csv" +# rmdir "$here/$scrapeName" +# mkdir "$here/$scrapeName" +touch "$parsedResultsCSV" +echo "JSON Name,Kernel Name,Kernel Time,Total Time" >> $parsedResultsCSV + +## parse each experiment's run_output.txt +## and append the parsed info to the CSV file +for element in "${existingExperiments[@]}" +do + parse_exp_result $element $3 $4 +done + +## merge search space info with parsed results for graphing +python merge.py $searchSpaceCSV $parsedResultsCSV "JSON Name" +cp "merged.csv" "$here/$scrapeName/graphing.csv" +rm "merged.csv" + + + + + +# notes below! +# some helpful grep patterns to remember, +# even if not all of them used in current script: +# echo "third grep" +# grep -E "(:alpha:|[0-9]*) = [0-9]*" "$filePath" +# echo "fourth grep" +# grep -E "^(dispatch) ([0-9]*): ([0-9]*) - ([0-9]*) = ([0-9]*)" "$filePath" +# echo "fifth grep" +# grep -E "cycles ([0-9]*)" "$filePath" \ No newline at end of file diff --git a/comparing-tile-sizes/scrutinizeGrapefruits.sh b/comparing-tile-sizes/scrutinizeGrapefruits.sh new file mode 100644 index 00000000..59ecb9fe --- /dev/null +++ b/comparing-tile-sizes/scrutinizeGrapefruits.sh @@ -0,0 +1,88 @@ +echo "scrutinizeGrapefruits.sh: ATTN: Run this script INSIDE directory Quidditch/comparing-tile-sizes/" +here=$(pwd) # save current directory so we can return to it +# script-specific constants +quidditchDir="/home/hoppip/Quidditch" +tileSizes="$quidditchDir/comparing-tile-sizes/tile-sizes-to-test/*.json" +prologueFile="$quidditchDir/comparing-tile-sizes/cmakelist-prologue.txt" +middleFile="$quidditchDir/comparing-tile-sizes/cmakelist-middle-original.txt" +epilogueFile="$quidditchDir/comparing-tile-sizes/cmakelist-epilogue.txt" +scrapeName="$2" +parsedResultsCSV="$here/$scrapeName/csv_experiment_results.csv" +searchSpaceCSV="$here/$1" +goldenOutputFile="$quidditchDir/comparing-tile-sizes/golden_output.txt" +# build-specific constants +grapefruitDir="$quidditchDir/runtime/samples/grapeFruit" +buildDir="$quidditchDir/build" +grapefruitExec="$buildDir/runtime/samples/grapeFruit/GrapeFruit" +verilator="$quidditchDir/toolchain/bin" + +## debugging +function_name(){ + echo "yohoho $1" +} + +## helper function +parse_exp_result(){ + filePath=$1 + runOutputJustValues="./temp.txt" + rm $runOutputJustValues 2> /dev/null + lineCount=$(wc --lines $filePath | head -n1 | sed -e 's/\s.*$//') + theTail=$(($lineCount - 1)) + theHead=$(($lineCount - 19)) + tail -n $theTail $filePath | head -n $theHead > $runOutputJustValues + diffResult=$(diff $goldenOutputFile $runOutputJustValues) + if [[ $diffResult != "" ]]; + then + echo "ERROR: $filePath contains incorrect results!" + echo $diffResult + else + echo "$filePath OK" + fi +} + +## this script requires a search space csv file +res=$(ls $searchSpaceCSV 2>/dev/null) +if [[ $searchSpaceCSV != $res ]]; + then + echo "ERROR: search space file $searchSpaceCSV not found!" + exit 1 +fi + +existingExperiments=() +missingExperiments=() + + +## scrape experiments from the search space +for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) + do + experimentResults="$here/$scrapeName/$ts/run_output.txt" + res=$(ls $experimentResults 2>/dev/null) + if [[ $experimentResults == $res ]]; + then + existingExperiments+=("$experimentResults") + else + missingExperiments+=("$experimentResults") + fi +done + + +echo "we will skipt the following missing experiments:" +for element in "${missingExperiments[@]}" +do + echo $element +done +echo "we will check the following experiments for correctness:" +for element in "${existingExperiments[@]}" +do + echo $element +done + +echo "checking..." + +## parse each experiment's run_output.txt +## and check that it matches golden reference +for element in "${existingExperiments[@]}" +do + parse_exp_result $element $3 $4 #looks to me like we can remove the last two args from this statement +done + diff --git a/myrtle/.gitignore b/myrtle/.gitignore new file mode 100644 index 00000000..7c14388b --- /dev/null +++ b/myrtle/.gitignore @@ -0,0 +1,4 @@ +**/__pycache__ +myrtle.egg-info/* +uv.lock +1x600x400wm-n-k_case1_searchSpace.csv \ No newline at end of file diff --git a/myrtle/LICENSE b/myrtle/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/myrtle/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/myrtle/README.md b/myrtle/README.md new file mode 100644 index 00000000..6e3c9458 --- /dev/null +++ b/myrtle/README.md @@ -0,0 +1,25 @@ +# myrtle +tiling cost model for the snitch cluster! + +## query Myrtle for a tile size + +``` +python3 myrtle.py +``` + +where + +- `` is the name of the iree dispatch to tile, for example, `"main$async_dispatch_9_matmul_transpose_b_1x161x600_f64"` +- `` is the tile size selection mode, either + - `"sflt"` - simple filtering tile selection + - `"scyc"` - simple cycle count predicted tile selection + - `"svrcyc"` - SVR (support vector machine) cycle count predicted tile selection + +- `` full path to where myrtle should store its output + +### Example runs + +``` +python3 myrtle.py "main\$async_dispatch_7_matmul_transpose_b_1x600x400_f64" svrcyc test_output.json +``` + diff --git a/myrtle/myrtle/__init__.py b/myrtle/myrtle/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myrtle/myrtle/graphing/__init__.py b/myrtle/myrtle/graphing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myrtle/myrtle/graphing/context2.png b/myrtle/myrtle/graphing/context2.png new file mode 100644 index 0000000000000000000000000000000000000000..0c0b400cb555963b67a77e772db394ae9c8c7735 GIT binary patch literal 71949 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�K!10%yf1Z-K@sZ<>5N=PfYV{!>dwho4Z%|eE~#BqRLrwf8~BD1vS`9|NK{q z^BQp}sd~Uh)@YtR(*ohy00cl{Mg%G!m~8WliwrOR%JmCee_iRft^Dcc8B+88)t9Y= zHBuC8h_YZ0+&eO2d_&-`kLNq+?{d{-d7&x4r>`jgEXC}CjZNA>Dl&8eeN%26|GB?f zzvYM}+S&X$Q}uG8Ocbv&P<{#B4d{Zz!chN4Y*h`;tJXBfl1)2f3sgGpZ(nPzKh`%! zrMZmZm6`we%To(MG)_nxSW|sohKy9z;Uj{o5pK)+R+}l#JBUXPF+-*&c_qTWL2F7R zVC+9%&dgd$?_r98+>i9uR+`KxKi;AHfkr5{J6HIP*M0fvbeIBM9C3?V46z~W(iEXn zYCqrWGoMZBIr7GHuS)mm^mTQ;!Gj%y0vL*f$!ht`}aq<+rtMBp53->+sfUc z*LN1(k=8G$5jhnZ8tc-orGc+3ic;Tg3@hZxzYd->^Gn6ELkt^*zAbl&{$0uf?lM~~$AO3pJ%7(JWMIU&7I5sUQhI$Qf+KY>FhG^3=Iw@`{dwwBdQA~! z2QC_>Om*`pG`JeIp5{`#!e(AM(UPdL^!ZryV56bj#r^RiYF++3lOlByb>7PI>6gzN zSI#>cJTbhv+o(S0(zDno)nt>5J|~{SC`(*6wdS8Sa(W#L8(TfPsM6hV{wQf`YAPVW zqO&EEfA>SZ{k^JAqwKM7di(b&b#T$Xb@@r$UBODyS5b~ zFQp(Hs?KsJlaPE{S$h+SCx#Xi$uV&ZliIVK^_3jsx6Kzs#ub>0Pp=v!0sblxEZl|BEljE@2C*&r29+=NX{hirS3cz~(#JkGkb=20qsW&Wqq+6J9hExLJOd zneXZCC5OWTV0;P#3~2JCih&~#z-VQoLK%JUC|R0|*xh*<6l2Sn9Pb&HmX=;c%Md8A z6~OBeYIc(q8$6nX?WaD1KQE7MYK<(p>t1r*S4n0(WJ5l!^Mb&VBZGs-pt}GirHbju zk&9rcC}2C|K?ed&Kzc?p@(IsSrUkg-(86QrIeBBVnwZ$bmgef}>Q{%HElUulFqqT8 zzF3YQul~Z1cQ{v?S8k%1ttG_;h2lkyUW^0O`fR>7wk@+M5&4Irx0upc3CE=B|zSY>FB8-dSSqb2{Wd%xq?doU4EANJ{9MJaG44iN4;zo(yg@bFPdU3uZB z^C$ds`S?Hk@1}e*LdO@M7FY-@ng=wMP_5<*5SS1Xp}IM5Rg;q^Xk_N$C_xxe3upyR zl`y&O%+^I=M;l0@PsT~wojX~^ZzgRS7K*7TR~9S2w=K>f2~U&vL`f=G1Oqsrw4O|m z;c6YUG*e;kxW*krd~rAQP?zk#etzzs@`dJpUBNaK_4JIdmDDCzQ})sl;!J@q>C2Ea zEX9fwGC@c2)4)mG(TKq7!&e193J3^5KPe80fy!PWNs5V`^<(d z@s;4lYYko7hd=&c;p1!i!oo{Vv9F6i3;`_~54`-_J~^tSo2U;90RteP_f)psNn1lB zm^3s_)8iKfGb?wvkwgXON=O&aK{(4LP-HEL(=*acs0Q@2@Now_ZawqrK-pWjZY54) zX3_?(l;<*ZIO$=hU`SAq`g@XHQ5IKjjO9;CRbU+qCm<3M#KEX_Us!sHfRYr7QzfrI zew4vE+D_{u@R_>0x`~r8Z2#vo(F3CutaduIa=Q^{jA#9nvsOsCUZyFQm?UU`P= zR-U2Bcbowd4P7tlU(g3*+`$Bb?WmvA4jlCQ*}~lXrf4XV@x5k?s*o_Ir@c<3ESV4^wApBjH$SGsz4j?Af zD)DPQ5DChvkFqo|X*ln(OPa;J@{J#B>dh*F=s{lnMwvSAeALTf`(tZr-I|o-`I7Y4 zRH_Ll{=rkOS8Oo;C^{xeHU282Q_dZWCx(p`i&7z%Rdwa50^3q~`X?g~$30O`xH`$q zo)%?Nnf2L!wqbL0mHGL94H`2}Yp}wu^lTp80RxDd=^-j!6&aHZ&2MGksXC(#-NC{R zgQ&>)FnaA|NBi)M{qAXg?AL2*$#=pIhTJ3-#@^5DMwwPoPMhPT+QXOT?dBCi9Bd;5 z|DBiAWm-pC3rBf|nnctN*(;1E<(c%?-rX-$c6E02`An&UE!~~{dl{Q@@CeS5vM%8X z;UM_KTo(|UiTN;$CS)PvYJg&Rjk^~I>~A3K{eRkf51_2i?pyRVMveWmQWP~2ET||_ zML~>}Djh_Mf+9tbj)fMJh)Pi@f)qjNT|@-wM#V;zCLl$S`cgzhq@T5a=$CwF{&((~ zbMD+TXXd;!CQA9$=hj-@l8Z*F1Z>oB>6ZD?>whH^W% zFB8Ew#gM%LTwG|7O9=yT!TE=vFp?s%Z>gyZns)acSW=x_ptJ-_sr8~vi@o+Jh%P`sQ&315bNCftd7@`#YUPWF+ z3Pm632vDyNNKBM|l|x~E1TJG4WVQ0lZ!;Q?BpCz6QwiSiP=TS+tgOE|G zA<}|C%!13#PG}5ap7$}yt*f`zzvGu}u#oyR_(@DcF$H(}03!pT%zS~WYCCC;YYr%! zGx@Nb{fc%D=-mjqP@{b^+fjCMhuuFSsINCO+a~mSYzKOjH7e&Pa4U5M{xkhHjWz4- zIq%P1zt}{*{$AkE$f)Zg(|)& zWV2UMfZ$$8!(>96ZPVrwa5(JHcBxX0%qkwF5 zLjRgPdtuw$Idg=ej)yx>{?!`74gRr@#qq$Cvp-!mt$x^NA;~HP&pH;{0mb5ZS#D$j zXIeQpXog(YHm$!C8F%TP(DIc>m6WdEKG()Y;me$#HjG;};m4#;KB0)CEQ5hL#aDyq zQWUydA2v8m{0p!{%_ZyPW3I168UL!6mw=rvnl;e^84ZL2MS#>Fyr*4{04akSzwk#5 z_@pqpu$eHBIKCNoGH_jcDipZIcO7_qN0{qL3g7zmY~Y5PSSNlrYN9Yy?XV&Ru+iPo-22# zAPI^E#b-hV{A@T)QxsP-dmh$(?sJ)}>!9$N#hP^!bsUI^b)kl#3dC^`SF~a(aNS66 z!RxQ04*XdDITUQ9k=m5PAr-a_3=DvDm@=mb9k9pFM<_uTXw})KX(x@0OcCd4LkQx2 zYAT-$*6Mi>cn8ro90ND8EO1ebhX5urilx6fvk9{bY#|E=#oP`|8Y#zed4L&Q@Y7Ga zSA50edxLRvgMdXlQOEZNiBYnJ@$>wpJPz;fkotg$ITAbp{oDlL#9ed<828WUr9A4m zV+`u#U63p%p&iN5?$j@r$z}O6=Is>9V7Q?qs8l4Ps>#U{u7wQ0t2Rx6KTGyI7a*Rb zz1=T0lgJ?o9k4yY*AO`&sZozs~`>CeQ@Hdq}CN1zkVqORbqUXgEHn7ik$8!+vy z2C(WhbjTX4xQa?eBK4zQ|K>8FIS@t*2=bk&;-dE5TBP?nGXj}~7eDkfy+1cS;Sn(@ zTQM{Gx3x&sg(ZHCWx}Y%Oc+%UuM`7mfCJArsH0>r47OwW!eCw#u>MAH910b76G05K z>6Y)jD)$DdiJ!m!DQSLsBDh0bFcNYiQ;CN9_=JNuFy+hc@i1$R!Nin)mV%V{@W==^ zPq-SwjS37(j|O62{%-f=@ZsD=^WNT~sRw>Shj6JLBb~9 zw;eyp{JwbFbzUGBSg5Z%I8QFeL_9D>Zjq|5GI(y_ptrY^-Z7*TPoS3X8vf$-YgJSy zYeHtQx_LpK$eK(&ymjF@IjiVmE}mHjGP+(W0P>wVGo>Nzll5vi0N40R6Q!fmL*2_> z2(Be_9^Rt$C#An-SmI5#RN4f0qV{wz7++=ZIrC(HY%;}`ONYq9DLgLsD^~>Tv!OqJ z1ja@PnZP@04i?m`k@_%`^;Qr0G1xLuU_c@^$4>uE2MPDv;pW_Y>;cyma0Rtwgx5aL z4uc@YyAE*;3W$#hQ$5Oci#aBhk)ZXSJ-?Y)Al43q7!2UerPKhxnCMs5W6$r6pt=li zcNGoSywGRdkKfoPzN{%2X}Y`l_O_HSEEc^<;xH2BnqWsDa0wb`D=kuzlDa3Sz5NSO zw|hvt6ae9+X_f_ZoFwdG)!W~_i@UOJOQ2G7;C>-Z(`PH$qA@0K67EE8afyE_BDY&R zJaTn%N}R=s@|l5;GC!0&^DptLY+k5|5JoRtV&v@j2Nc9^4c(Foc~}^i3Bb#&xLs9F zXGzwv@HPc)07-#%Pel6SximsrUeLDY^aOmR*n;3+Szg|QxU)8B4XFg$Z+wiO^(+>e zF&N~bYm&<<1&*fbV+D&#Njr8QSMVH5DFmcqX4x5c!DF2nF^wgD7ez((jf1Populn9 z_f=?MLMr%KV$GWTd|uaqj86lBcQdXbMAg)Jw4kKqb^6FVMai8n8gl?0?$XKWR!x`P z(e=et-&D7Rzd@tA=Ux2g?AF-2lvADOa$YH?tIj^BtHa+|;`8^VLxjv(31St6_WOGp z^YUmM0qoCopJ^F4p~z!~Dj<*`Aqm-73~j<31xX&L7+O z)hacVB#`KPGf1sDuzO6N&6ZL+Umxtq4xM>^XnJ}bUtj&g24&Ow1V^?@Xy3W8&31_; z7Y@%p2eS9o5C=8J-A{wN=g$E|BLiNENYBsz*F^l zZ-dHY!*mgYr$2?a_+BQX6nK6_U|LuuI#l;!`(2%Srf=<{8c?FT4Lq~k!#E7MZvFmj= z5w!cgg$GDOrb;r*-XJ|DKx)Dmf)MQ~9Q>nM7>eyKR^o1Kl zMQ_eDWZ5rTx$?BA;^D)Z(7v26J6+rhlP!zdM4*QUC%hR!(CaqPZ~W?TUw857ZgCHy zejJUBt^su;F>x-IZ#U=!uU@{q@wu9#3or7d;O`Ma}uqlnG$AE!#2M)+T;Kz4IwK;_$-U zvoTeIBs;M8rw9)B=F-zSV1TX$KW3;ZR!NY^5WQwt_{hhcuJr)(0_<-i!`dc3ziF3_ zu5Kmh)blYz3=B7Kyvnj0EY^Yq*aXsXuLO~y{2gh9lhTfaL~Z^F*6A$}&VbrWHCD7O zfUT1!PHYeoQbp*AHZ=`|W0g?~E10M!Z6E0)BR5oNK_?z}z|4C~^>)hpEYGBxTYPmK}DP>X625M2d$D?cD{)U_sk z1cQcOfglO`P8>`;bYJg+H$`nk(A_!kd^$_*7f>Cm=mya0AnT3(x5_*h7ZQiY_0Ei* zuB1ox6^H{dO2MOFHlh=x!Uj!!m6!kktTgBP0&+7#Z6DI zE`a5>FnG)82B!$ge`w$3Izb!sDLOP zxoF7}>iZ2SB8Cfi=p^rVQfVs6CJoNP_imHda&|6^zh6ALdqSf7p#8pmdn~W~OIh{Z zmRX)}$_ig?R)`j?8}qC5HT0BFcrD+GHaTB~fsp0TUo9^`kb3`(tj!bMeNT0Hm+#+q zo#*MmXn6bu-etvQ)5Ts7Sn2B2hn5V#>l;{{m2I)0Z`3AF+q{=rn_+zvWPi**O*GvH z=u=KLjLFE*g4L`^3(Jl~O_Bck#>cbys@2rhbu9<>IK3_^s)X=NJa8w& z-Ye^GD?zE}%D>NMMQ2k(3WwB41ur4GcB`+ftgJ0ER5lK~O^~5C<)Xv*@*%h_-5@zwp$fj$ku?2vA(9s~D5|CYkR?70fZKHxI?$-Q@EqB^+>mI6 z<%nVw!Z_!bHYpWV{W? z8*U3i>KSvsh>Aw^DFzDZdcX`!&=EvUQ0d!5qs?v3gIYX@nfE~f%;Q#Syo9kq)WgKt z-rCxUpA;2bU1 zF4(UVfM$sh?uY1|DWwG#rpqCQY z6Ri?)Xhb$;qnIdy`H@gL+95eziNovEhmJm>Nxt}_bI)BEHpO5JBPzaGW(IfbIQ75h zSJzOhby6I1PU(8P<9>n2b0O;ohECr6uX>W}63=khT{Y2f30!h(eVWr=Z+_WL-N|+9 z4}@ge#-#On&CgQT)3$J^PjWJ_Ro`NF*r?rfG?UkFhmzPshg18X*(X>j^!BUPJ`|8S z$#>Ge+Ow-xIVDFzGsr!IUuVfGu5f|hoIbQKGIrPk>x|d5Zp~D6t`aTk(ddPgQBha- zqj?SJ(}_DADz$DsnJ89;8#;YD5E3yzhkb^g9At1ls?%cRK&aqATjj}?zE=J%TR@^2 zKun>-)`rQ0ft3d}*c&!kwPAJ4LR^w;L#C!TIbLg&PFyqlhwmnv>3^PEKT=>^{MRWn zoq?h^eS>9r6KtjWfK{68Gpi>a6^O)7W@)(V={GCY$0UV(zIIqI!K%n1G_m|bvVeVz zgOfe1TSu_7q7(I&=1>qJb$nCb1k?KrO- z`mLpX{)LPakcH{TrpEgnlmge1qsS<8-NDnJ&uNBG^l!cd___O-LsiCs=JNgTH8XQg+mtDQc0Bxmb{npMqo$Km zIvTn=a~G}>@S1eo-j-X-#v&R;HXZizGV0ENWJMs7-16ijP){~+xhmwXHG3HLNZ=I` zegtf+iHNsGnI&wE_hg^A6WV~7Ba;lj7z^KK7yRlG_%3NZZo0K!o(SZ8>Fm~CUFWgx zg^|#L8mhkwi|pRr*a0&6KU-?&KtQh?;?O%18%oyg+I|2_zvXkLgQT!F{?vBb^<_*hueX}qqL{pM z56?rGrS+39;nC|BLIb#)v-K8c@Usf(r~SO^RT!ABPzEmf+AB~KR8^nc% zPz{Lm0^|nn<}&T$ueilNN7!Uiun5W@(t8x`c4m5d+ve_qrL>$wpGgWLwFTWpH6dNN;(T4fbw8T%Bo z5GCxq-n>cY=!*MVn-PThsG3fbHs8G5ot#*jd7TDj*%6`k!nc8K85IT`xBh;ymh-+9 zc5$MneY0oJM!e%Ih^c>7(A!jyXO53=c`*_V_zOPkp%)n3+}!NZ4`nws9i7t%6z~Fu zNyw0QaOGQtB~czX0Ia^ea!l0ynSniy!M*Gsq@n2`_TH#8_2WgE#uV}{>(>7u@S*ZA zx%v@X<;H0_G_O6i1xFOGoDB5HGW%<-p}gYnm-3BLZ}9-XJ@Dt>RG==`O4CXbdhsCq z75LziCDS=0U(CD%YaLvo7rah-|g3g&P4dncg3^+84UkE?MD0_PMB44U_s z+!8g{mRy%ygDtoe<^RrTp442->-G6A;Na@SK*P*2!n`svGr^=dUT_xq!MdnN*U`@? z@$5nM9~%vqa;`jPPs}I!%>RjM3sav8(uw7I{;iKqP{EG$ZYm;kXp8Pi{P)2>?zS*u z96bG2*v7fYTQGH_OeY&dbpTHUFw_QCpqqtLrtz1*PE|CBX(x{DopSz633Tdgb_s|b z4){>Fz@y{%lB?0W^*c579W!M8migI-mgndvNZ5P9m-KtA?9mF1xN1GhwrAJv4>72% z&S=}$CS%@sJ>u${Iiu_^SCpd#cuW(s44+u|`QNP5O*aeW)K!WiTP@ixPThaJnR7lf84p7Chg?D()iq;d1O-s*97-hxWcPOG$uFYiOmbW%t1 z%+fjr`S7)_TA9hOAD(enI}X)IpoV7P)@6U$tCan^tt!K)$3N(Eu|CHRHL>P6%W<=3 z`uNpe_P(4Qqmpi2m-b)kDoSqKVT+PrhVVZw$*^6FivwCT0ki>(r9i_H7=70$C92ra z>nxIF<%tOE$Mk{#kg)yqucAaQLUcFSqnLmU&mbN%wFym|DU&N z*v)YCD)JCTsIPFqQ(NxtuG?Pl<*J6&$)f7sJ?3MTiHAAdw(B}6L~FBWUK-h9VvE~M^I1LTWNuc_4zj%hnosy$jh zeX?HS!>Z&dc~RDQYC=vUefg&^pDLMsfm>5LrFYgWYS%8wV2v|x1AgWfHM(5hHQrrN z*KF5f-mjP(WcfPT7k`5F#GIc>KfA`+@j3H%=X!nonY_wg`XQt&WiOXR%h5=Ui9202 zsnJWoE-BshH&>TcD(~m#yYU=bPsTesx&=c;@HFL5pFUZ)neR+}y#K1u^)g8Pk>s=G z6@O$&w;XR2T7xJ4A0Ocrf8Q03ck^afP^6jgFGj%&qN8pR;gUYm)Bmybr@q8;HU0OV z`t}vvF;naN_dl7x#90aL{Ex3Rokm-lcf{@KS+Q~@#^=tT3kd$nC{=5-(Lth6v{scWagYBByN7QkGS?Q= zhZR@vtIRMOx|yRr@N8yCqPUqu{d)(!H#DA zjYofbJZ`)w@hkA>fBtCtQ0=o%o#E5 zPhU_eX}@9bl!fT7xf$oTph`0Myjt?sprb8;zC5EVlP(-9IX-0?d@|W#8qV3ZcQP+H z^j06$FS6-?CAT#B`@NOQ6Of?l|0VF_3(nMVdV-1$2pL{W4W+cNy6zv3u)9~FcR{t& z=bBN|M0Q*A*_|cfB@Jf|>OLQk`tt7gw5mbN!EBGrwD!S_;Ovs>>(xd!8LJR!O$j*B z0hy1n_31og{+`;b)XmE|vWn;Tz129W9x0M!QX_f@uhnXUD%(EMEOAA}y3+TV=D+mB ztmx{qtbf?E7hfx_?c3h1XOCuEWN&Hcv|C~^?6(+y%Gm|iIAgvpnjWI(P?|hyhCljNA)ohh zX%DI2)jlE?ryFz#+UYN6cVc_5`p-=#?K1?u+%oxy)y1WEtNjmoqjk*$a&-CAzDQ30 z7KQ!$ilkHb{N0-I?W?Y)?+O{l#?-%UR{qzf4BvH~`46_gt5IA+g#62+L$cmT>J^A~ zPd{?$=`#dV*q?tsGZ3VW`u9g^D|6-de4rd)rK6^Hg7HS6ABI18pw_rqH++#`i>lphZEQ(mT!0Oz&M{#j+^742Lt_5u> zL7`Bf&%Et}pAXx?7_@vvU;x{^958`u>b|87mvC}wnHMgx#t$`JMlhjkdz%TND)<;r z*NK3}Od)MnpMhj;~6QEzEsYn5{Tk;nHZqDi$8_vt3Zw6BhjK$iCPTCF7LgHM{ z$9F5BfS(=roeRK(yQObY8eZ}IT?T51tb-V!r(^4k>fm7TkH(HkE2{MMqnsliw78*r zK!Re#(wHer872r>aaExIye5Fvi8Ju-T?SR#k-R4XMVCOU1+NKY{b*+Vqu=o5Qe&M2 z!8eVKd1d;lQPI(xcJJ2r^YcTV74xO>)hk)zL-O-?U2QxybaQD>gZJG}Z{}o`aGf7L z#`o@1Ja;uOUvF9T++3w2CXedvW?jqHc+w{yV^!3<<-%a+#S(RG9V3VOJ$b#!H(K1P zGS;0li<8p5_bcE2FV#8r#`>NkMakdn&ga=GzA~@?a^$gFgLx_?)wf?qo(=f1;h1 ze}&m^meSJFuIPRzfL+ELWre7(U(ces_|1nC%ne-T-!B@Z5E&UM0aoeV@bDt9 zKaGmx|Mbrs?EGa4W+LCl(s_z_-ovOS6glOuBqJtsr8^Zv0ciFhYKIa~R7jlvjKd~U ztTja3s`-A2u9g11S{@ypmO#66u?wnoon1EBB}7^@w^lfXr6nar_ExFFKV)@37QLDn z*_-rkd_nd2=f@Abl;!GueP;0_k52woIl9=+J#&9o@WHSr=m|G2p1P}2jADsH+NFV% zjtILnTArj*Ad(}t6X*{%-n3Nh#4e0sQ&Dv><vZ+@-2rdP%k4%!n}DhYX&N18XXiO{=DbnY)QvqTW{T7fnY{YS?L7Dus6 zohLGHJ5>2;qP!ovwuW|)_zd_jNLH-6-@m^T7PcnZXvNZ&tsFa7_}8V|`n?)3)hUmS zT-wN&e`lWgn9c`nx%vgaiMp{L4yH%vYnw428(_p_Rt=e;5jCF>^KDi;eD!Y`B z${lWTSe=YvSor~mhC;X+xGUa8M&*Ed8dwW2ZQc_mFfpSH2 z_gBRfbYVfgs_AYrvaF4e^*nz3_?(3c#d0Ao#NN^&t% zJL}Y;m|m{MT>wa)yLI)RZLj8WFuz=&yP_FinfFr}&-A41-iFogX=lr2)Dv6|K3=3N zV)BM}eZ+KD*g3|bHf^^>9T-)LO58NBOzk6^Xqx;Y3C)ys>lwczd;*0Y4b=Y2jA^W@ z84%&9`UhR;HNG7GSH2cr`?F{q9z6;jgOCShix(L{WEY0pdZYEp zF1EHXL^MFxE8tRQ??b^nZKh#-CD3SbJBalwD=W`gC^i{8TQsPs`=@P>8$1?EPRayE z%%BPzVOq1q`@U?3oiH+jNI#Lc1ohBaKu`|A-CI*lTcxG-s0I{}-qOTyiA!KrcT-zi zl%j4eCF4sgt}>Q9)z{x2V07;rKyAnEeP4eeO51bu+6QH*kc*%>U_RIOh&|9JMFoOt z0@$6ufkvQ(D=hd5KqOGgDHF>XusdxA;}<}Zg@NW9W*>hta~uu8!h!_sa9>2GWCH4= zr>T$+%FZN6LdqMc*Pkw22y{{-zVay<3d@;dsr0OcjZptM-*)SiFyuz5Ytb9m$%urA zHJlB%$Qjf?^)E1(5=~ZVsRy@+EExHOlt+Yed!jw+Gvtz9fnoariia#qQMADgfBgza z9}^bV^gWV|W zO@aU&4F>ZiLM>L)X2RqE1b#q+jLa*PWi};e-$gf_uI)IK`PgYM#W7P1;vt%F z|NQ*?YRbxY;M`jBfYeiO2r4a}b`Vk}auA6JbsL~KQM!-G-yRU1WO*Rjx|a4A9v<@~hd=lb4T8?#L!ete z7bx?dGYw^E+@;*soCwS-Fh+%dBLUo|3ppo2P6)x~@!S-*2t+^VZ5gBT0uH0K%66PU z_(uT-8Znlo>f7_(6UrzAWqe3_h8I9gUvQdK^WM{?u;zOK8-h|B^_dnfCrG=}LPy)WSXqmzxfIr^p zy?(rb)zerprvSbCJ0{+{cTaD-R$`T0Z_HTg{rlU$73SgB3al(FdS|dHj_IjMJxKKZ z6*}Q`)|CfOfz&H4dBwv+4f_JfE`8=}X=w#O2uP5~3N#WK)p7eWJGArc5=0CTc4Oev zr_2v#>gwu)0TD*1R%CX?#oJb@YpMrr<>yzy^C2O-r%(5RGb!gy5?j!M`;|(cYl#*e z{9-CYV9phmiOxEO5D9gX>|1rd<7blN0QID@O1wod5An!|Q75_2sC!b|0gCm!GT?NS5o_Vo z2N{%7f`TdO`5L;yB4ZDP7jE#LJax(p)WXcpsGtg{9)foCim_1{EB}$49zs9O243EM zV2&Fdq=gtqqo1Ln@BmP>j0)IxM1~=qBa+vEp<23XRT7#2|HR;1Ofh)xx z_6CM^X7{f4$x%s{lTp@lH}y!R8=7q$7nOQYWj8q{p}J$nmumCIS8_x1Vsm;Y6JM!( z>70|rerVPiJG!{GBD1%s^2n>g)I1BV>u#$u?wBXC8zw}L?!L{Ro@Egvy-*=HX1J7uhjp6;heRk}pi#~lZS8fS( z%35GLI>PJL<%QV4bw!hEOW=6cLX*0MS*AJza%tKSX2hyaM`$KP1Dst54@@i6PmYbL)8t29#uQcznA(_TyEp; z<#oD4nz~~>rrRf~#g5eaX`FDVpQbrlQ+;C0{Nck@{137$`V@1&h;X_1Co0Smm>6;t zl&l?nTX`b;n8HKnw?~!OnW4`f4kV?#e0xJ1@dd>QuIrvP=C$cVBh(oTcBRo@DDKFI zFg7?!zU;kR6?pAM=qpMIr1lnPwbL;YSXLMwd}o6zA~wUj>%VMp}#8t%7KaX53JpO1Z0LtJC7v0{QoJd_6ty{3gtV^pHU zW^;i=1HEaTt*Z;CNgGyz&q=M~-L&WD|BB$v_tvd*FRLA#><=OL7pXdQ_?6XHCm*TZ zn_VX5`n1-|bne_5-pk=OXjs~bWYBzfuVwuQzk|L!Is-|k52ouLs89Cldg>MEkh3#W zsP28{g_YLue2l!N2olNl_XJK!BbUqUlz)7Gt; zAeWM${gms_`PjIr=9!i^s8*Cznt_f?d>UEV?#7qmXacO(u~A55qmM7H*rwjcr@*+-ICSWcrx`BNL^Jip@Vdjd6GY8Toz?7BRNl-`5Ta=Di3`d3_`k-W zR_vwZfuTpenQU7K+E)&b;v2&I|6zIMss9GSO!i%FF#60xgrl(X8sD!oyzsfFuA2FO zZ+#u}e{?g_|8;DNMC^MC3A(|)XnXw+x5Io1|KVQ#cTM8^*VI~w;PLmzwZHx458JR55}I}yP}U9Z4EpgtZsZ$dA?APNfp3ES5eGOSR5YNU|h>@F2RI#!MojXUVK@@bPZ=4323qn|IA$)U6bpM{Kq^A&^-Id}Llt?*Ok2d!1&)lAegX=Y z1y)e`g6X_sg2XSINsy)vcL^ae07A}|ffXuc!ohh*^+Tnc_6|NT=Xqo8l|^89E$j$Fk-7*HVgtM!rVQ=jdI6BH+4pcx&4l0eKlZ8l43n6R=GFhkY4~ z(XBRuEA;HY{_|dR)f*9efO|Ld{sPxPzo#W5E>0Ga^}48OtbwIVmvTODZ*)Z{>sm{a zpfKfv1!xr*8Oob9H~2*-34wJE20IrFrX(kyNy|G*cgT-W7I2J60SOO)I*oJaB)xm5 z1Lr1(Jjpu(kG@MRZ`hMKlE!1q0yMrzk{Ipnr#!B$++kqA&DvH1MusP;=c0g$r=keJ zzv7SCOFa4!w${`q-T=G=m7P>5H1f#+)vI1T@o^UEGzY_ng&pSvkAnK~J$9qb$_bN* z+h$yR0zt>m{&{2jfeCu^LLQvD+0SlAj>B+9a1v#lMA!?G+ok~Fx0QfO@7Y2uX{s57 z^;7J>6#1-&W}A(}w+rnQQx~ufIWUdK!6fN` zx_ife*E{=u22ycwAVr3uL=s=m>TGR|gj|Iz+^d)vVkCtrx`aXnOc|u;gt@01+vkpS zZEDFt9!)w=WHMOXdAI;Jy+BZ0B0^Wmj8&)}roM%!{=3s&Lf;ZJyw4EzCydk^P1K3d zQ_zFf-1Z$Pt4cpP38i-kI3tEA3;;SB8l@N#2O^{jmd3iMC&Q$TMZZ9N8^LPu&#`Rc zwZp~nmaM0k8PfG1zkJaEx!EruV1o>;=1R1ZKgzHS<#LVcLx;o&H0nI8pa#!eZlKqq zwFl7UCl7H4RUk2B!63B4#@6SgC}K4XKp4oaM;R zfH$e&hqrH}t8aOa=C&(K*3%j%Ru3-`GU-yOOL7o;pupGYkcSRkerXbP4VgAXI;824 zNPH2J==Tgl42hJ!$bN178PA-9p{OXlMGx2m8jyTi6wxvpojTEF3#3y&B2;>1lq%y* z?azQ6f>wY#8aDe;Tc8;|;|}zWR-!F*lMF~xWk`I}AP{O80u>tY4?aNg8r^jguTe^h z0ikrptXZ3oor0|%H`I2qyZiJwo*i&2k=!n8hjySmL@)){4FD*IZVQ_oPi*D7E0kA% z!BA8scyt2(c5U2_dJS!EfM=d|Xm>9VIiqX-38bXEfV6>>(DPEkBS^~Z;)l{#)yq*` zS(Jysu#eXrKre1(%_xWjM5_Zc4Y)~8MkVW0TA&w~sEBdRF{8a%htuC#yWfOzA`tIbU_G^< zbQO01i;DVS)|i`v0%#>lEfN=WK4E|Rp#=prN_0V1y}Y2~g-Nh9X8_TsVU8GSQVR2d zsaT1%orElgnpV&~oOMhAnLH9|`9tdw)^7q$j__sF+d}ljvy`v9{GwXr7FmR-b8y4rrf+SSC_6HHc$P?5fjrSG$EN`qcQCs=#2nm zrw2==sgn*TK??x&p03ZGE4@ssq5b=%C!Xk$xK?5bgw=dAWoBURgMGIuXZ%Z@xAXZw z*t;kC7mmMQ=(24G7fZYv+UO#uidPaIOWfR8sNXOZv#@iP=x9gONt;_n1)?VydlU;d zg5}$FQP(RZHX;H&U;A+*D#)owu)b%oqCyQuT^C$9?%mGWa4B2aPC&VdU`RUU98NhX zq#_a}d3tP+E?bV{7!rhiu|nT}^b}mC>o@`!#SLZ`ei2^&rC*Krc|cn*5M4pO5TVr&)cHNiX#f<>TXjJ6B{J@<;FBj7=9)N`@E z>-^`AI{Kpg(l4 z2s*252*cti!~}7DERkxFYMs5|@J&V6**0h)4i=hwTf$Cu_r%w5-j~mxyFItY(iQ#} z1~6Bn0p2641Q6i^G$0Okl>L6dkmadufDtZe_C<8XX+v$g_Di8nR70ItFrYFj1eBo# zAGts~jmH6e0TGHY7&PidXl-rvcCH;fi}rJ>=G9;G)=M$?lVtE922dP8OkP0rwgW|C zo1pB1RM+m)sc9^oTgV1@FeBX?a$1oA0i56&y4w6WV@51mh`1$eV@Og93WrEcWJWr# zoS~&Qf80=3QhAk(h2!wr5{*5o!b;otL%AtEsYLo&2-~l1pp$ryd z^cRX4P;7uGmj((uya2-0NZKD;9|Egl?o zK(-ro`pWb(jS|S6SlkP5pTy}(=b>V+4P8iz%3$a=LJ<6QInK!^gD+maazza0+jI;IC zaYa<&hmcGa)KShp?zKR6RssE94=YG{3L^xv7l5Oa{{3W%7N%pIrMGt`yZ;gsQqZlU zGi}Y`!zHF{sFBIxNtSUspv8^q>IoWVIW!A<{t6~`C{jc9#tu1`8OGK#!HvR;H@8EnWf2aoyp+yiKdWaC<)E7qQ^`UGQjw)prtbHYS<8S(z zxGEAJAsGQip3oBn$OLEfZkfLS>rd|pYF4Ef3X~l_ndID&I4lJ|8Qc@1`kC`7{WDfe z%9}Wkq+C8;Bhi}|GZc>!E1vs0jU|UQkl9_YCWg1k4H{1Veycz6D=Y-ho$xsoAr1|( z2pp)d1N?XX&70L^WUgRoVv?Pay}U3B{_A@KA4~~ITrya)In9~G8o)}ih4jPvh<&Qf zJw02HIs<#yFeTcjgcp8JUIb@KdI7>TO&x@Zmh+}3<;f}dD-|8#1X3)ZUqb!co7Uk+ zEo+$DMoS@S{`1mo&rE1@kn9=)KQ8vFa~~E_>0{XvSZhlr3k?fI0mk{aIWj6pL{SqU zi-R1AI(93aYi=XLL4sidA~vFp=94)G;iake4!@)GQ5(-0_yp-HzzG}#r?Yz%)+{UE zWSv4_1WGNk447c>Y!1nxoR!4BS$n`^+a{3lAo~hLR0P5s@Ozrxw&)q4a2*{bVpX5J zYgA#IjP@fZ^JFpWpC!N#Sm*5)QS=izyt8s6Ejb*vWs#XTNu^LTJ_20afV(4xBpLo5yvN5L48Fq}p{yZ0V)6eJ@E ztAsKv5;P1o=)mPvqKs+6Xs;@5wE)=aUVL!hzI)*-aKgYp+3w^I$NpP*`*d zb+V+vC=;Lj^Nb4v1PgRP!ysWn!-Idw4Wo+w;B3DEtxj(gV$nfH<`qh$>54Yfam@T- zxTa-1JWh`t5#A{<8fxPD(4#@R=Ja?M;bHl!rp}Ua8qsnU^3T@y?|+uLUvqi{AaGK5 zcei^|Ba%w08yc>JTnn}Qzz4_s<9AuCk8&JlNu1k`xz3iMf3~AE&7m`pnU%FkK%h8+ z_PO8`TA9DBvpy@Gbc!BjWy#TsplA?js^(FLru=C+6C>;bn%Dz_r~>)A8~|wWI0NJN z-y^ssK7!~*QoU(%%*$@`gbIRGIc#do?5TB;q($w+co^I*h~#pR925||L9N^c8u98> zz3y0BOBWa>g_XYvqklLZI!YfC4ug}}jZOB{n;Tcon0(&fcgr*FQDNjsUlo2odm$OV zKV*L@|0JvY`ff$9Q0kjG!X1}ZY58jSMlRp;r$p-F2RpU7?V}I>AjEGOdxdk$6_uqg z3m!;J&gx%1o)ltuK5bysd|>e6OUT6TMc~O6LvQdzTnMN>>_gmfA1Ygf!5~$AOA%xsQF$oDCIs(<7 z&>kW2M1&=M@E1b8Y73zu5RR(emTUf-k1U39xR%~utrce{q#9rkELxNTV|TL- zdG8pvC|*H5m*ja{VG)u27z`Bt@S!Lh@M&oqw$akc%1S}YrhI4z9IUcoxi0vXxg@&$ z^nC>`V(0*!SB1l`tew3+_+RiCN2`Qzyc5v5RW^op z-Yq3tF)MNEV%(H&4&tZ7T5e&W+6U`L8fTD{OC^+qO4yk`E-7iXCN2j+7Q-u(cpdp= z%|3^vu0X3}{SVBYHHBWGy{AUdOh3>uj_g+JM_Tt!$jsx~&fBU^7lKcIBlA-goAq;d zt-{8#$fRCzl+ynwW%4Fs@5~od2f5QaFpPNP zps=v;QxrLxfx=vaA3cft?FeJm;7d}S6a)#>*up~KEJg*L%|;xvF&P;u4qbd4t~&*z zO22~5)&lg$?A%0Ld=jn1Ng-yDsvyF=Iyu(9I|YvF;t+DZr|1LZft%mBm02vV#qWU} zUxg-`k>qq%$B9?>$?e+eDJUoi+IPRD2mE+cW_`%g!NEbSZJ$rX|DjKl5_5qvq@rtu zTHG_j>OSh}>hb^ub4IaEtd&A+`sD9k#H?pQkUP!-?w->C>u!i(p6r{i=J!S8ws&eb zg9^J#&$0j6#dIA34yc`ZLE1Pf8%*S;P||rV1Kxmro7nK`)hh$UxknDu6L(dcGMVa{ znj8xyKP#1uB8)eD6Ctxv0MAN%7XlUcO-UKQI8O|>UmU`O;bCW5{;uJdXssIy(Q`U_ zCMT_99P+uI;QU2p0AFkR$lP9$NHx8mISn9La3PjgJhzPQ>qL7%0t_ZAO<5gW zc?&=~MJxEB{P#gSY>WU)M>UzFE1@5a>QUJ^Ad7Xs)sNPfcNSE`S`h9Qp2u}a?;&G~ z%GRAGbMUNFz%kmeX<#jL7B7K`{n%w29-#qv19rvz+jwB5uh5mc9EEl1$91R|ukJ&G zA(T-g=rPGsoFnM?^&_6rj7D7d&B1-FFrF!!QvLQVCx@=B^69tter9jP^=yIm+mj4b zb=QWLq-R3+ZVlL5OYjaHXTHWRdSH>&^H3|#+39f|R;v3%)IwHM2jfgsQh*u&URT4b z;y6Nz$d>`-K81=E7f30m?bwZZE(y9>qKdovpU%69OwmLVenJ@u?nemf-vbpz>@Kof zTFbR-X|a|XnVOm!ytw}J{yr>LPq3Kw@7=riYCJAd`)H=ENjKzvDhD9G<_@(UUPYvQ z^EBwN=!5+QA?uBc>0qV`fpC?BT8}JVQK=rYNkvuF)t4bscgOl$Iv#+q_324}-Q}L1 zo*M?E{GxfPJkol}iG)`qty^tgsx$(3>M) z=f|+1Wyl4$jtJslf@tWxn9Pm0dEKwAtt|kqdF8IN+vp*eMnM^6DU=W0@ePgiz`#JQ zR3io2BfpXkft=58?yep{y!zZztU>D|Md7Ub_rW91K;6L+k4rkb zAJoOCNGMm~L7CgJQ3QV+u7MEYiN8u$OlfMCs>xulB}%)XObI;qQ&6Y z?DThH}dWfY8lVblzO`vaE5E%J7^2+ z&s$vs@KQct+s|Km>w`Xl>bxP5>^8uSRP3q;HssvaEWu_Fs>eExnU@ohkzmt9gdF!M&jm6}_)`St{fj z{-vy^w+fPk=WT&#HaVd)JH@*76&b<&Jh5Z>u6JEZ%#DpNqm2C=Ait{K43_Ou=Bh+! zmBKDiur;>>HyGN+e?AW!&t^PoSu4acV;9iGq zP2+!Z=v~&-JI4oqD1DF#Bvc$An_9r!FN*1_^3bt+)g9M1asSxki!rgW8_!T0cD-+aKvhn=Ydbb@QiTN6d{YD0nh5hC1<60Q7U6JsqW1 z11WP-!yEZZ_NG)DY!-ut>~s73kQH1794Yi{KfWvq+%^;Y-2U-V7-sof+xf8LRPZPt zoT4+}6IKo{nmlKsy#VhftD{{$L?W%jn4JyW~%di59cUXbDyLihFEQ#q%RQT ranked.shape[0]): + q = ranked.shape[0] + top = getBestXFrom(ranked, "rank", q, True) + cm = CustomMarker(marker=lambda x: f'${x["rank"]}$', label=lambda y: y["JSON Name"]) + return Graph2D( + keys=Keys2D( + x=x, + x_label=x, + x_unit=x_unit, + y=y, + y_label=y, + y_unit=y_unit, + ), + title=dispatchTitle, + scatterSets=[(top, cm)], + legend=True, + legend_pos="upper right", + ) + + +def getComparisonOfActualTimeVsEstimated( + dfs, x, x_unit, y, y_unit, z, lowIsGoodZ, dispatchNo, dispatchTitle, q, c +): + # rankings + ranked = rankBy(dfs, (dispatchNo, 1), z, lowIsGoodZ) + print(ranked) + + # if Q is invalid, graph ALL the rows instead. + if (q <= 0) or (q > ranked.shape[0]): + q = ranked.shape[0] + + top = getBestXFrom(ranked, "rank", q, True) + cm = CustomMarker( + marker=lambda x: f'${x["rank"]}$', + label=lambda y: y["JSON Name"], + stroke=lambda y: c, + ) + + return Graph2D( + keys=Keys2D( + x=x, + x_label=x, + x_unit=x_unit, + y=y, + y_label=y, + y_unit=y_unit, + ), + title=dispatchTitle, + scatterSets=[ + (top, cm) # , (top2, cm2) + ], + legend=False, + legend_pos="upper right", + ) + + +def frog(dfs, dispatchNo, dispatchTitle): + # dfs,(dispatchNo,1) + ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time", True) + est_ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time Estimate", True) + cm2 = CustomMarker( + marker=lambda x: "o", + label=lambda y: y["JSON Name"], + fill=lambda y: "YellowGreen", + stroke=lambda y: "Black", + ) + cm1 = CustomMarker(fill=lambda y: "Purple") + return Graph2D( + keys=Keys2D( + x="Row Dim", + x_label="Row Dim", + x_unit="8 byte elts", + y="Kernel Time", + y_label="Kernel Time", + y_unit="8 byte elts", + ), + title=dispatchTitle, + scatterSets=[(ranked, cm1)], + legend=True, + legend_pos="upper right", + ) + + +def toad(dfs, dispatchNo, dispatchTitle): + # dfs,(dispatchNo,1) + ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time", True) + est_ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time Estimate", True) + cm2 = CustomMarker( + marker=lambda x: "o", + label=lambda y: y["JSON Name"], + fill=lambda y: "YellowGreen", + stroke=lambda y: "Black", + ) + cm1 = CustomMarker(fill=lambda y: "Purple") + return Graph2D( + keys=Keys2D( + x="Row Dim", + x_label="Row Dim", + x_unit="8 byte elts", + y="Kernel Time", + y_label="Kernel Time", + y_unit="8 byte elts", + ), + title=dispatchTitle, + scatterSets=[(est_ranked, cm2)], + legend=True, + legend_pos="upper right", + ) + + +def dimsVsQuidditchTime(dfs, dispNo, dispTitle): + ranked = rankBy(dfs, (dispNo, 1), "Kernel Time", True) + best = getBestXFrom(ranked, "Kernel Time", 10, True) + a = Graph2D( + keys=Keys2D( + x="Reduction Dim", + x_label="Reduction Dim", + x_unit="elements", + y="Kernel Time", + y_label="Kernel Time", + y_unit="cycles", + ), + title=dispTitle, + scatterSets=[ + ( + best, + CustomMarker( + label=lambda y: f'({y["Row Dim"]},{y["Reduction Dim"]})', + size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2) * 2, + marker=lambda x: f'${x["rank"]}$', + ), + ) + ], + legend=True, + legend_pos="upper left", + legend_bb=(1,1) + ) + return a + + +# def rankBy(dfs, id, by, lowIsGood): +# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) +# df_sorted["rank"] = range(1, df_sorted.shape[0] + 1) +# return df_sorted +label: Callable[[T], str] = lambda y="no label": "_no label" + + +# def rankBy(dfs, id, by, lowIsGood): +# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) +# df_sorted["rank"] = range(1, df_sorted.shape[0] + 1) +# return df_sorted + + +def dimsVsEstimatedTime(dfs, dispNo, dispTitle): + ranked = rankBy(dfs, (dispNo, 1), "Kernel Time Estimate", True) + best = getBestXFrom(ranked, "Kernel Time Estimate", 10, True) + b = Graph2D( + keys=Keys2D( + x="Reduction Dim", + x_label="Reduction Dim", + x_unit="elements", + y="Kernel Time Estimate", + y_label="Kernel Time Estimate", + y_unit="cycles", + ), + title=dispTitle, + scatterSets=[ + ( + best, + CustomMarker( + label= lambda y: f'({y["Row Dim"]},{y["Reduction Dim"]})', + marker=lambda x: f'${x["rank"]}$', + size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2) * 2, + stroke=lambda x: "Purple", + ), + ) + ], + legend=True, + legend_pos="upper left", + legend_bb=(1,1) + ) + return b + + +def main(): + titles = [ + "Dispatch 1\nmatvec: <1x400>, <1200x400> -> <1x1200>", + "Dispatch 8\nmatvec: <1x600>, <600x600> -> <1x600>", + "Dispatch 7\nmatvec: <1x400>, <600x400> -> <1x600>", + ] + # graphs = [] + # dfs = shortcutToData("../estimated_cycles_out") + # top10 = {} + # for dispNo, dispTitle in zip([1, 8, 7], titles): + # quid = dimsVsQuidditchTime(dfs, dispNo, dispTitle) + # est = dimsVsEstimatedTime(dfs, dispNo, dispTitle) + # graphs.append(quid) + # graphs.append(est) + # top10[dispNo] = (quid,est) + # #graphEmAll((3, 2), graphs) + + # g = top10[7] + # graphEmAll((1,2), (g[0],g[1])) + # data = g[0].scatterSets[0][0] + # print(data) + # top5=getBestXFrom(data, "Kernel Time", 5, True) + # print(top5) + # print("hooodle") + # for i in range(0,5): + # better = top5.iloc[i]["Kernel Time"] + # print(top5.iloc[i]["JSON Name"],end=" compared to\n") + # for j in range(i+1,5): + # worse = top5.iloc[j]["Kernel Time"] + # percentage = ((worse + 0.0) - (better + 0.0) )/ (worse + 0.0) * 100.0 + # print("\t",end=f'{top5.iloc[j]["JSON Name"]} is {percentage:.2f} % better\n') + + +if __name__ == "__main__": + main() diff --git a/myrtle/myrtle/graphing/graphing-refactored.py b/myrtle/myrtle/graphing/graphing-refactored.py new file mode 100644 index 00000000..15c6d33f --- /dev/null +++ b/myrtle/myrtle/graphing/graphing-refactored.py @@ -0,0 +1,257 @@ +from mpl_toolkits.mplot3d.axes3d import Axes3D +from mpl_toolkits.mplot3d import proj3d +import sys +import pandas as pd +from graph_utils import graphEmAll, loadDFsDispatchCaseNo, deriveMoreData,addSVMPrediction,trimToTopX, Graph2D, Keys2D, CustomMarker, MySVM +import matplotlib as mpl +import matplotlib.pyplot as plt +from itertools import product, islice +from PIL import Image +import numpy as np +from sklearn.inspection import DecisionBoundaryDisplay +from sklearn.svm import SVC, SVR + +# example run: python graphing-refactored.py "/home/hoppip/myrtle/estimated_cycles_no_overhead" +# example run: python graphing-refactored.py /home/emily/myrtle/estimated_cycles_overhead +def main(): + args = sys.argv[1:] + if len(args) != 1: + print("USAGE: python3 graphing-refactored.py ") + print("\twhere contains static characteristics, \n\testimated time based on microkernels, and actual time") + exit(1) + print("HOLA") + caseNos = [1] # We only graph case 1; no padding anywhere. + dispatchOrder = [8,7,1] # All dispatches will be graphed in the order 8, 7, 1. + titles = { + 8:"Dispatch 8\nmatvec: <1x600>, <600x600> -> <1x600>", + 7:"Dispatch 7\nmatvec: <1x400>, <600x400> -> <1x600>", + 1:"Dispatch 1\nmatvec: <1x400>, <1200x400> -> <1x1200>", + } + print(f"Reading data from {args[0]}...") + # load dispatch data + dfs = loadDFsDispatchCaseNo(args[0], dispatchOrder, caseNos) + # derive more data about each dispatch + deriveMoreData(dfs,dispatchOrder,caseNos) + # train some SVMs to predict runtime + tunedOverhead = tryToFindOverHeadConstant(dfs,8,1,"tryToTuneOverheadCoef") + generalSVM = tryGeneralSVM(dfs,8,1,"tryGeneralSVM") + addSVMPrediction(dfs,dispatchOrder,caseNos,tunedOverhead) + addSVMPrediction(dfs,dispatchOrder,caseNos,generalSVM) + + # graph ALL data points + # Quidditch Time (FLOP/cycle) vs Attributes + keysFLOPs = Keys2D( + x="rank", + x_label="Rank", + x_unit="fastest to slowest", + y="FLOP Per Cycle", + y_label="Throughput", + y_unit="FLOPs per cycle", + ) + lookForTrendsDispatchCase(dfs,dispatchOrder,caseNos,titles,keysFLOPs,"hoodle","LFT-dispatches-rank-x-axis.png") + keysFLOPs.x="Kernel Time" + keysFLOPs.x_label="Kernel Time" + keysFLOPs.x_unit="cycles" + lookForTrendsDispatchCase(dfs,dispatchOrder,caseNos,titles,keysFLOPs,"hoodle","LFT-dispatches-kernel-time-x-axis.png") + # Actual Quidditch Time (cycles) VS Predicted Quidditch time (cycles) + keysActualVsReal = Keys2D( + x="rank", + x_label="Rank", + x_unit="fastest to slowest", + y="Kernel Time", + y_label="Kernel Time", + y_unit="cycles", + ) + actualVsPredictedTimeDispatchCase(dfs,dispatchOrder,caseNos,titles,"Kernel Time","Kernel Time Estimate",keysActualVsReal,"Estimate with Microkernel Time * Microkernel Runs",'ActualVsEstimate-3-dispatches-rank-x-axis.png') + actualVsPredictedTimeDispatchCase(dfs,dispatchOrder,caseNos,titles,"Kernel Time",tunedOverhead.predName,keysActualVsReal,"Estimate with SVR-tuned overhead constant",'SVR-overhead-estimate-3-dispatches.png') + actualVsPredictedTimeDispatchCase(dfs,dispatchOrder,caseNos,titles,"Kernel Time",generalSVM.predName,keysActualVsReal,"Estimate with general SVR ",'SVR-general-estimate-3-dispatches.png') + + # graph TOP TEN data points + dfs = trimToTopX(dfs, dispatchOrder, caseNos, "rank", 10) # destructive!! + lookForTrendsDispatchCase(dfs,dispatchOrder,caseNos,titles,keysFLOPs,"hoodle","LFT-dispatches-kernel-time-x-axis-top-10.png") + print("HASTA LUEGO") + +def tryToFindOverHeadConstant(dfs, dispNo, caseNo,predName="tryToTuneOverheadCoef"): + ranked = dfs[(dispNo,caseNo)] + feature_names = ["Kernel Time Estimate","UnrollAndJam Outer Loops"] + target_name = "Kernel Time" + train = ranked[feature_names] + X = np.array(train) + y = np.array(ranked[target_name]) + # Build the model + svm = SVR(kernel="linear", gamma=0.5, C=1.0) + # Train the model + svm.fit(X, y) + return MySVM(svm=svm,predName=predName,featureNames=feature_names) + +def tryGeneralSVM(dfs, dispNo, caseNo,predName="tryGeneralSVM"): + ranked = dfs[(dispNo,caseNo)] + feature_names = ["Microkernel Count","Regular Loads","Reused Streaming Loads","Space Needed in L1","Row Dim","Reduction Dim", "UnrollAndJam Factor"] + target_name = "Kernel Time" + train = ranked[feature_names] + X = np.array(train) + y = np.array(ranked[target_name]) + # Build the model + svm = SVR(kernel="linear", gamma=0.5, C=1.0) + # Train the model + svm.fit(X, y) + return MySVM(svm=svm,predName=predName,featureNames=feature_names) + +def combineDispatchesWithContext(graphs,img_title,img_name): + graphImages = [] + width = 0.0 + biggest = None + # find graph with max width and use this to set the width of the canvas + for g in graphs: + gImg = Image.open(f'{g.imagePath}.png') + if gImg.size[0] > width: + width = gImg.size[0] + biggest = gImg + graphImages.append(gImg) + # resize context header to fit canvas + orig = Image.open('context2.png') # hard coded + resized = orig.copy() + resized.thumbnail(biggest.size, Image.Resampling.LANCZOS) + resized.save("resized.png") + # place each of the three dispatches on the canvas + top = Image.open('resized.png') + g0 = graphImages[0] + g1 = graphImages[1] + g2 = graphImages[2] + canvas = Image.new('RGBA', (biggest.size[0], top.size[1]+g0.size[1]+g1.size[1]+g2.size[1]), (255, 255, 255, 255)) + canvas.paste(top, (0, 0), top) + canvas.paste(g0, (0, top.size[1]), g0) + canvas.paste(g1, (0, top.size[1]+g0.size[1]), g1) + canvas.paste(g2, (0, top.size[1]+g0.size[1]+g1.size[1]), g2) + canvas.save(img_name) + top.close() + g0.close() + g1.close() + g2.close() + +def lookForTrendsDispatchCase(dfs, dispatchNos, caseNos, titles, keys=Keys2D( + x="rank", + x_label="Rank", + x_unit="fastest to slowest", + y="FLOP Per Cycle", + y_label="Throughput", + y_unit="FLOPs per cycle", + ),imgTitle="Looking for Trends",imgName='LFT-3-dispatches.png',): + graphs = [] + for dispNo in dispatchNos: + for caseNo in caseNos: + title = titles[dispNo] + lftGraph = lookForTrends(dfs, dispNo, caseNo, title,keys) + graphs.append(lftGraph) + graphEmAll((1, 1), [lftGraph]) + combineDispatchesWithContext(graphs,imgTitle,imgName) + +def actualVsPredictedTimeDispatchCase(dfs, dispatchNos, caseNos, titles,y1="Kernel Time",y2="Kernel Time Estimate",keys=Keys2D( + x="rank", + x_label="Rank", + x_unit="fastest to slowest", + y="Kernel Time", + y_label="Kernel Time", + y_unit="cycles", + ),imgTitle="Actual vs Predicted Time",imgName='ActualVsEstimate-3-dispatches.png'): + graphs = [] + for dispNo in dispatchNos: + for caseNo in caseNos: + title = titles[dispNo] + lftGraph = actualVsPredictedTime(dfs, dispNo, caseNo, title,y1,y2,keys) + graphs.append(lftGraph) + graphEmAll((1, 1), [lftGraph]) + combineDispatchesWithContext(graphs,imgTitle,imgName) + + +def actualVsPredictedTime(dfs, dispNo, caseNo, title, y1="Kernel Time",y2="Kernel Time Estimate",keys=Keys2D( + x="rank", + x_label="Rank", + x_unit="fastest to slowest", + y="Kernel Time", + y_label="Kernel Time", + y_unit="cycles", + )): + tableData = dfs[(dispNo,caseNo)][["rankAsStr","Microkernel Row Dim","UnrollAndJam Factor","UnrollAndJam Outer Loops","Microkernel Count","Row Dim","Reduction Dim"]] + colLabels = ["rank","n'","U&J Factor","CC Outer Loops","Micro Runs","n","k"] + defW = (1/(len(colLabels)*3)) # default width + tableColWidths = [defW,defW,defW*1.5,defW*1.5,defW*1.25,defW*0.5,defW*0.5]#[defW]*len(colLabels) + return Graph2D( + imagePath=f'dispatch-{dispNo}-case-{1}', + keys=keys, + title=title, + scatterSets=[ + ( + dfs[(dispNo,caseNo)], + CustomMarker( + y=y1, + label= lambda y: f' {y["JSON Name"]}', + marker=lambda x: f'${x["rank"]}$', + size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2)*2, + stroke=lambda x: 'Black', + fill=lambda x: 'Black' + ), + ), + ( + dfs[(dispNo,caseNo)], + CustomMarker( + y=y2, + label= lambda y: f' {y["JSON Name"]}', + marker=lambda x: f'${x["rank"]}$', + size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2)*2, + stroke=lambda x: "Purple", + fill=lambda x: "Purple", + ), + ) + ], + legend = False, + table = True, + table_pos="right", + table_bb=(1.01,0,1,1), + table_col_widths = tableColWidths, + table_col_labels=colLabels, + table_row_labels=[], + table_data=tableData + ) + +def lookForTrends(dfs, dispNo, caseNo, dispTitle, keys=Keys2D( + x="rank", + x_label="Rank", + x_unit="fastest to slowest", + y="FLOP Per Cycle", + y_label="Throughput", + y_unit="FLOPs per cycle", + )): + tableData = dfs[(dispNo,caseNo)][["rankAsStr","Microkernel Row Dim","UnrollAndJam Factor","UnrollAndJam Outer Loops","Microkernel Count","Regular Loads","Reused Streaming Loads","Space Needed in L1","Row Dim","Reduction Dim"]] + colLabels = ["rank","n'","U&J","CC OLs","Micro Runs","RLs","Reused SLs","L1 Usage","n","k"] + defW = (1/(len(colLabels)*3)) # default width + tableColWidths = [defW*0.6,defW*0.5,defW*0.4,defW,defW*1.25,defW,defW*1.5,defW*1.6,defW*0.5,defW*0.5] + return Graph2D( + imagePath=f'LFT-dispatch-{dispNo}-case-{caseNo}', + keys=keys, + title=dispTitle, + scatterSets=[ + ( + dfs[(dispNo,caseNo)], + CustomMarker( + y="FLOP Per Cycle", + label= lambda y: f' {y["JSON Name"]}', + marker=lambda x: f'${x["rank"]}$', + size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2)*2, + stroke=lambda x: 'Black', + fill=lambda x: 'Black' + ), + ) + ], + legend = False, + table = True, + table_pos="right", + table_bb=(1.01,0,1,1), + table_col_widths = tableColWidths, + table_col_labels=colLabels, + table_row_labels=[], + table_data=tableData + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/myrtle/myrtle/myrtle.py b/myrtle/myrtle/myrtle.py new file mode 100644 index 00000000..9326f7ae --- /dev/null +++ b/myrtle/myrtle/myrtle.py @@ -0,0 +1,115 @@ +import sys +import json +import tile_gen.tile_size_generator as tsg +import re +import pickle +import pandas as pd +import sklearn.svm +from graphing.graph_utils import Curve +import os + + +# def rankBy(dfs, id, by, lowIsGood): +# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) +# df_sorted["rank"] = range(1, int(df_sorted.shape[0] + 1)) +# return df_sorted + +# def getBestX(dfs, id, by, x, lowIsGood): +# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) +# df_best_5 = df_sorted.iloc[range(0, x)] +# return df_best_5 + +def get_simple_cycle_estimate(timeEstimateFuncs, row_dim, col_dim, outerLoopIters, microCount): #, n, k): + if outerLoopIters == 1: + return timeEstimateFuncs[row_dim](col_dim) * microCount + else: # for ex, microkernel tile of 10 x 50 will have + # outer loop iters = unroll and jam outer loops = 2 + # unroll and jam factor of 5 + # so select function that estimates execution of microkernel + # with row dimension 10 / 2 = 5, and multiply that by outer loops + # TODO: ALSO, ADD A CONSTANT TO ACCOUNT FOR OVERHEAD OF SETTING UP STREAMING REGISTERS + return timeEstimateFuncs[row_dim/outerLoopIters](col_dim)*microCount #+ outerLoopIters*100 + +def tileSelection(csvFile, mode): + df = pd.read_csv(csvFile) + myLoc=os.path.abspath(__file__)[:-(len("myrtle.py"))] + if mode == "svrcyc": + file = open(f'{myLoc}/tile_sel/dispatch-8-svr.pickle', 'rb') + svr=pickle.load(file) + df["Predicted Kernel Time"] = df.apply(lambda y: svr.predict([y[["Microkernel Count","Regular Loads","Reused Streaming Loads","Space Needed in L1","Row Dim","Reduction Dim"]]])[0], axis=1) + ranked = df.sort_values("Predicted Kernel Time", ascending=True) + df = ranked + else: + if mode == "scyc": + linearApproxFilePath = f'{myLoc}/tile_sel/linesOfBestFit.pickle' + file = open(linearApproxFilePath, 'rb') + curves = pickle.load(file) + lines = {} + for c in curves: + lines[c.id]=c.func + df["Kernel Time Estimate"] = df.apply(lambda x: get_simple_cycle_estimate(lines,x["Microkernel Row Dim"], x["Microkernel Reduction Dim"],x["Outer Loop Iters"],x["Microkernel Count"]), axis=1) + ranked = df.sort_values("Kernel Time Estimate", ascending=True) + df = ranked + else: + # minimize microkernel runs + df_sorted = df.sort_values("Microkernel Count", ascending=True) + df_sorted = df_sorted.iloc[range(0, len(df_sorted)//3)] + # maximize space used in L1 + df_sorted = df_sorted.sort_values("Space Needed in L1", ascending=False) + df_sorted = df_sorted.iloc[range(0, len(df_sorted)//2)] + # minimize regular loads + final_ranking = df_sorted.sort_values("Regular Loads", ascending=False) + df = final_ranking + print(f'df is {df}') + m = 1 #TODO: expand tiling to matmul!! + n = int(df.iloc[0]["Row Dim"]) + k = int(df.iloc[0]["Reduction Dim"]) + dualBuffer = True + return (m,n,k,dualBuffer) + +# arg 1 is dispatchName as a string +# arg 2 is tile selection mode +# arg 3 is file to write tile scheme to +def main(): + # print("myrtle: ",end='') + dispatchName = sys.argv[1] + # print (dispatchName) + # for e in sys.argv: + # print(f'arg is {e}.') + dispatchRegex=re.compile(r'main\$async_dispatch_\d+_matmul_transpose_b_(\d+)x(\d+)x(\d+)_f64') + M,N,K = dispatchRegex.search(dispatchName).groups() + # query myrtle! + # generate options + jen = tsg.TileSizeGenerator(int(N),int(K),dispatchName) + options = jen.validOptions() + print("myrtle : ",end='') + jen.exportOptionsToCSV(f'{M}x{N}x{K}wm-n-k', 1, options) + m,n,k,dualBuffer = tileSelection(f'{M}x{N}x{K}wm-n-k_case1_searchSpace.csv',sys.argv[2]) + if sys.argv[2] == "sflt": + print("myrtle : ",end='') + print("We used simple filtering to select tiles.") + if sys.argv[2] == "scyc": + print("myrtle : ",end='') + print("We used a simple cycle estimation to select tiles.") + if sys.argv[2] == "svrcyc": + print("myrtle : ",end='') + print("We used an SVR to select tiles.") + # default values + with open(sys.argv[3], 'r') as file: + data = json.load(file) + node = {} + node["loop-order"] = [[2,0], [0,0], [1,0]] + + # set node values and export to JSON + node["tile-sizes"] = [[m], [n], [k]] + node["dual-buffer"] = dualBuffer + data[dispatchName]=node + f = open(sys.argv[3], "w") + f.write(f"{json.dumps(data)}") + f.close() + + + +if __name__ == "__main__": + main() + diff --git a/myrtle/myrtle/tile_SA/__init__.py b/myrtle/myrtle/tile_SA/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myrtle/myrtle/tile_SA/quidditch_load_counting.py b/myrtle/myrtle/tile_SA/quidditch_load_counting.py new file mode 100644 index 00000000..7124b210 --- /dev/null +++ b/myrtle/myrtle/tile_SA/quidditch_load_counting.py @@ -0,0 +1,160 @@ +from tile_SA.utils import roundUpToNearestMultipleOf, InputMatrix, TileSizes, HardwareLoop, unrollAndJamFactor, EnclosingSCFLoop, unrollAndJamOuterLoops +#from myrtle.peek_at_snitch_assembly import peek_at_lowered_matvec_tiling + + +def getLogicalSizeAfterPadding(mat: InputMatrix, sizes: TileSizes): + logicalSize = InputMatrix(n=mat.n, k=mat.k) + if mat.n % sizes.n != 0: + logicalSize.n = roundUpToNearestMultipleOf(mat.n, sizes.n) + if mat.k % sizes.k != 0: + logicalSize.k = roundUpToNearestMultipleOf(mat.k, sizes.k) + return logicalSize + + +"""Given a tile of size sz, what is the subtile size when there are 8 subtiles?""" + + +def coreTile(sz: int): + if sz % 8 != 0: + raise Exception(f"tile size MUST be divisible by 8, yet I have {sz}!") + else: + return sz // 8 + +# return total number of times microkernel runs to complete execution of linalg kernel +def getMicroKernelCount(mat: InputMatrix, sizes: TileSizes): + k_count = mat.k // sizes.k # tile the k dimension once + n_count = mat.n // sizes.n # tile the n dimension once + # tile the n dimension again (for each computer core) + micro_n_sz = coreTile(sizes.n) + per_cluster_count = sizes.n // micro_n_sz + assert per_cluster_count == 8 + micro_k_sz = mat.k // k_count + cluster_tile = InputMatrix(n=sizes.n, k=micro_k_sz) + microkernel_tile = InputMatrix(n=micro_n_sz, k=micro_k_sz) + microkernel_count = k_count * n_count * per_cluster_count + # ONLY FOR DEBUGGING VV + # print(f"sizes is {sizes}") + # print(f'per_cluster_count is {per_cluster_count}') + # print(f"microkernel tile: {microkernel_tile}") + # print(f"cluster tile: {cluster_tile}") + # print(f"k_count: {k_count}. n_count: {n_count}. per_cluster_count: {per_cluster_count}") + # print(f"how many microkernel tiles are there, then?") + # print(f"{k_count*n_count*per_cluster_count} because {k_count*n_count*per_cluster_count*micro_k_sz*micro_n_sz} = {mat.k * mat.n}") + # print(f"each core processes (k_count * n_count) = {k_count*n_count} of the {k_count*n_count*per_cluster_count} tiles, because {k_count*n_count*per_cluster_count}/{8}={k_count*n_count*per_cluster_count/8}") + # ^^ ONLY FOR DEBUGGING + # reality check + left = k_count*n_count*per_cluster_count*micro_k_sz*micro_n_sz + right = mat.k * mat.n + if left != right: + raise Exception(f'{left} should = {right}') + return (microkernel_count, microkernel_tile, cluster_tile) + + +def yodel(): + print("yodelayheehooooo~~~~~~!") + + +def LoadCountingAnnColumnNames(): + columns = [ + "Regular Loads", + "Total Streaming Loads", + "Other Streaming Loads", + "Start Reuse Streaming Loads", + "Reused Streaming Loads", + "Outer Loop Iters", + "HW Loop Body", + "HW Loop Iters", + "Microkernel Count", + "Microkernel Row Dim", + "Microkernel Reduction Dim", + ] + return columns + + + +# @dataclass +# class HardwareLoop: +# """Class for keeping track of hardware loop characteristics""" +# name: str = "frepOuter"#FrepOuter.name +# loop_repeats: int = 1 # number of times loop executes +# body_size: int = 1 # number of instructions in body of the loop +# #unrollAndJamFactor + +# @dataclass +# class EnclosingSCFLoop: +# """Class for keeping track of a potential loop surrounding the hardware""" +# name: str = "an enclosing loop" +# iters : int = 1 # number of times the enclosing loop executes +# exists : bool = False # whether the hardware loop is in fact enclosed by another loop + +def simulate_peek_at_lowered_matvec_tiling(matvec: InputMatrix): + # for potential_factor in range(1, self.pipeline_depth * 2): + expectedFMADDs = matvec.n * matvec.k + # create linalg, then lower to assembly + #linalg_mod, asm_mod, m, n, k = createMatmulTransposeB(1, matvec.n, matvec.k) + m = 1 + if m == 0: + m = 1 + k = matvec.k + n = matvec.n // 8 + if (n) * 8 != matvec.n: + raise Exception("Sorry, only row dimensions divisible by 8 allowed!") + + # look for a hardware loop + hLoop=HardwareLoop(loop_repeats=k,body_size=unrollAndJamFactor(n)) + # look for an enclosing loop + oLoopIters = unrollAndJamOuterLoops(n) + oLoop=EnclosingSCFLoop(iters=oLoopIters,exists=not(oLoopIters==1)) + res = expectedFMADDs == ( + (hLoop.body_size * hLoop.loop_repeats) * oLoop.iters * 8 + ) + if not oLoop.exists: + res = expectedFMADDs == (n * k * 8) + return (res, hLoop, oLoop) + +# matrix-vector transpose with type `, -> ` where `M = 1` +def getLoadCountingAnn(mat: InputMatrix, sizes: TileSizes): + logicalInput = getLogicalSizeAfterPadding(mat, sizes) + # logicalCount = the number of times a core-sized tile gets processed = (8 * outer L1 tiling loops) + # this number could be different than the number of microkernel runs per core, + # because if unroll and jam is performed, a microkernel must run more than once to process a single core-sized tile. + logicalCount, microkernel_tile, cluster_tile = getMicroKernelCount(logicalInput, sizes) + left = microkernel_tile.n * microkernel_tile.k * logicalCount + right = logicalInput.n * logicalInput.k + # reality check + if left != right: + raise Exception(f'after getMicroKernelCount: {left} should = {right}') + res, hLoop, oLoop = simulate_peek_at_lowered_matvec_tiling(cluster_tile) + if not res: + raise Exception("Lowering to snitch hardware loop failed!") + # outer_loop_iters is equivalent to the number of micro-kernel runs needed to process one core-sized tile + # if unroll and jam was performed, this value > 1. + outer_loop_iters = oLoop.iters if oLoop.exists else 1 + # loads during micro kernel execution(s) per core + regular_loads_per_core = outer_loop_iters*hLoop.body_size + other_streaming_loads_per_core = outer_loop_iters*(hLoop.body_size)*hLoop.loop_repeats + total_streaming_loads_per_core = outer_loop_iters*(hLoop.body_size*2)*hLoop.loop_repeats + start_reuse_streaming_loads_per_core = outer_loop_iters*(1)*hLoop.loop_repeats + reused_streaming_loads_per_core = outer_loop_iters*(hLoop.body_size-1)*hLoop.loop_repeats + assert total_streaming_loads_per_core == (start_reuse_streaming_loads_per_core+reused_streaming_loads_per_core+other_streaming_loads_per_core) + # per cluster + regular_loads = regular_loads_per_core * logicalCount + other_streaming_loads = other_streaming_loads_per_core * logicalCount + total_streaming_loads = total_streaming_loads_per_core * logicalCount + start_reuse_streaming_loads = start_reuse_streaming_loads_per_core * logicalCount + reused_streaming_loads = reused_streaming_loads_per_core * logicalCount + return(regular_loads,total_streaming_loads,other_streaming_loads,start_reuse_streaming_loads,reused_streaming_loads,outer_loop_iters, hLoop.body_size, hLoop.loop_repeats, logicalCount,microkernel_tile.n ,microkernel_tile.k) + +def main(): + input = InputMatrix(n=1200, k=400) + tiles = TileSizes(n=56, k=100) + print() + print(f"input: {input}") + print(f"tiles: {tiles}") + print() + res = getLoadCountingAnn(input, tiles) + print(res) + + +if __name__ == "__main__": + main() diff --git a/myrtle/myrtle/tile_SA/utils.py b/myrtle/myrtle/tile_SA/utils.py new file mode 100644 index 00000000..0cb36bde --- /dev/null +++ b/myrtle/myrtle/tile_SA/utils.py @@ -0,0 +1,54 @@ +from dataclasses import dataclass +#from xdsl.dialects.riscv_snitch import FrepOuter + +def roundUpToNearestMultipleOf(num, row_dim): + remainder = num % row_dim + if (remainder != 0): + return (num//row_dim) * row_dim + row_dim + else: + return num + +@dataclass +class InputMatrix: + """Class for keeping track of matrix dimensions in""" + """matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul)""" + n: int = 1200 + k: int = 400 + +@dataclass +class TileSizes: + """Class for keeping track of matrix-vector transpose tiling in each dimension;""" + """matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul)""" + m: int = 1 + n : int = 40 + k : int = 100 + +@dataclass +class HardwareLoop: + """Class for keeping track of hardware loop characteristics""" + name: str = "frepOuter"#FrepOuter.name + loop_repeats: int = 1 # number of times loop executes + body_size: int = 1 # number of instructions in body of the loop + +@dataclass +class EnclosingSCFLoop: + """Class for keeping track of a potential loop surrounding the hardware""" + name: str = "an enclosing loop" + iters : int = 1 # number of times the enclosing loop executes + exists : bool = False # whether the hardware loop is in fact enclosed by another loop + +def unrollAndJamFactor(rowDim): + options = [7,6,5,4,3,2] + factor = 1 + for option in options: + if rowDim % option == 0: + factor = option + break + return factor + +def unrollAndJamOuterLoops(rowDim): + # print(f'outer loops is {rowDim} / {unrollAndJamFactor(rowDim)} which is {rowDim / unrollAndJamFactor(rowDim)}') + if unrollAndJamFactor(rowDim) != 1: + return int(rowDim / unrollAndJamFactor(rowDim)) + else: + return 1 \ No newline at end of file diff --git a/myrtle/myrtle/tile_gen/__init__.py b/myrtle/myrtle/tile_gen/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py b/myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py new file mode 100644 index 00000000..85f84592 --- /dev/null +++ b/myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py @@ -0,0 +1,229 @@ +from xdsl.backend.riscv.lowering import ( + convert_arith_to_riscv, + convert_func_to_riscv_func, + convert_memref_to_riscv, + convert_scf_to_riscv_scf, +) +from xdsl.backend.riscv.lowering.convert_riscv_scf_to_riscv_cf import ( + ConvertRiscvScfToRiscvCfPass, +) +from xdsl.backend.riscv.lowering.convert_snitch_stream_to_snitch import ( + ConvertSnitchStreamToSnitch, +) +from xdsl.builder import ImplicitBuilder +from xdsl.context import MLContext +from xdsl.dialects import arith, func, linalg +from xdsl.dialects.builtin import AffineMap, AffineMapAttr, MemRefType, ModuleOp, f64 +from xdsl.dialects.riscv import riscv_code +#from xdsl.interpreters.utils.ptr import TypedPtr +from xdsl.ir import Attribute, Block, Region, SSAValue +from xdsl.passes import PipelinePass +from xdsl.tools.command_line_tool import get_all_dialects +from xdsl.transforms import ( + arith_add_fastmath, + convert_linalg_to_loops, + convert_linalg_to_memref_stream, + convert_memref_stream_to_loops, + convert_memref_stream_to_snitch_stream, + convert_riscv_scf_for_to_frep, + dead_code_elimination, + loop_hoist_memref, + lower_affine, + memref_streamify, + reconcile_unrealized_casts, +) +from xdsl.transforms.canonicalize import CanonicalizePass +from xdsl.transforms.lower_snitch import LowerSnitchPass +from xdsl.transforms.mlir_opt import MLIROptPass +from xdsl.transforms.riscv_register_allocation import RISCVRegisterAllocation +from xdsl.transforms.riscv_scf_loop_range_folding import ( + RiscvScfLoopRangeFoldingPass, +) +from xdsl.transforms.snitch_register_allocation import SnitchRegisterAllocation +from collections import Counter +from dataclasses import dataclass, field +from xdsl.transforms.test_lower_snitch_stream_to_asm import ( + TEST_LOWER_SNITCH_STREAM_TO_ASM_PASSES, +) +from xdsl.transforms.test_lower_memref_stream_to_snitch_stream import ( + TEST_LOWER_MEMREF_STREAM_TO_SNITCH_STREAM +) +from xdsl.transforms.test_optimise_memref_stream import ( + TEST_OPTIMISE_MEMREF_STREAM +) +from io import StringIO +from xdsl.dialects.riscv import RISCVAsmOperation, RsRsOffIntegerOperation, MulOp, LiOp +from xdsl.dialects.riscv_cf import ConditionalBranchOperation +from xdsl.dialects.riscv_func import FuncOp +from xdsl.dialects.riscv_snitch import FrepOuter +from xdsl.passes import ModulePass +from myrtle.utils import InputMatrix, HardwareLoop, EnclosingSCFLoop + + +def createMatmulTransposeB(m=0, outputVectorEltCount=40, inputVectorEltCount=100): + if m == 0: + m = 1 + k = inputVectorEltCount + n = outputVectorEltCount // 8 + if (n) * 8 != outputVectorEltCount: + raise Exception("Sorry, only row dimensions divisible by 8 allowed!") + ctx = MLContext() + for dialect_name, dialect_factory in get_all_dialects().items(): + ctx.register_dialect(dialect_name, dialect_factory) + a_shape2 = (m, k) + b_shape2 = (n, k) + c_shape2 = (m, n) + # Let's try to recreate the parsed matmultranspose_b using an xDSL IR builder... + a_type2 = MemRefType(f64, a_shape2) + b_type2 = MemRefType(f64, b_shape2) + c_type2 = MemRefType(f64, c_shape2) + kernel_op2 = func.FuncOp("matmul_transpose_b", ((a_type2, b_type2, c_type2), ())) + with ImplicitBuilder(kernel_op2.body) as (a2, b2, c2): + # Add name hints to make it easier to track how values are lowered + a2.name_hint = "A" + b2.name_hint = "B" + c2.name_hint = "C" + body2 = Region(Block(arg_types=(f64, f64, f64))) + linalg.Generic( + inputs=(a2, b2), + outputs=(c2,), + body=body2, + indexing_maps=( + AffineMapAttr(AffineMap.from_callable(lambda m, n, k: (m, k))), + AffineMapAttr(AffineMap.from_callable(lambda m, n, k: (n, k))), + AffineMapAttr(AffineMap.from_callable(lambda m, n, k: (m, n))), + ), + iterator_types=( + linalg.IteratorTypeAttr.parallel(), + linalg.IteratorTypeAttr.parallel(), + linalg.IteratorTypeAttr.reduction(), + ), + ) + with ImplicitBuilder(body2) as (a_val2, b_val2, acc_old_val2): + prod_val2 = arith.Mulf(a_val2, b_val2).result + acc_new_val2 = arith.Addf(acc_old_val2, prod_val2).result + linalg.YieldOp(acc_new_val2) + # Add more name hints to make it easier to track how values are lowered + a_val2.name_hint = "a" + b_val2.name_hint = "b" + acc_old_val2.name_hint = "acc_old" + prod_val2.name_hint = "prod" + acc_new_val2.name_hint = "acc_new" + func.Return() + + linalg_module = ModuleOp((kernel_op2,)) + my_linalg_to_snitch = PipelinePass( + [ + convert_linalg_to_memref_stream.ConvertLinalgToMemrefStreamPass(), + arith_add_fastmath.AddArithFastMathFlagsPass(), + *TEST_OPTIMISE_MEMREF_STREAM, + *TEST_LOWER_MEMREF_STREAM_TO_SNITCH_STREAM, + convert_riscv_scf_for_to_frep.ConvertRiscvScfForToFrepPass(), + *TEST_LOWER_SNITCH_STREAM_TO_ASM_PASSES, + ] + ) + + my_asm_module = run_thru_all_passes( + my_linalg_to_snitch.passes, + linalg_module, + ctx, + ) + + return (linalg_module, my_asm_module, m, n, k) + + +def look_for_frep(module: ModuleOp, output=StringIO()) -> (list, str): + freps = [] + frepsAsHLs = [] + for op in module.body.walk(): + assert isinstance(op, RISCVAsmOperation) or (isinstance(op, FuncOp)), f"{op}" + if op.name == FrepOuter.name: + freps.append(op) + for op in freps: + asm = op.assembly_line() + if asm is not None: + print(asm, file=output) + if "immediate" in op.max_rep.op.attributes: + imm = op.max_rep.op.attributes["immediate"].value.data + print(f"this frep was passed an immediate value of {imm}", file=output) + frepsAsHLs.append( + HardwareLoop(loop_repeats=imm + 1, body_size=op.max_inst) + ) + else: + print(f"ERROR: op.max_rep.op: {op.max_rep.op}", file=output) + print(f"max_inst is {op.max_inst}", file=output) + if len(freps) == 0: + print(f"ERROR no frep found in lowering!!", file=output) + return (frepsAsHLs, output.getvalue()) + + +def look_for_enclosing_loop( + module: ModuleOp, output=StringIO() +) -> (EnclosingSCFLoop, str): + mulsBefore = [] + mulsAfter = [] + sawFrep = False + frepCount = 0 + branches = [] + for op in module.body.walk(): + assert isinstance(op, RISCVAsmOperation) or (isinstance(op, FuncOp)), f"{op}" + if op.name == FrepOuter.name: + frepCount = frepCount + 1 + if isinstance(op, ConditionalBranchOperation): + branches.append(op) + if len(branches) > 1: + print(f"ERROR: more than one branch found!!", file=output) + if frepCount == 0: + print(f"ERROR no frep found in lowering!!", file=output) + if frepCount > 1: + print(f"ERROR more than one frep found in lowering!!", file=output) + if len(branches) == 0: + return (EnclosingSCFLoop(exists=False), output.getvalue()) + limb = branches[0] + print(f"rs1: {limb.rs1}, rs2: {limb.rs2}", file=output) + print(f"rs1.op: {limb.rs1.op}, rs2.op: {limb.rs2.op}", file=output) + if limb.rs2.op.name != LiOp.name: + return (EnclosingSCFLoop(exists=False), output.getvalue()) + imm = limb.rs2.op.attributes["immediate"].value.data + return (EnclosingSCFLoop(limb.name, iters=imm, exists=True), output.getvalue()) + + +def peek_at_lowered_matvec_tiling(matvec: InputMatrix): + expectedFMADDs = matvec.n * matvec.k + # create linalg, then lower to assembly + linalg_mod, asm_mod, m, n, k = createMatmulTransposeB(1, matvec.n, matvec.k) + # look for a hardware loop + loops, commentary = look_for_frep(asm_mod) + if len(loops) != 1: + raise Exception( + f"ERROR: expected only ONE hardware loop, but found {len(loops)} {commentary}" + ) + # look for an enclosing loop + oLoop, commentary2 = look_for_enclosing_loop(asm_mod) + # print(riscv_code(asm_mod)) # DEBUGGING ONLY + # check the number of FMADDs is as expected + res = expectedFMADDs == ( + (loops[0].body_size * loops[0].loop_repeats) * oLoop.iters * 8 + ) + if not oLoop.exists: + res = expectedFMADDs == (n * k * 8) + return (res, loops[0], oLoop) + + +def run_thru_all_passes( + passes: tuple[ModulePass, ...], module: ModuleOp, ctx: MLContext +) -> ModuleOp: + res = module.clone() + for p in passes: + p.apply(ctx, res) + return res + + +def main(): + print("\n|----------- HOODLE ----------|", end="\n") + print(check_lowered_matvec_tiling(0, 40, 100)) + print(check_lowered_matvec_tiling(0, 64, 80)) + + +if __name__ == "__main__": + main() diff --git a/myrtle/myrtle/tile_gen/tile_size_generator.py b/myrtle/myrtle/tile_gen/tile_size_generator.py new file mode 100644 index 00000000..e63500f3 --- /dev/null +++ b/myrtle/myrtle/tile_gen/tile_size_generator.py @@ -0,0 +1,246 @@ +from dataclasses import dataclass, field +import pandas as pd +import sys +from itertools import product +import tile_SA.quidditch_load_counting as qlc +from tile_SA.utils import InputMatrix, TileSizes, roundUpToNearestMultipleOf +# @dataclass +# class InputMatrix: +# """Class for keeping track of matrix dimensions in""" +# """matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul)""" +# n: int = 1200 +# k: int = 400 + + +class TileSizeGenerator: + def __init__(self, outputVectorEltCount, inputVectorEltCount, dispatchName=""): + self.me = InputMatrix(n=outputVectorEltCount, k=inputVectorEltCount) + self.dispatchName = dispatchName + + def dividesIntoN(self, num): + return self.me.n % num == 0 + + def dividesIntoK(self, num): + return self.me.k % num == 0 + + def myfunc(self): + print(f"I am {self.me}") + self.validOptions() + + def rowDimOptions(self): + hardware_loop_body_options = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + byEight = list(map(lambda x: 8 * x, hardware_loop_body_options)) + max = self.me.n + min = byEight[0] + exhaustive = list(range(min, max+1, 8)) + return exhaustive + + def reductionDimOptions(self): + max = self.me.k + min = 8 + exhaustive = list(range(min, max + 1)) + if (self.me.k % 2) != 0: + print(f"WARNING: K = {self.me.k} is NOT divisible by 2!") + return exhaustive + + def paddedNDimOptions(self): + hardware_loop_body_options = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + byEight = list(map(lambda x: 8 * x, hardware_loop_body_options)) + max = roundUpToNearestMultipleOf(self.me.n, 8) # "pad" to nearest multiple of 8 + min = byEight[0] + exhaustive = list(range(min, max+1, 8)) + return exhaustive + + def paddedKDimOptions(self): + step = 1 + if self.me.k < 40: + return [self.me.k] + else: # we want about 40 tile sizes to pick from + step = self.me.k // 40 + max = self.me.k + min = 8 + exhaustive = list(range(min, max + 1,step)) + if (self.me.k % 2) != 0: + print(f"WARNING: K = {self.me.k} is NOT divisible by 2!") + + return exhaustive + + def validOptions(self): + # all possible values for n and k + little_n_options = self.rowDimOptions() + little_k_options = self.reductionDimOptions() + # print(f'n options are {list(little_n_options)}') + # print(f'k options are {list(little_k_options)}') + # filter for n's and k's that divide evenly into N and K + little_n_no_pad = list(filter(lambda x: self.dividesIntoN(x), little_n_options)) + if len(little_n_no_pad) <= 1: # prime N dimension, or not divisible by 8 + n_options = self.paddedNDimOptions() + else: + n_options = little_n_no_pad + little_k_no_pad = list(filter(lambda x: self.dividesIntoK(x), little_k_options)) + if len(little_k_no_pad) == 1: # prime K dimension + k_options = self.paddedKDimOptions() + else: + k_options = little_k_no_pad + # have k dim options for double buffering + k_options = list( + filter(lambda x: x <= (self.me.k // 2) + 1, k_options)) + # print(f'now n options are {list(n_options)}') + # print(f'now k options are {list(k_options)}') + options_as_pairs = list(product(n_options, k_options)) + annotated_options = list(map(lambda tup: self.annotateOption(tup), options_as_pairs)) + # filter by size + valid_options = list( + filter(lambda tup: self.smallEnough(tup[0][0], tup[0][1]), annotated_options) + ) + # print(valid_options) + return valid_options + # filter by size + # sizeInfo = list(map(lambda tup: jen.annotateOption(tup), valid_options)) + # # add load counting information + # sizeAndLoadInfo = jen.exportOptionsToCSV(dispatchName, caseNo, sizeInfo) + # extras = jen.addMoreColsForConvenience(sizeAndLoadInfo) + # return(extras) + # return list(product(little_n_no_pad, little_k_no_pad)) + #return list(product(little_n_no_pad, k_dim_halve_options_for_double_buffering)) + + # print("litlle n no padding: [", end="") + # for i in little_n_no_pad: + # print(i, end=", ") + # print("]") + # print("little k no padding:[", end="") + # for i in little_k_no_pad: + # print(i, end=", ") + # print("]") + # # return (little_n_no_pad,little_k_no_pad) + # return list(product(little_n_no_pad, little_k_no_pad)) + + def weightMatTileSize(self, row_dim, reduction_dim): + return row_dim * reduction_dim + + def spaceForTiles(self, row_dim, reduction_dim): + # space in element count + inputVectorTile = 1 * reduction_dim + weightMatTiles = 2 * self.weightMatTileSize(row_dim, reduction_dim) + space = inputVectorTile + weightMatTiles + # space in bytes + spaceInBytes = space * 8 # number of elements * 8 bytes per element + # print(f'0-{48}-{100}:\ninputVectorTile elts: {inputVectorTile} * 8 = {inputVectorTile*8}. \nweightMatTiles elts: {weightMatTiles} * 8 = {weightMatTiles*8}.\ntotal in bytes: {spaceInBytes}.') + return spaceInBytes + + def spaceRemaining(self, row_dim, reduction_dim): + l1MemoryBytes = 100000 + outputMatVec = roundUpToNearestMultipleOf(self.me.n, row_dim) * 8 + # if we padded the row dimension, + # we allocate an extra (unused) buffer of size n + remainder = self.me.n % row_dim + if (remainder != 0): + outputMatVec = outputMatVec + self.me.n*8 + outputFusedAdd = self.me.n * 8 + inputFusedAdd = self.me.n * 8 + remaining = ( + l1MemoryBytes + - outputMatVec + - outputFusedAdd + - inputFusedAdd + - self.spaceForTiles(row_dim, reduction_dim) + ) + return remaining + + def smallEnough(self,row_dim, red_dim): + return self.spaceRemaining(row_dim, red_dim) > 0 + + # annotate a (row_dim, reduction_dim) pair with + # total L1 space used for tiles + # weight matrix tile size + # total spaced used in L1 + # space remaining, etc. + def annotateOption(self, tup): + return ( + tup, + self.spaceForTiles(tup[0], tup[1]), + self.weightMatTileSize(tup[0], tup[1]), + self.spaceRemaining(tup[0], tup[1]), + ) + + # annotate a (row_dim, reduction_dim) pair with + # quidditch load counting information + # flatten tuple + # matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul) + # + # outputVectorEltCount = N (AKA "row_dim") + # inputVectorEltCount = K (AKA "reduction dim") + # + # ex. python3 tile_size_gen.py "dispatch_1" 1200 400 + def flattenThenAnnotateMore(self, ann, caseNo: int): + input = InputMatrix(n=self.me.n, k=self.me.k) + tiles = TileSizes(n=ann[0][0], k=ann[0][1]) + flat = self.convertAnnotationToFlatTuple(ann) + loadInfo = (caseNo,) + qlc.getLoadCountingAnn(input, tiles) + concatted = flat + loadInfo + return concatted + + # helper for converting to CSV + def convertAnnotationToFlatTuple(self, elt): + return ( + f"{0}-{elt[0][0]}-{elt[0][1]}", + elt[0][0], + elt[0][1], + elt[1], + elt[2], + elt[3], + ) + + # helper for converting to CSV + def annotationColumnNames(self): + columns = [ + "JSON Name", + "Row Dim", + "Reduction Dim", + "Space Needed in L1", + "Weight Matrix Tile Size", + "Space Remaining", + ] + return columns + + # export annotated options to CSV + def exportOptionsToCSV(self, dispatchName, caseNo, options): + flat = list(map(lambda tup: self.flattenThenAnnotateMore(tup, caseNo), options)) + cols = ( + self.annotationColumnNames() + ["Case"] + ) + qlc.LoadCountingAnnColumnNames() + df = pd.DataFrame(flat, columns=cols) + df.to_csv( + f"./{dispatchName}_case{caseNo}_searchSpace.csv", + index=False, + ) + print( + f"Saved CSV to ./{dispatchName}_case{caseNo}_searchSpace.csv" + ) + return df + + def tupleIze(a, b): + return (a, b) + + def get_tile_shape(row_dim, col_dim): + if row_dim > col_dim: + shape = "tall" + if row_dim < col_dim: + shape = "wide" + if row_dim == col_dim: + shape = "square" + return shape + + def addMoreColsForConvenience(df): + df["Total Loads"] = df["Regular Loads"] + df["Total Streaming Loads"] + df["Tile Shape"] = df.apply( + lambda x: get_tile_shape(x["Row Dim"], x["Reduction Dim"]), axis=1 + ) + return df + +def main(): + args = sys.argv[1:] + + +if __name__ == "__main__": + main() diff --git a/myrtle/myrtle/tile_sel/README.md b/myrtle/myrtle/tile_sel/README.md new file mode 100644 index 00000000..6b6f69a7 --- /dev/null +++ b/myrtle/myrtle/tile_sel/README.md @@ -0,0 +1,3 @@ +# TODO + +take tile selection logic out of the top level `myrtle.py` file and put it in its own a module here. \ No newline at end of file diff --git a/myrtle/myrtle/tile_sel/dispatch-8-svr.pickle b/myrtle/myrtle/tile_sel/dispatch-8-svr.pickle new file mode 100644 index 0000000000000000000000000000000000000000..9add666bf5ba37b00a70a00cf35802a43e86c6ba GIT binary patch literal 2775 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.10.1", + "pyqt6==6.9.1", + "pyqt6-qt6==6.9.1", + "pyqt6-sip==13.10.2" +] + +[dependency-groups] +dev = [ + "marimo==0.13.11" +] diff --git a/myrtle/requirements.txt b/myrtle/requirements.txt new file mode 100644 index 00000000..2c902d48 --- /dev/null +++ b/myrtle/requirements.txt @@ -0,0 +1,5 @@ +numpy==2.1.2 +pandas==2.2.2 +PyQt6 +scikit-learn==1.7.0 +matplotlib>=3.10.1 \ No newline at end of file diff --git a/myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv new file mode 100644 index 00000000..d67da3c4 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv @@ -0,0 +1,48 @@ +JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time +0-24-8,24,8,3136,192,68064,1,60000,960000,480000,160000,320000,1,3,8,20000,3,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,352490,2162791 +0-24-10,24,10,3920,240,67280,1,48000,960000,480000,160000,320000,1,3,10,16000,3,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,297450,1940589 +0-24-16,24,16,6272,384,64928,1,30000,960000,480000,160000,320000,1,3,16,10000,3,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,235366,1695104 +0-24-20,24,20,7840,480,63360,1,24000,960000,480000,160000,320000,1,3,20,8000,3,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,193627,1526480 +0-24-25,24,25,9800,600,61400,1,19200,960000,480000,160000,320000,1,3,25,6400,3,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,172486,1441904 +0-24-40,24,40,15680,960,55520,1,12000,960000,480000,160000,320000,1,3,40,4000,3,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,139631,1310807 +0-24-50,24,50,19600,1200,51600,1,9600,960000,480000,160000,320000,1,3,50,3200,3,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,128962,1268178 +0-24-80,24,80,31360,1920,39840,1,6000,960000,480000,160000,320000,1,3,80,2000,3,80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,120108,1233181 +0-24-100,24,100,39200,2400,32000,1,4800,960000,480000,160000,320000,1,3,100,1600,3,100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,105934,1178348 +0-40-8,40,8,5184,320,66016,1,60000,960000,480000,96000,384000,1,5,8,12000,5,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,256089,1778007 +0-40-10,40,10,6480,400,64720,1,48000,960000,480000,96000,384000,1,5,10,9600,5,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,221806,1640892 +0-40-16,40,16,10368,640,60832,1,30000,960000,480000,96000,384000,1,5,16,6000,5,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,192875,1525120 +0-40-20,40,20,12960,800,58240,1,24000,960000,480000,96000,384000,1,5,20,4800,5,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,153091,1366126 +0-40-25,40,25,16200,1000,55000,1,19200,960000,480000,96000,384000,1,5,25,3840,5,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,138354,1307151 +0-40-40,40,40,25920,1600,45280,1,12000,960000,480000,96000,384000,1,5,40,2400,5,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,108216,1184379 +0-40-50,40,50,32400,2000,38800,1,9600,960000,480000,96000,384000,1,5,50,1920,5,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,110769,1196184 +0-40-80,40,80,51840,3200,19360,1,6000,960000,480000,96000,384000,1,5,80,1200,5,80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,107146,1180489 +0-40-100,40,100,64800,4000,6400,1,4800,960000,480000,96000,384000,1,5,100,960,5,100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,89720,1111478 +0-48-8,48,8,6208,384,64992,1,60000,960000,480000,80000,400000,1,6,8,10000,6,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,240836,1714663 +0-48-10,48,10,7760,480,63440,1,48000,960000,480000,80000,400000,1,6,10,8000,6,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,204027,1576709 +0-48-16,48,16,12416,768,58784,1,30000,960000,480000,80000,400000,1,6,16,5000,6,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,186784,1500582 +0-48-20,48,20,15520,960,55680,1,24000,960000,480000,80000,400000,1,6,20,4000,6,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,141453,1317217 +0-48-25,48,25,19400,1200,51800,1,19200,960000,480000,80000,400000,1,6,25,3200,6,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,129862,1270904 +0-48-40,48,40,31040,1920,40160,1,12000,960000,480000,80000,400000,1,6,40,2000,6,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106211,1176321 +0-48-50,48,50,38800,2400,32400,1,9600,960000,480000,80000,400000,1,6,50,1600,6,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109976,1191954 +0-48-80,48,80,62080,3840,9120,1,6000,960000,480000,80000,400000,1,6,80,1000,6,80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106735,1177052 +0-80-8,80,8,10304,640,60896,1,60000,960000,480000,96000,384000,2,5,8,6000,10,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,354364,2169091 +0-80-10,80,10,12880,800,58320,1,48000,960000,480000,96000,384000,2,5,10,4800,10,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,285619,1896981 +0-80-16,80,16,20608,1280,50592,1,30000,960000,480000,96000,384000,2,5,16,3000,10,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,190237,1512510 +0-80-20,80,20,25760,1600,45440,1,24000,960000,480000,96000,384000,2,5,20,2400,10,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,153729,1369370 +0-80-25,80,25,32200,2000,39000,1,19200,960000,480000,96000,384000,2,5,25,1920,10,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,135496,1293577 +0-80-40,80,40,51520,3200,19680,1,12000,960000,480000,96000,384000,2,5,40,1200,10,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106161,1174645 +0-80-50,80,50,64400,4000,6800,1,9600,960000,480000,96000,384000,2,5,50,960,10,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,108924,1187560 +0-120-8,120,8,15424,960,55776,1,60000,960000,480000,96000,384000,3,5,8,4000,15,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,328343,2066876 +0-120-10,120,10,19280,1200,51920,1,48000,960000,480000,96000,384000,3,5,10,3200,15,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,265093,1813422 +0-120-16,120,16,30848,1920,40352,1,30000,960000,480000,96000,384000,3,5,16,2000,15,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,169280,1429097 +0-120-20,120,20,38560,2400,32640,1,24000,960000,480000,96000,384000,3,5,20,1600,15,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,141036,1317755 +0-120-25,120,25,48200,3000,23000,1,19200,960000,480000,96000,384000,3,5,25,1280,15,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,125798,1256802 +0-200-8,200,8,25664,1600,45536,1,60000,960000,480000,96000,384000,5,5,8,2400,25,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,322060,2040075 +0-200-10,200,10,32080,2000,39120,1,48000,960000,480000,96000,384000,5,5,10,1920,25,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,262347,1800347 +0-200-16,200,16,51328,3200,19872,1,30000,960000,480000,96000,384000,5,5,16,1200,25,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,165065,1411802 +0-200-20,200,20,64160,4000,7040,1,24000,960000,480000,96000,384000,5,5,20,960,25,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,138296,1304769 +0-240-8,240,8,30784,1920,40416,1,60000,960000,480000,96000,384000,6,5,8,2000,30,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,324115,2047464 +0-240-10,240,10,38480,2400,32720,1,48000,960000,480000,96000,384000,6,5,10,1600,30,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,264770,1810659 +0-240-16,240,16,61568,3840,9632,1,30000,960000,480000,96000,384000,6,5,16,1000,30,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,167242,1422605 +0-400-8,400,8,51264,3200,19936,1,60000,960000,480000,96000,384000,10,5,8,1200,50,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,344388,2129393 +0-400-10,400,10,64080,4000,7120,1,48000,960000,480000,96000,384000,10,5,10,960,50,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,283504,1885686 diff --git a/myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv new file mode 100644 index 00000000..cdc256e4 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv @@ -0,0 +1,4 @@ +JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time +0-40-23,40,23,14904,920,75496,1,2800,128800,64400,12880,51520,1,5,23,560,5,23,main$async_dispatch_0_matmul_transpose_b_1x400x161_f64,90606,1128198 +0-80-23,80,23,29624,1840,60776,1,2800,128800,64400,12880,51520,2,5,23,280,10,23,main$async_dispatch_0_matmul_transpose_b_1x400x161_f64,89953,1125444 +0-200-23,200,23,73784,4600,16616,1,2800,128800,64400,12880,51520,5,5,23,112,25,23,main$async_dispatch_0_matmul_transpose_b_1x400x161_f64,90235,1124716 diff --git a/myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv new file mode 100644 index 00000000..677260c6 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv @@ -0,0 +1,32 @@ +JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time +0-24-8,24,8,3136,192,82464,1,30000,480000,240000,80000,160000,1,3,8,10000,3,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90614,1248502 +0-24-10,24,10,3920,240,81680,1,24000,480000,240000,80000,160000,1,3,10,8000,3,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90614,1221491 +0-24-16,24,16,6272,384,79328,1,15000,480000,240000,80000,160000,1,3,16,5000,3,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90506,1188868 +0-24-20,24,20,7840,480,77760,1,12000,480000,240000,80000,160000,1,3,20,4000,3,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90558,1167912 +0-24-25,24,25,9800,600,75800,1,9600,480000,240000,80000,160000,1,3,25,3200,3,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90645,1155776 +0-24-40,24,40,15680,960,69920,1,6000,480000,240000,80000,160000,1,3,40,2000,3,40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90059,1136797 +0-24-50,24,50,19600,1200,66000,1,4800,480000,240000,80000,160000,1,3,50,1600,3,50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90019,1132348 +0-24-80,24,80,31360,1920,54240,1,3000,480000,240000,80000,160000,1,3,80,1000,3,80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90100,1125616 +0-24-100,24,100,39200,2400,46400,1,2400,480000,240000,80000,160000,1,3,100,800,3,100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90083,1119527 +0-24-200,24,200,78400,4800,7200,1,1200,480000,240000,80000,160000,1,3,200,400,3,200,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90378,1115541 +0-40-8,40,8,5184,320,80416,1,30000,480000,240000,48000,192000,1,5,8,6000,5,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1202191 +0-40-10,40,10,6480,400,79120,1,24000,480000,240000,48000,192000,1,5,10,4800,5,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1181151 +0-40-16,40,16,10368,640,75232,1,15000,480000,240000,48000,192000,1,5,16,3000,5,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1165214 +0-40-20,40,20,12960,800,72640,1,12000,480000,240000,48000,192000,1,5,20,2400,5,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1144447 +0-40-25,40,25,16200,1000,69400,1,9600,480000,240000,48000,192000,1,5,25,1920,5,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89802,1133899 +0-40-40,40,40,25920,1600,59680,1,6000,480000,240000,48000,192000,1,5,40,1200,5,40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89802,1118890 +0-40-50,40,50,32400,2000,53200,1,4800,480000,240000,48000,192000,1,5,50,960,5,50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1121403 +0-40-80,40,80,51840,3200,33760,1,3000,480000,240000,48000,192000,1,5,80,600,5,80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89871,1121450 +0-40-100,40,100,64800,4000,20800,1,2400,480000,240000,48000,192000,1,5,100,480,5,100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89871,1111783 +0-120-8,120,8,15424,960,70176,1,30000,480000,240000,48000,192000,3,5,8,2000,15,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89827,1240472 +0-120-10,120,10,19280,1200,66320,1,24000,480000,240000,48000,192000,3,5,10,1600,15,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89777,1209024 +0-120-16,120,16,30848,1920,54752,1,15000,480000,240000,48000,192000,3,5,16,1000,15,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89777,1157826 +0-120-20,120,20,38560,2400,47040,1,12000,480000,240000,48000,192000,3,5,20,800,15,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89790,1141866 +0-120-25,120,25,48200,3000,37400,1,9600,480000,240000,48000,192000,3,5,25,640,15,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89827,1131593 +0-120-40,120,40,77120,4800,8480,1,6000,480000,240000,48000,192000,3,5,40,400,15,40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89780,1117408 +0-200-8,200,8,25664,1600,59936,1,30000,480000,240000,48000,192000,5,5,8,1200,25,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89855,1245643 +0-200-10,200,10,32080,2000,53520,1,24000,480000,240000,48000,192000,5,5,10,960,25,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89855,1213836 +0-200-16,200,16,51328,3200,34272,1,15000,480000,240000,48000,192000,5,5,16,600,25,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89771,1156004 +0-200-20,200,20,64160,4000,21440,1,12000,480000,240000,48000,192000,5,5,20,480,25,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89783,1143313 +0-200-25,200,25,80200,5000,5400,1,9600,480000,240000,48000,192000,5,5,25,384,25,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89842,1133160 +0-600-8,600,8,76864,4800,8736,1,30000,480000,240000,48000,192000,15,5,8,400,75,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89771,1196145 diff --git a/myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv new file mode 100644 index 00000000..6a46ca45 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv @@ -0,0 +1,48 @@ +JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time +0-24-8,24,8,3136,192,82464,1,45000,720000,360000,120000,240000,1,3,8,15000,3,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90278,1317337 +0-24-10,24,10,3920,240,81680,1,36000,720000,360000,120000,240000,1,3,10,12000,3,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90276,1277927 +0-24-12,24,12,4704,288,80896,1,30000,720000,360000,120000,240000,1,3,12,10000,3,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90272,1258100 +0-24-15,24,15,5880,360,79720,1,24000,720000,360000,120000,240000,1,3,15,8000,3,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90143,1222605 +0-24-20,24,20,7840,480,77760,1,18000,720000,360000,120000,240000,1,3,20,6000,3,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90272,1200568 +0-24-24,24,24,9408,576,76192,1,15000,720000,360000,120000,240000,1,3,24,5000,3,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90234,1178265 +0-24-25,24,25,9800,600,75800,1,14400,720000,360000,120000,240000,1,3,25,4800,3,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90267,1178109 +0-24-30,24,30,11760,720,73840,1,12000,720000,360000,120000,240000,1,3,30,4000,3,30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90143,1166853 +0-24-40,24,40,15680,960,69920,1,9000,720000,360000,120000,240000,1,3,40,3000,3,40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90279,1150626 +0-24-50,24,50,19600,1200,66000,1,7200,720000,360000,120000,240000,1,3,50,2400,3,50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90272,1151103 +0-24-60,24,60,23520,1440,62080,1,6000,720000,360000,120000,240000,1,3,60,2000,3,60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90189,1137990 +0-24-75,24,75,29400,1800,56200,1,4800,720000,360000,120000,240000,1,3,75,1600,3,75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90114,1130408 +0-24-100,24,100,39200,2400,46400,1,3600,720000,360000,120000,240000,1,3,100,1200,3,100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90066,1125137 +0-24-120,24,120,47040,2880,38560,1,3000,720000,360000,120000,240000,1,3,120,1000,3,120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90115,1123133 +0-24-150,24,150,58800,3600,26800,1,2400,720000,360000,120000,240000,1,3,150,800,3,150,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90292,1120558 +0-24-200,24,200,78400,4800,7200,1,1800,720000,360000,120000,240000,1,3,200,600,3,200,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90305,1117767 +0-40-8,40,8,5184,320,80416,1,45000,720000,360000,72000,288000,1,5,8,9000,5,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1245536 +0-40-10,40,10,6480,400,79120,1,36000,720000,360000,72000,288000,1,5,10,7200,5,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1217497 +0-40-12,40,12,7776,480,77824,1,30000,720000,360000,72000,288000,1,5,12,6000,5,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89834,1194662 +0-40-15,40,15,9720,600,75880,1,24000,720000,360000,72000,288000,1,5,15,4800,5,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89772,1180010 +0-40-20,40,20,12960,800,72640,1,18000,720000,360000,72000,288000,1,5,20,3600,5,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89768,1159836 +0-40-24,40,24,15552,960,70048,1,15000,720000,360000,72000,288000,1,5,24,3000,5,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89834,1150147 +0-40-25,40,25,16200,1000,69400,1,14400,720000,360000,72000,288000,1,5,25,2880,5,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1149929 +0-40-30,40,30,19440,1200,66160,1,12000,720000,360000,72000,288000,1,5,30,2400,5,30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1140039 +0-40-40,40,40,25920,1600,59680,1,9000,720000,360000,72000,288000,1,5,40,1800,5,40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89814,1125071 +0-40-50,40,50,32400,2000,53200,1,7200,720000,360000,72000,288000,1,5,50,1440,5,50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1127722 +0-40-60,40,60,38880,2400,46720,1,6000,720000,360000,72000,288000,1,5,60,1200,5,60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89834,1122734 +0-40-75,40,75,48600,3000,37000,1,4800,720000,360000,72000,288000,1,5,75,960,5,75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89813,1117539 +0-40-100,40,100,64800,4000,20800,1,3600,720000,360000,72000,288000,1,5,100,720,5,100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89871,1111783 +0-40-120,40,120,77760,4800,7840,1,3000,720000,360000,72000,288000,1,5,120,600,5,120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89990,1112946 +0-120-8,120,8,15424,960,70176,1,45000,720000,360000,72000,288000,3,5,8,3000,15,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1307012 +0-120-10,120,10,19280,1200,66320,1,36000,720000,360000,72000,288000,3,5,10,2400,15,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91364,1262146 +0-120-12,120,12,23136,1440,62464,1,30000,720000,360000,72000,288000,3,5,12,2000,15,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90327,1224677 +0-120-15,120,15,28920,1800,56680,1,24000,720000,360000,72000,288000,3,5,15,1600,15,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91364,1192663 +0-120-20,120,20,38560,2400,47040,1,18000,720000,360000,72000,288000,3,5,20,1200,15,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1155408 +0-120-24,120,24,46272,2880,39328,1,15000,720000,360000,72000,288000,3,5,24,1000,15,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1139593 +0-120-25,120,25,48200,3000,37400,1,14400,720000,360000,72000,288000,3,5,25,960,15,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1142824 +0-120-30,120,30,57840,3600,27760,1,12000,720000,360000,72000,288000,3,5,30,800,15,30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91419,1137534 +0-120-40,120,40,77120,4800,8480,1,9000,720000,360000,72000,288000,3,5,40,600,15,40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91366,1122419 +0-200-8,200,8,25664,1600,59936,1,45000,720000,360000,72000,288000,5,5,8,1800,25,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89761,1312768 +0-200-10,200,10,32080,2000,53520,1,36000,720000,360000,72000,288000,5,5,10,1440,25,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89790,1266186 +0-200-12,200,12,38496,2400,47104,1,30000,720000,360000,72000,288000,5,5,12,1200,25,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89598,1227463 +0-200-15,200,15,48120,3000,37480,1,24000,720000,360000,72000,288000,5,5,15,960,25,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89757,1197700 +0-200-20,200,20,64160,4000,21440,1,18000,720000,360000,72000,288000,5,5,20,720,25,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89598,1158318 +0-200-24,200,24,76992,4800,8608,1,15000,720000,360000,72000,288000,5,5,24,600,25,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89598,1141555 +0-200-25,200,25,80200,5000,5400,1,14400,720000,360000,72000,288000,5,5,25,576,25,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89769,1143879 +0-600-8,600,8,76864,4800,8736,1,45000,720000,360000,72000,288000,15,5,8,600,75,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89757,1243185 diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv new file mode 100644 index 00000000..91d8682a --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv @@ -0,0 +1,18 @@ +JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape +0-24-100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,105051,1172888,24,100,39200,2400,32000,1,4800,960000,480000,160000,320000,1,3,100,1600,3,100,964800,wide +0-24-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,139659,1308333,24,40,15680,960,55520,1,12000,960000,480000,160000,320000,1,3,40,4000,3,40,972000,wide +0-24-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,139659,1308333,24,50,19600,1200,51600,1,9600,960000,480000,160000,320000,1,3,50,3200,3,50,969600,wide +0-24-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,351842,2158774,24,8,3136,192,68064,1,60000,960000,480000,160000,320000,1,3,8,20000,3,8,1020000,tall +0-24-80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,120533,1231430,24,80,31360,1920,39840,1,6000,960000,480000,160000,320000,1,3,80,2000,3,80,966000,wide +0-40-100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,89658,1110948,40,100,64800,4000,6400,1,4800,960000,480000,96000,384000,1,5,100,960,5,100,964800,wide +0-40-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,108303,1183714,40,40,25920,1600,45280,1,12000,960000,480000,96000,384000,1,5,40,2400,5,40,972000,square +0-40-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,110811,1194951,40,50,32400,2000,38800,1,9600,960000,480000,96000,384000,1,5,50,1920,5,50,969600,wide +0-40-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,256068,1776377,40,8,5184,320,66016,1,60000,960000,480000,96000,384000,1,5,8,12000,5,8,1020000,tall +0-40-80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,107160,1180624,40,80,51840,3200,19360,1,6000,960000,480000,96000,384000,1,5,80,1200,5,80,966000,wide +0-48-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106206,1176438,48,40,31040,1920,40160,1,12000,960000,480000,80000,400000,1,6,40,2000,6,40,972000,tall +0-48-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109639,1190676,48,50,38800,2400,32400,1,9600,960000,480000,80000,400000,1,6,50,1600,6,50,969600,wide +0-48-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,239465,1708958,48,8,6208,384,64992,1,60000,960000,480000,80000,400000,1,6,8,10000,6,8,1020000,tall +0-48-80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106720,1177628,48,80,62080,3840,9120,1,6000,960000,480000,80000,400000,1,6,80,1000,6,80,966000,wide +0-80-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106157,1176118,80,40,51520,3200,19680,1,12000,960000,480000,96000,384000,2,5,40,1200,10,40,972000,tall +0-80-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109169,1188538,80,50,64400,4000,6800,1,9600,960000,480000,96000,384000,2,5,50,960,10,50,969600,tall +0-80-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,354369,2169751,80,8,10304,640,60896,1,60000,960000,480000,96000,384000,2,5,8,6000,10,8,1020000,tall diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv new file mode 100644 index 00000000..b7a69c73 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv @@ -0,0 +1,42 @@ +JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape +0-24-104,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,133167,1284223,24,104,40768,2496,30432,2,4800,998400,499200,166400,332800,1,3,104,1600,3,104,1003200,wide +0-24-112,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,158363,1398894,24,112,43904,2688,27296,2,4800,1075200,537600,179200,358400,1,3,112,1600,3,112,1080000,wide +0-24-120,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,167288,1419104,24,120,47040,2880,24160,2,4800,1152000,576000,192000,384000,1,3,120,1600,3,120,1156800,wide +0-24-128,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,180448,1490723,24,128,50176,3072,21024,2,4800,1228800,614400,204800,409600,1,3,128,1600,3,128,1233600,wide +0-24-136,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,129958,1271622,24,136,53312,3264,17888,2,3600,979200,489600,163200,326400,1,3,136,1200,3,136,982800,wide +0-24-144,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,149984,1351389,24,144,56448,3456,14752,2,3600,1036800,518400,172800,345600,1,3,144,1200,3,144,1040400,wide +0-24-152,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,160151,1391286,24,152,59584,3648,11616,2,3600,1094400,547200,182400,364800,1,3,152,1200,3,152,1098000,wide +0-24-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,305685,1965176,24,16,6272,384,64928,2,30000,960000,480000,160000,320000,1,3,16,10000,3,16,990000,tall +0-24-160,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,171613,1438302,24,160,62720,3840,8480,2,3600,1152000,576000,192000,384000,1,3,160,1200,3,160,1155600,wide +0-24-168,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,174951,1450470,24,168,65856,4032,5344,2,3600,1209600,604800,201600,403200,1,3,168,1200,3,168,1213200,wide +0-24-176,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109169,1188538,24,176,68992,4224,2208,2,3600,1267200,633600,211200,422400,1,3,176,1200,3,176,1270800,wide +0-24-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,305685,1965176,24,24,9408,576,61792,2,20400,979200,489600,163200,326400,1,3,24,6800,3,24,999600,square +0-24-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,302597,1962088,24,32,12544,768,58656,2,15600,998400,499200,166400,332800,1,3,32,5200,3,32,1014000,wide +0-24-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,244422,1729603,24,48,18816,1152,52384,2,10800,1036800,518400,172800,345600,1,3,48,3600,3,48,1047600,wide +0-24-56,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,228434,1665727,24,56,21952,1344,49248,2,9600,1075200,537600,179200,358400,1,3,56,3200,3,56,1084800,wide +0-24-64,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,244324,1729091,24,64,25088,1536,46112,2,8400,1075200,537600,179200,358400,1,3,64,2800,3,64,1083600,wide +0-24-72,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,200557,1554774,24,72,28224,1728,42976,2,7200,1036800,518400,172800,345600,1,3,72,2400,3,72,1044000,wide +0-24-88,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,155517,1397695,24,88,34496,2112,36704,2,6000,1056000,528000,176000,352000,1,3,88,2000,3,88,1062000,wide +0-24-96,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,229844,1672015,24,96,37632,2304,33568,2,6000,1152000,576000,192000,384000,1,3,96,2000,3,96,1158000,wide +0-40-104,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,133189,1283634,40,104,67392,4160,3808,2,4800,998400,499200,99840,399360,1,5,104,960,5,104,1003200,wide +0-40-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,193104,1522559,40,16,10368,640,60832,2,30000,960000,480000,96000,384000,1,5,16,6000,5,16,990000,tall +0-40-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,238987,1706789,40,24,15552,960,55648,2,20400,979200,489600,97920,391680,1,5,24,4080,5,24,999600,tall +0-40-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,263308,1791252,40,32,20736,1280,50464,2,15600,998400,499200,99840,399360,1,5,32,3120,5,32,1014000,tall +0-40-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,207318,1581715,40,48,31104,1920,40096,2,10800,1036800,518400,103680,414720,1,5,48,2160,5,48,1047600,wide +0-40-56,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,156134,1375192,40,56,36288,2240,34912,2,9600,1075200,537600,107520,430080,1,5,56,1920,5,56,1084800,wide +0-40-64,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,214576,1610608,40,64,41472,2560,29728,2,8400,1075200,537600,107520,430080,1,5,64,1680,5,64,1083600,wide +0-40-72,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,147559,1343601,40,72,46656,2880,24544,2,7200,1036800,518400,103680,414720,1,5,72,1440,5,72,1044000,wide +0-40-88,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,149984,1351389,40,88,57024,3520,14176,2,6000,1056000,528000,105600,422400,1,5,88,1200,5,88,1062000,wide +0-40-96,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,175225,1452056,40,96,62208,3840,8992,2,6000,1152000,576000,115200,460800,1,5,96,1200,5,96,1158000,wide +0-48-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,186746,1498002,48,16,12416,768,58784,2,30000,960000,480000,80000,400000,1,6,16,5000,6,16,990000,tall +0-48-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,219658,1633488,48,24,18624,1152,52576,2,20400,979200,489600,81600,408000,1,6,24,3400,6,24,999600,tall +0-48-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,257192,1780662,48,32,24832,1536,46368,2,15600,998400,499200,83200,416000,1,6,32,2600,6,32,1014000,tall +0-48-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,201405,1554686,48,48,37248,2304,33952,2,10800,1036800,518400,86400,432000,1,6,48,1800,6,48,1047600,square +0-48-56,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,155593,1373233,48,56,43456,2688,27744,2,9600,1075200,537600,89600,448000,1,6,56,1600,6,56,1084800,wide +0-48-64,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,213824,1606760,48,64,49664,3072,21536,2,8400,1075200,537600,89600,448000,1,6,64,1400,6,64,1083600,wide +0-48-72,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,148111,1344135,48,72,55872,3456,15328,2,7200,1036800,518400,86400,432000,1,6,72,1200,6,72,1044000,wide +0-48-88,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109169,1188538,48,88,68288,4224,2912,2,6000,1056000,528000,88000,440000,1,6,88,1000,6,88,1062000,wide +0-80-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,190305,1512268,80,16,20608,1280,50592,2,30000,960000,480000,96000,384000,2,5,16,3000,10,16,990000,tall +0-80-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,200557,1554774,80,24,30912,1920,40288,2,20400,979200,489600,97920,391680,2,5,24,2040,10,24,999600,tall +0-80-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,212468,1602133,80,32,41216,2560,29984,2,15600,998400,499200,99840,399360,2,5,32,1560,10,32,1014000,tall +0-80-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,182434,1481577,80,48,61824,3840,9376,2,10800,1036800,518400,103680,414720,2,5,48,1080,10,48,1047600,tall diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv new file mode 100644 index 00000000..8fc9fd9d --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv @@ -0,0 +1,15 @@ +JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape +0-120-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,67997,1131399,120,25,48200,3000,37400,1,9600,480000,240000,48000,192000,3,5,25,640,15,25,489600,tall +0-120-40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,53171,1116304,120,40,77120,4800,8480,1,6000,480000,240000,48000,192000,3,5,40,400,15,40,486000,tall +0-200-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,69553,1131953,200,25,80200,5000,5400,1,9600,480000,240000,48000,192000,5,5,25,384,25,25,489600,tall +0-24-100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,54441,1118477,24,100,39200,2400,46400,1,2400,480000,240000,80000,160000,1,3,100,800,3,100,482400,wide +0-24-200,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,49150,1112587,24,200,78400,4800,7200,1,1200,480000,240000,80000,160000,1,3,200,400,3,200,481200,wide +0-24-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89734,1153158,24,25,9800,600,75800,1,9600,480000,240000,80000,160000,1,3,25,3200,3,25,489600,wide +0-24-40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,72175,1134625,24,40,15680,960,69920,1,6000,480000,240000,80000,160000,1,3,40,2000,3,40,486000,wide +0-24-50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,66757,1128811,24,50,19600,1200,66000,1,4800,480000,240000,80000,160000,1,3,50,1600,3,50,484800,wide +0-24-80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,61675,1123274,24,80,31360,1920,54240,1,3000,480000,240000,80000,160000,1,3,80,1000,3,80,483000,wide +0-40-100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,46812,1110948,40,100,64800,4000,20800,1,2400,480000,240000,48000,192000,1,5,100,480,5,100,482400,wide +0-40-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,71741,1133934,40,25,16200,1000,69400,1,9600,480000,240000,48000,192000,1,5,25,1920,5,25,489600,tall +0-40-40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,56816,1118816,40,40,25920,1600,59680,1,6000,480000,240000,48000,192000,1,5,40,1200,5,40,486000,square +0-40-50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,57551,1120483,40,50,32400,2000,53200,1,4800,480000,240000,48000,192000,1,5,50,960,5,50,484800,wide +0-40-80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,55623,1119061,40,80,51840,3200,33760,1,3000,480000,240000,48000,192000,1,5,80,600,5,80,483000,wide diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv new file mode 100644 index 00000000..564e1123 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv @@ -0,0 +1,34 @@ +JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape +0-120-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,105501,1169813,120,22,42416,2640,43184,2,11400,501600,250800,50160,200640,3,5,22,760,15,22,513000,tall +0-120-33,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,93178,1157490,120,33,63624,3960,21976,2,7800,514800,257400,51480,205920,3,5,33,520,15,33,522600,tall +0-120-44,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,87587,1152136,120,44,84832,5280,768,2,6000,528000,264000,52800,211200,3,5,44,400,15,44,534000,tall +0-200-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,111430,1179508,200,22,70576,4400,15024,2,11400,501600,250800,50160,200640,5,5,22,456,25,22,513000,tall +0-24-107,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,79894,1143608,24,107,41944,2568,43656,2,2400,513600,256800,85600,171200,1,3,107,800,3,107,516000,wide +0-24-117,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,85151,1147681,24,117,45864,2808,39736,2,2400,561600,280800,93600,187200,1,3,117,800,3,117,564000,wide +0-24-127,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,91451,1153981,24,127,49784,3048,35816,2,2400,609600,304800,101600,203200,1,3,127,800,3,127,612000,wide +0-24-137,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,68663,1131824,24,137,53704,3288,31896,2,1800,493200,246600,82200,164400,1,3,137,600,3,137,495000,wide +0-24-147,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,71033,1134194,24,147,57624,3528,27976,2,1800,529200,264600,88200,176400,1,3,147,600,3,147,531000,wide +0-24-157,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,85364,1147762,24,157,61544,3768,24056,2,1800,565200,282600,94200,188400,1,3,157,600,3,157,567000,wide +0-24-167,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89767,1152928,24,167,65464,4008,20136,2,1800,601200,300600,100200,200400,1,3,167,600,3,167,603000,wide +0-24-177,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,95301,1158097,24,177,69384,4248,16216,2,1800,637200,318600,106200,212400,1,3,177,600,3,177,639000,wide +0-24-187,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,99394,1163888,24,187,73304,4488,12296,2,1800,673200,336600,112200,224400,1,3,187,600,3,187,675000,wide +0-24-197,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,104523,1167319,24,197,77224,4728,8376,2,1800,709200,354600,118200,236400,1,3,197,600,3,197,711000,wide +0-24-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,161762,1225886,24,22,8624,528,76976,2,11400,501600,250800,83600,167200,1,3,22,3800,3,22,513000,tall +0-24-33,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,140831,1203903,24,33,12936,792,72664,2,7800,514800,257400,85800,171600,1,3,33,2600,3,33,522600,wide +0-24-44,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,126053,1190165,24,44,17248,1056,68352,2,6000,528000,264000,88000,176000,1,3,44,2000,3,44,534000,wide +0-24-55,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,117045,1179445,24,55,21560,1320,64040,2,4800,528000,264000,88000,176000,1,3,55,1600,3,55,532800,wide +0-24-65,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,114246,1177077,24,65,25480,1560,60120,2,4200,546000,273000,91000,182000,1,3,65,1400,3,65,550200,wide +0-24-75,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,103858,1167953,24,75,29400,1800,56200,2,3600,540000,270000,90000,180000,1,3,75,1200,3,75,543600,wide +0-24-86,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,80593,1144043,24,86,33712,2064,51888,2,3000,516000,258000,86000,172000,1,3,86,1000,3,86,519000,wide +0-24-96,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,116965,1180497,24,96,37632,2304,47968,2,3000,576000,288000,96000,192000,1,3,96,1000,3,96,579000,wide +0-40-107,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,71827,1136510,40,107,69336,4280,16264,2,2400,513600,256800,51360,205440,1,5,107,480,5,107,516000,wide +0-40-117,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,81756,1146434,40,117,75816,4680,9784,2,2400,561600,280800,56160,224640,1,5,117,480,5,117,564000,wide +0-40-127,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,87309,1151987,40,127,82296,5080,3304,2,2400,609600,304800,60960,243840,1,5,127,480,5,127,612000,wide +0-40-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,120672,1182193,40,22,14256,880,71344,2,11400,501600,250800,50160,200640,1,5,22,2280,5,22,513000,tall +0-40-33,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,112161,1175801,40,33,21384,1320,64216,2,7800,514800,257400,51480,205920,1,5,33,1560,5,33,522600,tall +0-40-44,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,103664,1165185,40,44,28512,1760,57088,2,6000,528000,264000,52800,211200,1,5,44,1200,5,44,534000,wide +0-40-55,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,84042,1146926,40,55,35640,2200,49960,2,4800,528000,264000,52800,211200,1,5,55,960,5,55,532800,wide +0-40-65,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,84394,1149077,40,65,42120,2600,43480,2,4200,546000,273000,54600,218400,1,5,65,840,5,65,550200,wide +0-40-75,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,81960,1146638,40,75,48600,3000,37000,2,3600,540000,270000,54000,216000,1,5,75,720,5,75,543600,wide +0-40-86,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,77634,1142317,40,86,55728,3440,29872,2,3000,516000,258000,51600,206400,1,5,86,600,5,86,519000,wide +0-40-96,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89566,1153034,40,96,62208,3840,23392,2,3000,576000,288000,57600,230400,1,5,96,600,5,96,579000,wide diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv new file mode 100644 index 00000000..b72c8025 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv @@ -0,0 +1,31 @@ +JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape +0-24-10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,243499,1284237,24,10,3920,240,81680,1,36000,720000,360000,120000,240000,1,3,10,12000,3,10,756000,tall +0-24-100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,80994,1122125,24,100,39200,2400,46400,1,3600,720000,360000,120000,240000,1,3,100,1200,3,100,723600,wide +0-24-12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,217262,1258257,24,12,4704,288,80896,1,30000,720000,360000,120000,240000,1,3,12,10000,3,12,750000,tall +0-24-120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,79292,1119386,24,120,47040,2880,38560,1,3000,720000,360000,120000,240000,1,3,120,1000,3,120,723000,wide +0-24-15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,191694,1232673,24,15,5880,360,79720,1,24000,720000,360000,120000,240000,1,3,15,8000,3,15,744000,tall +0-24-150,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,76490,1118136,24,150,58800,3600,26800,1,2400,720000,360000,120000,240000,1,3,150,800,3,150,722400,wide +0-24-20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,148969,1190920,24,20,7840,480,77760,1,18000,720000,360000,120000,240000,1,3,20,6000,3,20,738000,tall +0-24-200,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,72988,1112697,24,200,78400,4800,7200,1,1800,720000,360000,120000,240000,1,3,200,600,3,200,721800,wide +0-24-24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,134705,1174568,24,24,9408,576,76192,1,15000,720000,360000,120000,240000,1,3,24,5000,3,24,735000,square +0-24-25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,144643,1185966,24,25,9800,600,75800,1,14400,720000,360000,120000,240000,1,3,25,4800,3,25,734400,wide +0-24-30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,130217,1171196,24,30,11760,720,73840,1,12000,720000,360000,120000,240000,1,3,30,4000,3,30,732000,wide +0-24-40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,117565,1159445,24,40,15680,960,69920,1,9000,720000,360000,120000,240000,1,3,40,3000,3,40,729000,wide +0-24-50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,105382,1146377,24,50,19600,1200,66000,1,7200,720000,360000,120000,240000,1,3,50,2400,3,50,727200,wide +0-24-60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,93493,1133403,24,60,23520,1440,62080,1,6000,720000,360000,120000,240000,1,3,60,2000,3,60,726000,wide +0-24-75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,88898,1129237,24,75,29400,1800,56200,1,4800,720000,360000,120000,240000,1,3,75,1600,3,75,724800,wide +0-24-8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,310238,1353027,24,8,3136,192,82464,1,45000,720000,360000,120000,240000,1,3,8,15000,3,8,765000,tall +0-40-10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,174619,1214935,40,10,6480,400,79120,1,36000,720000,360000,72000,288000,1,5,10,7200,5,10,756000,tall +0-40-100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,69121,1110948,40,100,64800,4000,20800,1,3600,720000,360000,72000,288000,1,5,100,720,5,100,723600,wide +0-40-12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,152380,1192216,40,12,7776,480,77824,1,30000,720000,360000,72000,288000,1,5,12,6000,5,12,750000,tall +0-40-120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,70917,1111317,40,120,77760,4800,7840,1,3000,720000,360000,72000,288000,1,5,120,600,5,120,723000,wide +0-40-15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,137285,1178124,40,15,9720,600,75880,1,24000,720000,360000,72000,288000,1,5,15,4800,5,15,744000,tall +0-40-20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,119287,1159603,40,20,12960,800,72640,1,18000,720000,360000,72000,288000,1,5,20,3600,5,20,738000,tall +0-40-24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,108786,1148622,40,24,15552,960,70048,1,15000,720000,360000,72000,288000,1,5,24,3000,5,24,735000,tall +0-40-25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,107000,1147316,40,25,16200,1000,69400,1,14400,720000,360000,72000,288000,1,5,25,2880,5,25,734400,tall +0-40-30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,98109,1138425,40,30,19440,1200,66160,1,12000,720000,360000,72000,288000,1,5,30,2400,5,30,732000,tall +0-40-40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,84547,1123932,40,40,25920,1600,59680,1,9000,720000,360000,72000,288000,1,5,40,1800,5,40,729000,square +0-40-50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,85386,1126892,40,50,32400,2000,53200,1,7200,720000,360000,72000,288000,1,5,50,1440,5,50,727200,wide +0-40-60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,80221,1120055,40,60,38880,2400,46720,1,6000,720000,360000,72000,288000,1,5,60,1200,5,60,726000,wide +0-40-75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,76421,1117436,40,75,48600,3000,37000,1,4800,720000,360000,72000,288000,1,5,75,960,5,75,724800,wide +0-40-8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,204744,1246250,40,8,5184,320,80416,1,45000,720000,360000,72000,288000,1,5,8,9000,5,8,765000,tall diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv new file mode 100644 index 00000000..9ad9f5d2 --- /dev/null +++ b/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv @@ -0,0 +1,35 @@ +JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape +0-24-104,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,109765,1151146,24,104,40768,2496,44832,2,3600,748800,374400,124800,249600,1,3,104,1200,3,104,752400,wide +0-24-112,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,134151,1175532,24,112,43904,2688,41696,2,3600,806400,403200,134400,268800,1,3,112,1200,3,112,810000,wide +0-24-128,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,121256,1161864,24,128,50176,3072,35424,2,3000,768000,384000,128000,256000,1,3,128,1000,3,128,771000,wide +0-24-136,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,117771,1157479,24,136,53312,3264,32288,2,3000,816000,408000,136000,272000,1,3,136,1000,3,136,819000,wide +0-24-144,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,124479,1164202,24,144,56448,3456,29152,2,3000,864000,432000,144000,288000,1,3,144,1000,3,144,867000,wide +0-24-152,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,98810,1138878,24,152,59584,3648,26016,2,2400,729600,364800,121600,243200,1,3,152,800,3,152,732000,wide +0-24-16,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,287527,1328554,24,16,6272,384,79328,2,22800,729600,364800,121600,243200,1,3,16,7600,3,16,752400,tall +0-24-160,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,113007,1153075,24,160,62720,3840,22880,2,2400,768000,384000,128000,256000,1,3,160,800,3,160,770400,wide +0-24-168,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,115977,1156029,24,168,65856,4032,19744,2,2400,806400,403200,134400,268800,1,3,168,800,3,168,808800,wide +0-24-176,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,121563,1161599,24,176,68992,4224,16608,2,2400,844800,422400,140800,281600,1,3,176,800,3,176,847200,wide +0-24-184,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,126018,1166054,24,184,72128,4416,13472,2,2400,883200,441600,147200,294400,1,3,184,800,3,184,885600,wide +0-24-192,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,132705,1171465,24,192,75264,4608,10336,2,2400,921600,460800,153600,307200,1,3,192,800,3,192,924000,wide +0-24-208,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,99032,1139284,24,208,81536,4992,4064,2,1800,748800,374400,124800,249600,1,3,208,600,3,208,750600,wide +0-24-216,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,100894,1141146,24,216,84672,5184,928,2,1800,777600,388800,129600,259200,1,3,216,600,3,216,779400,wide +0-24-32,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,224213,1263544,24,32,12544,768,73056,2,11400,729600,364800,121600,243200,1,3,32,3800,3,32,741000,wide +0-24-48,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,179036,1220109,24,48,18816,1152,66784,2,7800,748800,374400,124800,249600,1,3,48,2600,3,48,756600,wide +0-24-56,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,161788,1201559,24,56,21952,1344,63648,2,6600,739200,369600,123200,246400,1,3,56,2200,3,56,745800,wide +0-24-64,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,176112,1216464,24,64,25088,1536,60512,2,6000,768000,384000,128000,256000,1,3,64,2000,3,64,774000,wide +0-24-72,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,154947,1195820,24,72,28224,1728,57376,2,5400,777600,388800,129600,259200,1,3,72,1800,3,72,783000,wide +0-24-80,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,157295,1197742,24,80,31360,1920,54240,2,4800,768000,384000,128000,256000,1,3,80,1600,3,80,772800,wide +0-24-88,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,115001,1156337,24,88,34496,2112,51104,2,4200,739200,369600,123200,246400,1,3,88,1400,3,88,743400,wide +0-24-96,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,163048,1203343,24,96,37632,2304,47968,2,4200,806400,403200,134400,268800,1,3,96,1400,3,96,810600,wide +0-40-104,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,102441,1144325,40,104,67392,4160,18208,2,3600,748800,374400,74880,299520,1,5,104,720,5,104,752400,wide +0-40-112,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,129677,1170250,40,112,72576,4480,13024,2,3600,806400,403200,80640,322560,1,5,112,720,5,112,810000,wide +0-40-128,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,110867,1151688,40,128,82944,5120,2656,2,3000,768000,384000,76800,307200,1,5,128,600,5,128,771000,wide +0-40-16,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,230363,1271509,40,16,10368,640,75232,2,22800,729600,364800,72960,291840,1,5,16,4560,5,16,752400,tall +0-40-32,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,190282,1231428,40,32,20736,1280,64864,2,11400,729600,364800,72960,291840,1,5,32,2280,5,32,741000,tall +0-40-48,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,151657,1192577,40,48,31104,1920,54496,2,7800,748800,374400,74880,299520,1,5,48,1560,5,48,756600,wide +0-40-56,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,113118,1154263,40,56,36288,2240,49312,2,6600,739200,369600,73920,295680,1,5,56,1320,5,56,745800,wide +0-40-64,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,154903,1196590,40,64,41472,2560,44128,2,6000,768000,384000,76800,307200,1,5,64,1200,5,64,774000,wide +0-40-72,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,112895,1154786,40,72,46656,2880,38944,2,5400,777600,388800,77760,311040,1,5,72,1080,5,72,783000,wide +0-40-80,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,140450,1180955,40,80,51840,3200,33760,2,4800,768000,384000,76800,307200,1,5,80,960,5,80,772800,wide +0-40-88,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,103232,1144011,40,88,57024,3520,28576,2,4200,739200,369600,73920,295680,1,5,88,840,5,88,743400,wide +0-40-96,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,122001,1163885,40,96,62208,3840,23392,2,4200,806400,403200,80640,322560,1,5,96,840,5,96,810600,wide diff --git a/myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv b/myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv new file mode 100644 index 00000000..6632fc69 --- /dev/null +++ b/myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv @@ -0,0 +1,316 @@ +kernels,linalg_xdsl +matmul_transb 1x100x1xf64,437 +matmul_transb 1x100x2xf64,454 +matmul_transb 1x100x3xf64,450 +matmul_transb 1x100x4xf64,462 +matmul_transb 1x100x5xf64,560 +matmul_transb 1x100x6xf64,667 +matmul_transb 1x100x7xf64,766 +matmul_transb 1x100x8xf64,885 +matmul_transb 1x100x9xf64,1281 +matmul_transb 1x105x1xf64,457 +matmul_transb 1x105x2xf64,474 +matmul_transb 1x105x3xf64,470 +matmul_transb 1x105x4xf64,481 +matmul_transb 1x105x5xf64,585 +matmul_transb 1x105x6xf64,697 +matmul_transb 1x105x7xf64,801 +matmul_transb 1x105x8xf64,925 +matmul_transb 1x105x9xf64,1341 +matmul_transb 1x110x1xf64,477 +matmul_transb 1x110x2xf64,494 +matmul_transb 1x110x3xf64,490 +matmul_transb 1x110x4xf64,501 +matmul_transb 1x110x5xf64,610 +matmul_transb 1x110x6xf64,727 +matmul_transb 1x110x7xf64,836 +matmul_transb 1x110x8xf64,965 +matmul_transb 1x110x9xf64,1401 +matmul_transb 1x115x1xf64,497 +matmul_transb 1x115x2xf64,514 +matmul_transb 1x115x3xf64,510 +matmul_transb 1x115x4xf64,521 +matmul_transb 1x115x5xf64,635 +matmul_transb 1x115x6xf64,757 +matmul_transb 1x115x7xf64,871 +matmul_transb 1x115x8xf64,1005 +matmul_transb 1x115x9xf64,1461 +matmul_transb 1x120x1xf64,517 +matmul_transb 1x120x2xf64,534 +matmul_transb 1x120x3xf64,530 +matmul_transb 1x120x4xf64,541 +matmul_transb 1x120x5xf64,660 +matmul_transb 1x120x6xf64,787 +matmul_transb 1x120x7xf64,906 +matmul_transb 1x120x8xf64,1045 +matmul_transb 1x120x9xf64,1521 +matmul_transb 1x125x1xf64,537 +matmul_transb 1x125x2xf64,554 +matmul_transb 1x125x3xf64,550 +matmul_transb 1x125x4xf64,561 +matmul_transb 1x125x5xf64,685 +matmul_transb 1x125x6xf64,817 +matmul_transb 1x125x7xf64,941 +matmul_transb 1x125x8xf64,1085 +matmul_transb 1x125x9xf64,1581 +matmul_transb 1x130x1xf64,557 +matmul_transb 1x130x2xf64,574 +matmul_transb 1x130x3xf64,590 +matmul_transb 1x130x4xf64,581 +matmul_transb 1x130x5xf64,706 +matmul_transb 1x130x6xf64,847 +matmul_transb 1x130x7xf64,976 +matmul_transb 1x130x8xf64,1125 +matmul_transb 1x130x9xf64,1636 +matmul_transb 1x135x1xf64,577 +matmul_transb 1x135x2xf64,594 +matmul_transb 1x135x3xf64,610 +matmul_transb 1x135x4xf64,601 +matmul_transb 1x135x5xf64,731 +matmul_transb 1x135x6xf64,877 +matmul_transb 1x135x7xf64,1011 +matmul_transb 1x135x8xf64,1165 +matmul_transb 1x135x9xf64,1696 +matmul_transb 1x140x1xf64,597 +matmul_transb 1x140x2xf64,614 +matmul_transb 1x140x3xf64,630 +matmul_transb 1x140x4xf64,621 +matmul_transb 1x140x5xf64,756 +matmul_transb 1x140x6xf64,907 +matmul_transb 1x140x7xf64,1046 +matmul_transb 1x140x8xf64,1205 +matmul_transb 1x140x9xf64,1756 +matmul_transb 1x145x1xf64,617 +matmul_transb 1x145x2xf64,634 +matmul_transb 1x145x3xf64,650 +matmul_transb 1x145x4xf64,641 +matmul_transb 1x145x5xf64,781 +matmul_transb 1x145x6xf64,937 +matmul_transb 1x145x7xf64,1081 +matmul_transb 1x145x8xf64,1245 +matmul_transb 1x145x9xf64,1816 +matmul_transb 1x150x1xf64,637 +matmul_transb 1x150x2xf64,654 +matmul_transb 1x150x3xf64,670 +matmul_transb 1x150x4xf64,661 +matmul_transb 1x150x5xf64,806 +matmul_transb 1x150x6xf64,967 +matmul_transb 1x150x7xf64,1116 +matmul_transb 1x150x8xf64,1285 +matmul_transb 1x150x9xf64,1876 +matmul_transb 1x155x1xf64,657 +matmul_transb 1x155x2xf64,674 +matmul_transb 1x155x3xf64,690 +matmul_transb 1x155x4xf64,681 +matmul_transb 1x155x5xf64,835 +matmul_transb 1x155x6xf64,997 +matmul_transb 1x155x7xf64,1151 +matmul_transb 1x155x8xf64,1325 +matmul_transb 1x155x9xf64,1936 +matmul_transb 1x160x1xf64,677 +matmul_transb 1x160x2xf64,694 +matmul_transb 1x160x3xf64,710 +matmul_transb 1x160x4xf64,701 +matmul_transb 1x160x5xf64,860 +matmul_transb 1x160x6xf64,1027 +matmul_transb 1x160x7xf64,1186 +matmul_transb 1x160x8xf64,1365 +matmul_transb 1x160x9xf64,1996 +matmul_transb 1x165x1xf64,697 +matmul_transb 1x165x2xf64,714 +matmul_transb 1x165x3xf64,730 +matmul_transb 1x165x4xf64,721 +matmul_transb 1x165x5xf64,885 +matmul_transb 1x165x6xf64,1057 +matmul_transb 1x165x7xf64,1221 +matmul_transb 1x165x8xf64,1405 +matmul_transb 1x165x9xf64,2056 +matmul_transb 1x170x1xf64,717 +matmul_transb 1x170x2xf64,734 +matmul_transb 1x170x3xf64,750 +matmul_transb 1x170x4xf64,741 +matmul_transb 1x170x5xf64,915 +matmul_transb 1x170x6xf64,1087 +matmul_transb 1x170x7xf64,1256 +matmul_transb 1x170x8xf64,1445 +matmul_transb 1x170x9xf64,2116 +matmul_transb 1x175x1xf64,737 +matmul_transb 1x175x2xf64,753 +matmul_transb 1x175x3xf64,770 +matmul_transb 1x175x4xf64,761 +matmul_transb 1x175x5xf64,935 +matmul_transb 1x175x6xf64,1117 +matmul_transb 1x175x7xf64,1291 +matmul_transb 1x175x8xf64,1485 +matmul_transb 1x175x9xf64,2176 +matmul_transb 1x180x1xf64,757 +matmul_transb 1x180x2xf64,773 +matmul_transb 1x180x3xf64,790 +matmul_transb 1x180x4xf64,781 +matmul_transb 1x180x5xf64,960 +matmul_transb 1x180x6xf64,1147 +matmul_transb 1x180x7xf64,1326 +matmul_transb 1x180x8xf64,1525 +matmul_transb 1x180x9xf64,2236 +matmul_transb 1x185x1xf64,777 +matmul_transb 1x185x2xf64,793 +matmul_transb 1x185x3xf64,810 +matmul_transb 1x185x4xf64,801 +matmul_transb 1x185x5xf64,985 +matmul_transb 1x185x6xf64,1177 +matmul_transb 1x185x7xf64,1361 +matmul_transb 1x185x8xf64,1565 +matmul_transb 1x185x9xf64,2296 +matmul_transb 1x190x1xf64,797 +matmul_transb 1x190x2xf64,813 +matmul_transb 1x190x3xf64,830 +matmul_transb 1x190x4xf64,821 +matmul_transb 1x190x5xf64,1010 +matmul_transb 1x190x6xf64,1207 +matmul_transb 1x190x7xf64,1396 +matmul_transb 1x190x8xf64,1605 +matmul_transb 1x190x9xf64,2356 +matmul_transb 1x195x1xf64,817 +matmul_transb 1x195x2xf64,833 +matmul_transb 1x195x3xf64,850 +matmul_transb 1x195x4xf64,841 +matmul_transb 1x195x5xf64,1035 +matmul_transb 1x195x6xf64,1237 +matmul_transb 1x195x7xf64,1431 +matmul_transb 1x195x8xf64,1645 +matmul_transb 1x195x9xf64,2416 +matmul_transb 1x200x1xf64,837 +matmul_transb 1x200x2xf64,853 +matmul_transb 1x200x3xf64,870 +matmul_transb 1x200x4xf64,861 +matmul_transb 1x200x5xf64,1060 +matmul_transb 1x200x6xf64,1267 +matmul_transb 1x200x7xf64,1466 +matmul_transb 1x200x8xf64,1685 +matmul_transb 1x200x9xf64,2476 +matmul_transb 1x30x1xf64,157 +matmul_transb 1x30x2xf64,175 +matmul_transb 1x30x3xf64,170 +matmul_transb 1x30x4xf64,192 +matmul_transb 1x30x5xf64,212 +matmul_transb 1x30x6xf64,246 +matmul_transb 1x30x7xf64,278 +matmul_transb 1x30x8xf64,314 +matmul_transb 1x30x9xf64,442 +matmul_transb 1x35x1xf64,177 +matmul_transb 1x35x2xf64,195 +matmul_transb 1x35x3xf64,190 +matmul_transb 1x35x4xf64,212 +matmul_transb 1x35x5xf64,237 +matmul_transb 1x35x6xf64,276 +matmul_transb 1x35x7xf64,312 +matmul_transb 1x35x8xf64,354 +matmul_transb 1x35x9xf64,502 +matmul_transb 1x40x1xf64,197 +matmul_transb 1x40x2xf64,215 +matmul_transb 1x40x3xf64,210 +matmul_transb 1x40x4xf64,232 +matmul_transb 1x40x5xf64,262 +matmul_transb 1x40x6xf64,317 +matmul_transb 1x40x7xf64,347 +matmul_transb 1x40x8xf64,394 +matmul_transb 1x40x9xf64,562 +matmul_transb 1x45x1xf64,217 +matmul_transb 1x45x2xf64,235 +matmul_transb 1x45x3xf64,230 +matmul_transb 1x45x4xf64,252 +matmul_transb 1x45x5xf64,295 +matmul_transb 1x45x6xf64,347 +matmul_transb 1x45x7xf64,382 +matmul_transb 1x45x8xf64,434 +matmul_transb 1x45x9xf64,622 +matmul_transb 1x50x1xf64,237 +matmul_transb 1x50x2xf64,255 +matmul_transb 1x50x3xf64,250 +matmul_transb 1x50x4xf64,272 +matmul_transb 1x50x5xf64,320 +matmul_transb 1x50x6xf64,377 +matmul_transb 1x50x7xf64,417 +matmul_transb 1x50x8xf64,474 +matmul_transb 1x50x9xf64,682 +matmul_transb 1x55x1xf64,257 +matmul_transb 1x55x2xf64,275 +matmul_transb 1x55x3xf64,270 +matmul_transb 1x55x4xf64,282 +matmul_transb 1x55x5xf64,345 +matmul_transb 1x55x6xf64,407 +matmul_transb 1x55x7xf64,452 +matmul_transb 1x55x8xf64,514 +matmul_transb 1x55x9xf64,741 +matmul_transb 1x60x1xf64,277 +matmul_transb 1x60x2xf64,295 +matmul_transb 1x60x3xf64,290 +matmul_transb 1x60x4xf64,302 +matmul_transb 1x60x5xf64,370 +matmul_transb 1x60x6xf64,437 +matmul_transb 1x60x7xf64,487 +matmul_transb 1x60x8xf64,565 +matmul_transb 1x60x9xf64,801 +matmul_transb 1x65x1xf64,297 +matmul_transb 1x65x2xf64,315 +matmul_transb 1x65x3xf64,310 +matmul_transb 1x65x4xf64,322 +matmul_transb 1x65x5xf64,381 +matmul_transb 1x65x6xf64,467 +matmul_transb 1x65x7xf64,521 +matmul_transb 1x65x8xf64,605 +matmul_transb 1x65x9xf64,861 +matmul_transb 1x70x1xf64,317 +matmul_transb 1x70x2xf64,335 +matmul_transb 1x70x3xf64,330 +matmul_transb 1x70x4xf64,342 +matmul_transb 1x70x5xf64,406 +matmul_transb 1x70x6xf64,497 +matmul_transb 1x70x7xf64,556 +matmul_transb 1x70x8xf64,645 +matmul_transb 1x70x9xf64,921 +matmul_transb 1x75x1xf64,337 +matmul_transb 1x75x2xf64,355 +matmul_transb 1x75x3xf64,350 +matmul_transb 1x75x4xf64,362 +matmul_transb 1x75x5xf64,431 +matmul_transb 1x75x6xf64,517 +matmul_transb 1x75x7xf64,591 +matmul_transb 1x75x8xf64,685 +matmul_transb 1x75x9xf64,981 +matmul_transb 1x80x1xf64,357 +matmul_transb 1x80x2xf64,375 +matmul_transb 1x80x3xf64,370 +matmul_transb 1x80x4xf64,382 +matmul_transb 1x80x5xf64,456 +matmul_transb 1x80x6xf64,547 +matmul_transb 1x80x7xf64,626 +matmul_transb 1x80x8xf64,725 +matmul_transb 1x80x9xf64,1041 +matmul_transb 1x85x1xf64,377 +matmul_transb 1x85x2xf64,395 +matmul_transb 1x85x3xf64,390 +matmul_transb 1x85x4xf64,402 +matmul_transb 1x85x5xf64,481 +matmul_transb 1x85x6xf64,577 +matmul_transb 1x85x7xf64,661 +matmul_transb 1x85x8xf64,765 +matmul_transb 1x85x9xf64,1101 +matmul_transb 1x90x1xf64,397 +matmul_transb 1x90x2xf64,414 +matmul_transb 1x90x3xf64,410 +matmul_transb 1x90x4xf64,422 +matmul_transb 1x90x5xf64,510 +matmul_transb 1x90x6xf64,607 +matmul_transb 1x90x7xf64,696 +matmul_transb 1x90x8xf64,805 +matmul_transb 1x90x9xf64,1161 +matmul_transb 1x95x1xf64,417 +matmul_transb 1x95x2xf64,434 +matmul_transb 1x95x3xf64,430 +matmul_transb 1x95x4xf64,442 +matmul_transb 1x95x5xf64,535 +matmul_transb 1x95x6xf64,637 +matmul_transb 1x95x7xf64,731 +matmul_transb 1x95x8xf64,845 +matmul_transb 1x95x9xf64,1221 diff --git a/myrtle/test_output.json b/myrtle/test_output.json new file mode 100644 index 00000000..8e309ba1 --- /dev/null +++ b/myrtle/test_output.json @@ -0,0 +1 @@ +{"main$async_dispatch_7_matmul_transpose_b_1x600x400_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [40], [100]], "dual-buffer": true}} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f3eb16ae..f5ce621a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ -r runtime/requirements.txt -r runtime/samples/nsnet2/requirements.txt -e xdsl +-e myrtle diff --git a/runtime/cmake/quidditch_module.cmake b/runtime/cmake/quidditch_module.cmake index cce72522..c873f1f7 100644 --- a/runtime/cmake/quidditch_module.cmake +++ b/runtime/cmake/quidditch_module.cmake @@ -155,15 +155,17 @@ endfunction() # The python script should take one positional argument, which is the 'DST' # argument and output the MLIR file there. Additionally the 'DTYPE' flag is # communicated via a `--dtype=` flag. +# For the FakeNN sample test case, 'M', 'N' and 'K' should be integers that +# set the dimensions of the single linear layer. We use --M, --N, and --K to specify them. macro(iree_turbine) - cmake_parse_arguments(_RULE "" "SRC;DTYPE;DST" "" ${ARGN}) + cmake_parse_arguments(_RULE "" "SRC;DTYPE;M;N;K;DST" "" ${ARGN}) cmake_path(GET _RULE_SRC STEM filename) cmake_path(ABSOLUTE_PATH _RULE_SRC BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE source_path) add_custom_command( OUTPUT ${_RULE_DST} - COMMAND ${Python3_EXECUTABLE} ${source_path} ${_RULE_DST} --dtype=${_RULE_DTYPE} + COMMAND ${Python3_EXECUTABLE} ${source_path} ${_RULE_DST} --dtype=${_RULE_DTYPE} --m=${_RULE_M} --n=${_RULE_N} --k=${_RULE_K} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${_RULE_SRC} COMMENT "Translating ${filename} using iree-turbine" diff --git a/runtime/runtime/src/Quidditch/CMakeLists.txt b/runtime/runtime/src/Quidditch/CMakeLists.txt index f7bdb8c3..cd678bfc 100644 --- a/runtime/runtime/src/Quidditch/CMakeLists.txt +++ b/runtime/runtime/src/Quidditch/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(device) add_subdirectory(dispatch) add_subdirectory(executable) add_subdirectory(loader) +add_subdirectory(time_dispatch) diff --git a/runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt b/runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt new file mode 100644 index 00000000..e7312c74 --- /dev/null +++ b/runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt @@ -0,0 +1,11 @@ + +iree_cc_library( + NAME + time_dispatch + SRCS + time_dispatch.c + DEPS + snRuntimeInterface + iree::base + PUBLIC +) diff --git a/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c b/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c new file mode 100644 index 00000000..faab7662 --- /dev/null +++ b/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c @@ -0,0 +1,12 @@ + +#include "time_dispatch.h" + +#include +unsigned long myrtle_actual_cycles[5][2] = { + {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}; + +void myrtle_record_cycles(uint32_t kernel, uint32_t atEnd) { + uint32_t register r; + asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); + myrtle_actual_cycles[kernel][atEnd] = r; +} diff --git a/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h b/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h new file mode 100644 index 00000000..02034075 --- /dev/null +++ b/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h @@ -0,0 +1,7 @@ + +#pragma once + +#include +#include + +inline void myrtle_record_cycles(uint32_t kernel, uint32_t atEnd); diff --git a/runtime/samples/.gitignore b/runtime/samples/.gitignore new file mode 100644 index 00000000..422da9c0 --- /dev/null +++ b/runtime/samples/.gitignore @@ -0,0 +1 @@ +*.json-exported.json \ No newline at end of file diff --git a/runtime/samples/CMakeLists.txt b/runtime/samples/CMakeLists.txt index 04527805..1876a7e8 100644 --- a/runtime/samples/CMakeLists.txt +++ b/runtime/samples/CMakeLists.txt @@ -2,5 +2,10 @@ include(quidditch_module) add_subdirectory(big_matvec) add_subdirectory(nsnet2) +add_subdirectory(grapeFruit) +add_subdirectory(fakeNN) +add_subdirectory(calabaza) +add_subdirectory(caqui) add_subdirectory(util) add_subdirectory(vec_multiply) +add_subdirectory(pomelo) diff --git a/runtime/samples/fakeNN/CMakeLists.txt b/runtime/samples/fakeNN/CMakeLists.txt new file mode 100644 index 00000000..62530701 --- /dev/null +++ b/runtime/samples/fakeNN/CMakeLists.txt @@ -0,0 +1,46 @@ +set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) +iree_turbine(SRC fakeNN.py DST ${CMAKE_CURRENT_BINARY_DIR}/fakeNN.mlirbc DTYPE "f64" M 2 N 120 K 40) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/fakeNN.mlirbc DST fakeNN FLAGS --mlir-disable-threading --iree-quidditch-time-disp=fakenn2x120x40 --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-myrtle=${MYRTLE_PATH}) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/fakeNN.mlirbc LLVM DST fakeNN_llvm FLAGS --iree-quidditch-time-disp=fakenn2x120x40 --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-myrtle=${MYRTLE_PATH}) +add_library(fakeNN_util fakeNN_util.c) +target_link_libraries(fakeNN_util + PRIVATE + samples_util + snRuntimeInterface + Quidditch::dispatch::dispatch + Quidditch::time_dispatch::time_dispatch +) +target_include_directories(fakeNN_util INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +macro(create_experiment_variant) + cmake_parse_arguments(_RULE "PRECOMMIT;NIGHTLY" "TARGET;IREE_MODULE;QUERY_FUNC" "" ${ARGN}) + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c "\ +#include <${_RULE_IREE_MODULE}.h> + +#include \"fakeNN_util.h\" + +int main() { + return run_fakeNN_experiment(${_RULE_QUERY_FUNC}); +} +") + add_executable(${_RULE_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c) + target_link_libraries( + ${_RULE_TARGET} + PRIVATE + fakeNN_util + ${_RULE_IREE_MODULE} + snRuntime + ) +endmacro() + +create_experiment_variant( + TARGET FakeNN + IREE_MODULE fakeNN + QUERY_FUNC "quidditch_compiled_fake_n_n_linked_quidditch_library_query" +) +create_experiment_variant( + TARGET FakeNNLLVM + IREE_MODULE fakeNN_llvm + QUERY_FUNC "compiled_fake_n_n_linked_llvm_cpu_library_query" +) \ No newline at end of file diff --git a/runtime/samples/fakeNN/fakeNN.py b/runtime/samples/fakeNN/fakeNN.py new file mode 100644 index 00000000..f6f84d90 --- /dev/null +++ b/runtime/samples/fakeNN/fakeNN.py @@ -0,0 +1,157 @@ +import argparse +import os +import random +import torch +import torch.nn as nn +from iree.turbine import aot +import numpy as np + +seed = 1234 +random.seed(seed) +torch.manual_seed(seed) +os.environ['PYTHONHASHSEED'] = str(seed) + +WIN_LEN = 0.02 +HOP_FRAC = 0.5 +FS = 16000 +MIN_GAIN = -80 + +n_fft = int(WIN_LEN * FS) +frame_shift = WIN_LEN * HOP_FRAC +n_hop = n_fft * HOP_FRAC +spec_size = n_fft // 2 + 1 + +parser = argparse.ArgumentParser(prog='iree-turbine') +parser.add_argument('output', nargs='?') +parser.add_argument('--frames', dest='frames', metavar='N', type=int, default=1, nargs='?') +parser.add_argument('--m', dest='mDim', metavar='Mdim', type=int, default=1, nargs='?') +parser.add_argument('--n', dest='nDim', metavar='Ndim', type=int, default=1, nargs='?') +parser.add_argument('--k', dest='kDim', metavar='Kdim', type=int, default=1, nargs='?') +parser.add_argument('--dtype', dest='dtype', metavar='F', choices=['f32', 'f64'], default='f32') +parser.add_argument('-dump', dest='dump', action='store_true', default=False) +args = parser.parse_args() + +name_to_dtype = { + 'f32': torch.float32, + 'f64': torch.float64, +} +dtype = name_to_dtype[args.dtype] + +# beware! the name of your nn.Module subclass matters! +# use camel case starting with a capital letter, and remember that given a name like FakeNN, +# cmake generates a static linked library for the module called +# compiled_fake_n_n_linked_llvm_cpu_library_query etc. +class FakeNN(nn.Module): + def __init__(self, n_features, hidden_1): + super().__init__() + self.n_features = n_features + self.hidden_1 = hidden_1 + # self.hidden_2 = hidden_2 + # self.hidden_3 = hidden_3 + # fc1 + self.fc1 = nn.Linear(n_features, hidden_1, dtype=dtype) + # # rnn + # self.rnn1 = nn.GRU(input_size=hidden_1, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) + # self.rnn2 = nn.GRU(input_size=hidden_2, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) + # # fc2 + # self.fc2 = nn.Linear(hidden_2, hidden_3, dtype=dtype) + # # fc3 + # self.fc3 = nn.Linear(hidden_3, hidden_3, dtype=dtype) + # # fc4 + # self.fc4 = nn.Linear(hidden_3, n_features, dtype=dtype) + # other + self.eps = 1e-9 + + def forward(self, stft_noisy, *state_in): + mask_pred, *state_out = self._forward(stft_noisy, *state_in) + return mask_pred, *state_out + + def _forward(self, stft_noisy, *state_in): + # print("we are inside the foward function") + x = self.fc1(stft_noisy) + # for some reason, the "compiled_fake_n_n_linked_llvm_cpu_library_query" inside FakeNNLLVM.h + # does not get generated unless I pass these two intermediate tensor states inside the forward function + # and modify them + # TODO: Since I'm not using the global intermediate states, find a way to get rid of them but still + # generate the "compiled_fake_n_n_linked_llvm_cpu_library_query" needed. + state_out = [*state_in] + sevens = torch.full(state_out[0].shape, 7,dtype=state_out[0].dtype)#from_numpy(np.full(state_out[0].shape, 7,state_out[0].dtype)) + state_out[0] = torch.add(state_out[0],sevens) + #result_tuple= model(torch.from_numpy(blah),state1,state2) + #y, state_out[0] = self.rnn1(x, state_in[0]) + # x, state_out[1] = self.rnn2(x, state_in[1]) + # x = self.fc2(x) + + # x = nn.functional.relu(x) + # x = self.fc3(x) + # x = nn.functional.relu(x) + # x = self.fc4(x) + # x = torch.sigmoid(x) + # sort shape + #mask_pred = x.permute(0, 2, 1).unsqueeze(1) + # print(f'inside the forward funcion, about to return{state_out}') + return x, *state_out + + +model = FakeNN(n_features=args.kDim, hidden_1=args.nDim) +#print(model) +model.train(False) + + +def with_frames(n_frames): + size = 1, n_frames, model.n_features + # beware! the name of your aot.CompiledModule subclass matters! + # use camel case starting with a capital letter, and remember that given a name like FakeNN, + # cmake generates a static linked library for the module called + # compiled_fake_n_n_linked_llvm_cpu_library_query etc. + class CompiledFakeNN(aot.CompiledModule): + # Make the hidden state globals that persist as long as the IREE session does. + state1 = aot.export_global(torch.zeros(1, 1, 2, dtype=dtype), mutable=True, uninitialized=False) + state2 = aot.export_global(torch.zeros(1, 1, 2, dtype=dtype), mutable=True, uninitialized=False) + def main(self, x=aot.AbstractTensor(*size, dtype=dtype)): + y, out1, out2 = aot.jittable(model.forward)( + x, self.state1, self.state2, + constraints=[] + ) + self.state1 = out1 + self.state2 = out2 + return y + + return CompiledFakeNN + +# I'm not sure what Markus meant by n_frames, but it appears +# that n_frames is equivalent to the M dimension (row dimension) of the input. +exported = aot.export(with_frames(n_frames=args.mDim)) +if args.dump: + exported.print_readable() +else: + exported.save_mlir(args.output) + # emily's notes below vvvvvvvvvvvvvvvvv + # np_dtype = np.float32 + # if dtype == torch.float64: + # np_dtype = np.float64 + + # blah = np.full((1,args.mDim, args.kDim), 7,dtype=np_dtype) + # state1 = torch.zeros(1, 1, 400, dtype=dtype) + # state2 = torch.zeros(1, 1, 400, dtype=dtype) + # result_tuple= model(torch.from_numpy(blah),state1,state2) + # print("YODEL") + # print(result_tuple[0]) + # print(result_tuple[0].shape) + + +# THE FOLLOWING CODE RUNS THE NsNet2 NN IN PYTHON!! + # np_dtype = np.float32 + # if dtype == torch.float64: + # np_dtype = np.float64 + # print("yodelaheyyyhoooooooo") + # blah = np.full((1,1, 161), 7,dtype=np_dtype) + # blah = np.array([[list([i+1 for i in range (0,161)])]],dtype=np_dtype) + # state1 = torch.zeros(1, 1, 400, dtype=dtype) + # state2 = torch.zeros(1, 1, 400, dtype=dtype) + # result_tuple= model(torch.from_numpy(blah),state1,state2) + # print("YODEL") + # print(result_tuple) +# https://docs.pytorch.org/docs/stable/generated/torch.nn.Linear.html +# https://docs.pytorch.org/tutorials/beginner/basics/buildmodel_tutorial.html +# https://docs.kanaries.net/topics/Python/nn-linear \ No newline at end of file diff --git a/runtime/samples/fakeNN/fakeNN_util.c b/runtime/samples/fakeNN/fakeNN_util.c new file mode 100644 index 00000000..1db0e5ca --- /dev/null +++ b/runtime/samples/fakeNN/fakeNN_util.c @@ -0,0 +1,77 @@ +#include "fakeNN_util.h" + +#include +#include + +#include + +#include +#include + +// ../toolchain/bin/snitch_cluster.vlt /home/hoppip/Quidditch/build/runtime/samples/fakeNN/fakeNN + +iree_status_t compiled_fake_n_n_create(iree_vm_instance_t *, iree_allocator_t, + iree_vm_module_t **); + +// copied from Quidditch/snitch_cluster/sw/snRuntime/src/riscv.h +inline uint32_t snrt_mcycle() { + uint32_t register r; + asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); + return r; +} + +extern unsigned long myrtle_actual_cycles[5][2]; + + +int run_fakeNN_experiment( + iree_hal_executable_library_query_fn_t implementation) { + if (!snrt_is_dm_core()) return quidditch_dispatch_enter_worker_loop(); + + double(*inputData)[mDim*kDim] = aligned_alloc(64, (mDim*kDim) * sizeof(double)); + double(*outputData)[mDim*nDim] = aligned_alloc(64, (mDim*nDim) * sizeof(double)); + + // initialize input data + for (int i = 0; i < IREE_ARRAYSIZE(*inputData); i++) { + (*inputData)[i] = (i + 1); + } + + // initialize output data to all zeros + for (int i = 0; i < IREE_ARRAYSIZE(*outputData); i++) { + (*outputData)[i] = 0; + } + + model_config_t config = { + .libraries = (iree_hal_executable_library_query_fn_t[]){implementation}, + .num_libraries = 1, + .module_constructor = compiled_fake_n_n_create, + .main_function = iree_make_cstring_view("compiled_fake_n_n.main"), + + .element_type = IREE_HAL_ELEMENT_TYPE_FLOAT_64, + + .num_inputs = 1, + .input_data = (const void *[]){inputData, inputData}, + .input_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*inputData)}, + .input_ranks = (const iree_host_size_t[]){3}, + .input_shapes = (const iree_hal_dim_t *[]){(iree_hal_dim_t[]){1, mDim, kDim}}, + + .num_outputs = 1, + .output_data = (void *[]){outputData}, + .output_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*outputData)}, + }; + + unsigned long cycles = snrt_mcycle(); + IREE_CHECK_OK(run_model(&config)); + unsigned long cycles_after = snrt_mcycle(); + + for (int i = 0; i < IREE_ARRAYSIZE(*outputData); i++) { + double value = (*outputData)[i]; + printf("%f\n", value); + } + + int i = 0; + printf("dispatch 0: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + printf("\ncycles %lu\n", cycles_after - cycles); + free(inputData); + free(outputData); + return 0; +} diff --git a/runtime/samples/fakeNN/fakeNN_util.h b/runtime/samples/fakeNN/fakeNN_util.h new file mode 100644 index 00000000..6ccb1d65 --- /dev/null +++ b/runtime/samples/fakeNN/fakeNN_util.h @@ -0,0 +1,11 @@ + +#pragma once + +#include + +int run_fakeNN_experiment( + iree_hal_executable_library_query_fn_t implementation); + +#define mDim 2 +#define nDim 120 +#define kDim 40 \ No newline at end of file diff --git a/runtime/samples/fakeNN/requirements.txt b/runtime/samples/fakeNN/requirements.txt new file mode 100644 index 00000000..586f84b7 --- /dev/null +++ b/runtime/samples/fakeNN/requirements.txt @@ -0,0 +1,2 @@ +torch==2.3.0 +iree-turbine==2.3.0 diff --git a/runtime/samples/fakeNN/ts-answer.json b/runtime/samples/fakeNN/ts-answer.json new file mode 100644 index 00000000..dca3137c --- /dev/null +++ b/runtime/samples/fakeNN/ts-answer.json @@ -0,0 +1 @@ +{"main$async_dispatch_0_matmul_transpose_b_2x120x40_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [120], [20]], "dual-buffer": true}} \ No newline at end of file diff --git a/runtime/samples/grapeFruit/CMakeLists.txt b/runtime/samples/grapeFruit/CMakeLists.txt new file mode 100644 index 00000000..04a32a81 --- /dev/null +++ b/runtime/samples/grapeFruit/CMakeLists.txt @@ -0,0 +1,46 @@ +set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) +iree_turbine(SRC grapeFruit.py DST ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DTYPE "f64" M 5 N 6 K 7) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DST grapeFruit FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc LLVM DST grapeFruit_llvm FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) +add_library(grapeFruit_util grapeFruit_util.c) +target_link_libraries(grapeFruit_util + PRIVATE + samples_util + snRuntimeInterface + Quidditch::dispatch::dispatch + Quidditch::time_dispatch::time_dispatch +) +target_include_directories(grapeFruit_util INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +macro(create_experiment_variant) + cmake_parse_arguments(_RULE "PRECOMMIT;NIGHTLY" "TARGET;IREE_MODULE;QUERY_FUNC" "" ${ARGN}) + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c "\ +#include <${_RULE_IREE_MODULE}.h> + +#include \"grapeFruit_util.h\" + +int main() { + return run_grapeFruit_experiment(${_RULE_QUERY_FUNC}); +} +") + add_executable(${_RULE_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c) + target_link_libraries( + ${_RULE_TARGET} + PRIVATE + grapeFruit_util + ${_RULE_IREE_MODULE} + snRuntime + ) +endmacro() + +create_experiment_variant( + TARGET GrapeFruit + IREE_MODULE grapeFruit + QUERY_FUNC "quidditch_compiled_ns_net2_linked_quidditch_library_query" +) +create_experiment_variant( + TARGET GrapeFruitLLVM + IREE_MODULE grapeFruit_llvm + QUERY_FUNC "compiled_ns_net2_linked_llvm_cpu_library_query" +) \ No newline at end of file diff --git a/runtime/samples/grapeFruit/disp-9-bandaid.json b/runtime/samples/grapeFruit/disp-9-bandaid.json new file mode 100644 index 00000000..0ad3ec7b --- /dev/null +++ b/runtime/samples/grapeFruit/disp-9-bandaid.json @@ -0,0 +1,30 @@ +{ + "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64": { + "loop-order": [ + [ + 2, + 0 + ], + [ + 0, + 0 + ], + [ + 1, + 0 + ] + ], + "tile-sizes": [ + [ + 1 + ], + [ + 56 + ], + [ + 100 + ] + ], + "dual-buffer": true + } +} \ No newline at end of file diff --git a/runtime/samples/grapeFruit/grapeFruit.py b/runtime/samples/grapeFruit/grapeFruit.py new file mode 100644 index 00000000..64bf5c46 --- /dev/null +++ b/runtime/samples/grapeFruit/grapeFruit.py @@ -0,0 +1,110 @@ +import argparse +import os +import random +import torch +import torch.nn as nn +from iree.turbine import aot + +seed = 1234 +random.seed(seed) +torch.manual_seed(seed) +os.environ['PYTHONHASHSEED'] = str(seed) + +WIN_LEN = 0.02 +HOP_FRAC = 0.5 +FS = 16000 +MIN_GAIN = -80 + +n_fft = int(WIN_LEN * FS) +frame_shift = WIN_LEN * HOP_FRAC +n_hop = n_fft * HOP_FRAC +spec_size = n_fft // 2 + 1 + +parser = argparse.ArgumentParser(prog='iree-turbine') +parser.add_argument('output', nargs='?') +parser.add_argument('--frames', dest='frames', metavar='N', type=int, default=1, nargs='?') +parser.add_argument('--m', dest='mDim', metavar='Mdim', type=int, default=1, nargs='?') +parser.add_argument('--n', dest='nDim', metavar='Ndim', type=int, default=1, nargs='?') +parser.add_argument('--k', dest='kDim', metavar='Kdim', type=int, default=1, nargs='?') +parser.add_argument('--dtype', dest='dtype', metavar='F', choices=['f32', 'f64'], default='f32') +parser.add_argument('-dump', dest='dump', action='store_true', default=False) +args = parser.parse_args() + +name_to_dtype = { + 'f32': torch.float32, + 'f64': torch.float64, +} +dtype = name_to_dtype[args.dtype] + + +class NsNet2(nn.Module): + def __init__(self, n_features, hidden_1, hidden_2, hidden_3): + super().__init__() + self.n_features = n_features + self.hidden_1 = hidden_1 + self.hidden_2 = hidden_2 + self.hidden_3 = hidden_3 + # fc1 + self.fc1 = nn.Linear(n_features, hidden_1, dtype=dtype) + # rnn + self.rnn1 = nn.GRU(input_size=hidden_1, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) + self.rnn2 = nn.GRU(input_size=hidden_2, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) + # fc2 + self.fc2 = nn.Linear(hidden_2, hidden_3, dtype=dtype) + # fc3 + self.fc3 = nn.Linear(hidden_3, hidden_3, dtype=dtype) + # fc4 + self.fc4 = nn.Linear(hidden_3, n_features, dtype=dtype) + # other + self.eps = 1e-9 + + def forward(self, stft_noisy, *state_in): + mask_pred, *state_out = self._forward(stft_noisy, *state_in) + return mask_pred, *state_out + + def _forward(self, stft_noisy, *state_in): + x = self.fc1(stft_noisy) + state_out = [*state_in] + x, state_out[0] = self.rnn1(x, state_in[0]) + x, state_out[1] = self.rnn2(x, state_in[1]) + x = self.fc2(x) + x = nn.functional.relu(x) + x = self.fc3(x) + x = nn.functional.relu(x) + x = self.fc4(x) + x = torch.sigmoid(x) + # sort shape + mask_pred = x.permute(0, 2, 1).unsqueeze(1) + return mask_pred, *state_out + + +model = NsNet2(n_features=spec_size, hidden_1=400, hidden_2=400, hidden_3=600) +model.train(False) + + +def with_frames(n_frames): + size = 1, n_frames, model.n_features + + class CompiledNsNet2(aot.CompiledModule): + # Make the hidden state globals that persist as long as the IREE session does. + state1 = aot.export_global(torch.zeros(1, 1, 400, dtype=dtype), mutable=True, uninitialized=False) + state2 = aot.export_global(torch.zeros(1, 1, 400, dtype=dtype), mutable=True, uninitialized=False) + + def main(self, x=aot.AbstractTensor(*size, dtype=dtype)): + y, out1, out2 = aot.jittable(model.forward)( + x, self.state1, self.state2, + constraints=[] + ) + self.state1 = out1 + self.state2 = out2 + return y + + return CompiledNsNet2 + + +exported = aot.export(with_frames(n_frames=args.frames)) +if args.dump: + exported.print_readable() +else: + exported.save_mlir(args.output) + # exported.save_mlir("/home/hoppip/Quidditch/runtime/samples/grapeFruit/theMLIR.mlir") diff --git a/runtime/samples/grapeFruit/grapeFruit_util.c b/runtime/samples/grapeFruit/grapeFruit_util.c new file mode 100644 index 00000000..01ab1399 --- /dev/null +++ b/runtime/samples/grapeFruit/grapeFruit_util.c @@ -0,0 +1,81 @@ +#include "grapeFruit_util.h" + +#include +#include + +#include + +#include +#include + + +iree_status_t compiled_ns_net2_create(iree_vm_instance_t *, iree_allocator_t, + iree_vm_module_t **); + +// copied from Quidditch/snitch_cluster/sw/snRuntime/src/riscv.h +inline uint32_t snrt_mcycle() { + uint32_t register r; + asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); + return r; +} + +extern unsigned long myrtle_actual_cycles[5][2]; + + +int run_grapeFruit_experiment( + iree_hal_executable_library_query_fn_t implementation) { + if (!snrt_is_dm_core()) return quidditch_dispatch_enter_worker_loop(); + + double(*data)[161] = aligned_alloc(64, 161 * sizeof(double)); + + for (int i = 0; i < IREE_ARRAYSIZE(*data); i++) { + (*data)[i] = (i + 1); + } + + model_config_t config = { + .libraries = (iree_hal_executable_library_query_fn_t[]){implementation}, + .num_libraries = 1, + .module_constructor = compiled_ns_net2_create, + .main_function = iree_make_cstring_view("compiled_ns_net2.main"), + + .element_type = IREE_HAL_ELEMENT_TYPE_FLOAT_64, + + .num_inputs = 1, + .input_data = (const void *[]){data, data}, + .input_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*data)}, + .input_ranks = (const iree_host_size_t[]){3}, + .input_shapes = (const iree_hal_dim_t *[]){(iree_hal_dim_t[]){1, 1, 161}}, + + .num_outputs = 1, + .output_data = (void *[]){data}, + .output_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*data)}, + }; + + unsigned long cycles = snrt_mcycle(); + IREE_CHECK_OK(run_model(&config)); + unsigned long cycles_after = snrt_mcycle(); + + // for (int i = 0; i < IREE_ARRAYSIZE(*data); i++) { + // double value = (*data)[i]; + // printf("%f\n", value); + // } + + // for(int i = 0; i < 5; i ++){ + // printf("%d: %lu - %lu = %lu\n", i, myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + // } + int i = 0; + printf("dispatch 9: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + i = 1; + printf("dispatch 0: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + i = 2; + printf("dispatch 7: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + i = 3; + printf("dispatch 8: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + i = 4; + printf("dispatch 1: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); + + + printf("\ncycles %lu\n", cycles_after - cycles); + free(data); + return 0; +} diff --git a/runtime/samples/grapeFruit/grapeFruit_util.h b/runtime/samples/grapeFruit/grapeFruit_util.h new file mode 100644 index 00000000..5b02ece4 --- /dev/null +++ b/runtime/samples/grapeFruit/grapeFruit_util.h @@ -0,0 +1,7 @@ + +#pragma once + +#include + +int run_grapeFruit_experiment( + iree_hal_executable_library_query_fn_t implementation); diff --git a/runtime/samples/grapeFruit/requirements.txt b/runtime/samples/grapeFruit/requirements.txt new file mode 100644 index 00000000..586f84b7 --- /dev/null +++ b/runtime/samples/grapeFruit/requirements.txt @@ -0,0 +1,2 @@ +torch==2.3.0 +iree-turbine==2.3.0 diff --git a/runtime/samples/grapeFruit/ts-answer.json b/runtime/samples/grapeFruit/ts-answer.json new file mode 100644 index 00000000..54389ed1 --- /dev/null +++ b/runtime/samples/grapeFruit/ts-answer.json @@ -0,0 +1 @@ +{"main$async_dispatch_0_matmul_transpose_b_1x400x161_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [200], [23]], "dual-buffer": true}, "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [48], [80]], "dual-buffer": true}, "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [40], [100]], "dual-buffer": true}, "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [40], [120]], "dual-buffer": true}, "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [144], [40]], "dual-buffer": true}} \ No newline at end of file diff --git a/runtime/samples/nsnet2/0-40-100.json b/runtime/samples/nsnet2/0-40-100.json new file mode 100644 index 00000000..04d4decf --- /dev/null +++ b/runtime/samples/nsnet2/0-40-100.json @@ -0,0 +1,27 @@ +{ + "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64" : { + "tile-sizes":[[0], [40], [100]], + "loop-order":[[2,0], [0,0], [1,0]], + "dual-buffer": true + }, + "main$async_dispatch_0_matmul_transpose_b_1x400x161_f64" : { + "tile-sizes":[[0],[40],[0]], + "loop-order":[[2,0], [0,0], [1,0]], + "dual-buffer": false + }, + "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64" : { + "tile-sizes":[[0], [40], [100]], + "loop-order":[[2,0], [0,0], [1,0]], + "dual-buffer": true + }, + "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64" : { + "tile-sizes":[[0], [40], [100]], + "loop-order":[[2,0], [0,0], [1,0]], + "dual-buffer": true + }, + "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64" : { + "tile-sizes":[[0], [56], [100]], + "loop-order":[[2,0], [0,0], [1,0]], + "dual-buffer": true + } +} diff --git a/runtime/samples/nsnet2/CMakeLists.txt b/runtime/samples/nsnet2/CMakeLists.txt index 71a5e7c1..d66097ef 100644 --- a/runtime/samples/nsnet2/CMakeLists.txt +++ b/runtime/samples/nsnet2/CMakeLists.txt @@ -1,7 +1,8 @@ -iree_turbine(SRC NsNet2.py DST ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DTYPE "f64") -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DST nsnet2) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc LLVM DST nsnet2_llvm) +set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) +iree_turbine(SRC NsNet2.py DST ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DTYPE "f64" M 5 N 6 K 7) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DST nsnet2 FLAGS --mlir-disable-threading --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/0-40-100.json) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc LLVM DST nsnet2_llvm FLAGS --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/0-40-100.json) add_library(nsnet2_util nsnet2_util.c) target_link_libraries(nsnet2_util diff --git a/runtime/samples/nsnet2/NsNet2.py b/runtime/samples/nsnet2/NsNet2.py index 1d529c0d..2b7bd698 100644 --- a/runtime/samples/nsnet2/NsNet2.py +++ b/runtime/samples/nsnet2/NsNet2.py @@ -23,6 +23,9 @@ parser = argparse.ArgumentParser(prog='iree-turbine') parser.add_argument('output', nargs='?') parser.add_argument('--frames', dest='frames', metavar='N', type=int, default=1, nargs='?') +parser.add_argument('--m', dest='mDim', metavar='Mdim', type=int, default=1, nargs='?') +parser.add_argument('--n', dest='nDim', metavar='Ndim', type=int, default=1, nargs='?') +parser.add_argument('--k', dest='kDim', metavar='Kdim', type=int, default=1, nargs='?') parser.add_argument('--dtype', dest='dtype', metavar='F', choices=['f32', 'f64'], default='f32') parser.add_argument('-dump', dest='dump', action='store_true', default=False) args = parser.parse_args() diff --git a/runtime/samples/nsnet2/nsnet2_util.c b/runtime/samples/nsnet2/nsnet2_util.c index 874f5fe6..30d2f55a 100644 --- a/runtime/samples/nsnet2/nsnet2_util.c +++ b/runtime/samples/nsnet2/nsnet2_util.c @@ -10,6 +10,13 @@ iree_status_t compiled_ns_net2_create(iree_vm_instance_t *, iree_allocator_t, iree_vm_module_t **); +// copied from Quidditch/snitch_cluster/sw/snRuntime/src/riscv.h +inline uint32_t snrt_mcycle() { + uint32_t register r; + asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); + return r; +} + int run_nsnet2_experiment( iree_hal_executable_library_query_fn_t implementation) { if (!snrt_is_dm_core()) return quidditch_dispatch_enter_worker_loop(); @@ -39,12 +46,15 @@ int run_nsnet2_experiment( .output_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*data)}, }; + unsigned long cycles = snrt_mcycle(); IREE_CHECK_OK(run_model(&config)); + unsigned long cycles_after = snrt_mcycle(); for (int i = 0; i < IREE_ARRAYSIZE(*data); i++) { double value = (*data)[i]; printf("%f\n", value); } + printf("\ncycles %lu\n", cycles_after - cycles); free(data); return 0; } diff --git a/runtime/snitch_cluster/CMakeLists.txt b/runtime/snitch_cluster/CMakeLists.txt index fcce6b2b..06ea70c6 100644 --- a/runtime/snitch_cluster/CMakeLists.txt +++ b/runtime/snitch_cluster/CMakeLists.txt @@ -58,6 +58,7 @@ target_include_directories(snRuntimeInterface ${snRuntimeSrc}/sw/deps/riscv-opcodes ${CMAKE_CURRENT_LIST_DIR}/api ) +#/home/hoppip/Quidditch/snitch_cluster/target/snitch_cluster/sw/runtime/rtl/src/snrt.h RADDISH # Required while snRuntime uses 'inline' qualifiers for declarations. target_compile_options(snRuntimeInterface INTERFACE -Wno-undefined-inline) diff --git a/runtime/tests/CMakeLists.txt b/runtime/tests/CMakeLists.txt index 7704a11d..ca3b8ec4 100644 --- a/runtime/tests/CMakeLists.txt +++ b/runtime/tests/CMakeLists.txt @@ -82,6 +82,10 @@ endmacro() test_executable(HelloWorld PRECOMMIT) test_executable(vec_multiply PRECOMMIT) +test_executable(caqui PRECOMMIT) +test_executable(pomelo PRECOMMIT) +test_executable(calabaza PRECOMMIT) +test_executable(GrapeFruit NIGHTLY) test_executable(big_matvec NIGHTLY) test_executable(NsNet2 NIGHTLY) test_executable(NsNet2LLVM NIGHTLY) From 3371c34633e1caf3c82f0365815d994dfc4d75b7 Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Mon, 21 Jul 2025 15:03:07 +0200 Subject: [PATCH 2/8] prepare to split this branch into pieces --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index 5d267c9d..88428701 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,16 @@ /build .ccls* */compile_commands.json + +comparing-tile-sizes/1x1200x400wm-n-k/* +comparing-tile-sizes/1x600x600wm-n-k/* +comparing-tile-sizes/1x400x161wm-n-k/* +comparing-tile-sizes/1x600x400wm-n-k/* +fakeNN/linear-layer-search-space/out/* +IGNORE/* +.vscode/* +Testing/* +build-notes/* +build/* +toolchain/* +fakeNN/* \ No newline at end of file From 5169e12b843b25d02f1cb26508757b89ed0f8d9b Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Wed, 23 Jul 2025 12:48:17 +0200 Subject: [PATCH 3/8] automatically tile nsnet2 (provided tile sizes are passed in) --- .gitignore | 4 +- .gitmodules | 3 - .../Conversion/ConvertSnitchToLLVM.cpp | 29 ---- .../Quidditch/Conversion/ConvertToRISCV.cpp | 112 ------------- .../Dialect/Snitch/IR/QuidditchSnitchOps.cpp | 9 - .../Dialect/Snitch/IR/QuidditchSnitchOps.td | 39 ----- .../Dialect/Snitch/Transforms/Passes.td | 9 - .../Snitch/Transforms/SpecializeDMACode.cpp | 92 ---------- .../src/Quidditch/Target/ConfigureTiles.cpp | 59 +------ .../compiler/src/Quidditch/Target/Passes.td | 9 +- .../src/Quidditch/Target/QuidditchTarget.cpp | 28 +--- comparing-tile-sizes/.gitignore | 42 ----- comparing-tile-sizes/README.md | 145 ---------------- comparing-tile-sizes/cmakelist-epilogue.txt | 42 ----- .../cmakelist-middle-original.txt | 2 - comparing-tile-sizes/cmakelist-prologue.txt | 2 - comparing-tile-sizes/compileGrapefruits.sh | 123 -------------- comparing-tile-sizes/context.png | Bin 71949 -> 0 bytes .../ex_1x600x600wm-n-k_case1_searchSpace.csv | 48 ------ comparing-tile-sizes/ex_mini.csv | 2 - .../generateTileSizeJSONFiles.py | 55 ------ comparing-tile-sizes/merge.py | 13 -- comparing-tile-sizes/runGrapefruits.sh | 48 ------ comparing-tile-sizes/run_experiment.sh | 53 ------ comparing-tile-sizes/scrapeGrapefruits.sh | 102 ------------ comparing-tile-sizes/scrutinizeGrapefruits.sh | 88 ---------- requirements.txt | 1 - runtime/cmake/quidditch_module.cmake | 6 +- runtime/runtime/src/Quidditch/CMakeLists.txt | 1 - .../Quidditch/time_dispatch/CMakeLists.txt | 11 -- .../Quidditch/time_dispatch/time_dispatch.c | 12 -- .../Quidditch/time_dispatch/time_dispatch.h | 7 - runtime/samples/CMakeLists.txt | 5 - runtime/samples/fakeNN/CMakeLists.txt | 46 ----- runtime/samples/fakeNN/fakeNN.py | 157 ------------------ runtime/samples/fakeNN/fakeNN_util.c | 77 --------- runtime/samples/fakeNN/fakeNN_util.h | 11 -- runtime/samples/fakeNN/requirements.txt | 2 - runtime/samples/fakeNN/ts-answer.json | 1 - runtime/samples/grapeFruit/CMakeLists.txt | 46 ----- .../samples/grapeFruit/disp-9-bandaid.json | 30 ---- runtime/samples/grapeFruit/grapeFruit.py | 110 ------------ runtime/samples/grapeFruit/grapeFruit_util.c | 81 --------- runtime/samples/grapeFruit/grapeFruit_util.h | 7 - runtime/samples/grapeFruit/requirements.txt | 2 - runtime/samples/grapeFruit/ts-answer.json | 1 - runtime/samples/nsnet2/0-40-100.json | 27 --- runtime/samples/nsnet2/CMakeLists.txt | 4 +- .../samples/nsnet2/manually-chosen-tiles.json | 0 runtime/snitch_cluster/CMakeLists.txt | 1 - runtime/tests/CMakeLists.txt | 4 - 51 files changed, 18 insertions(+), 1790 deletions(-) delete mode 100644 comparing-tile-sizes/.gitignore delete mode 100644 comparing-tile-sizes/README.md delete mode 100644 comparing-tile-sizes/cmakelist-epilogue.txt delete mode 100644 comparing-tile-sizes/cmakelist-middle-original.txt delete mode 100644 comparing-tile-sizes/cmakelist-prologue.txt delete mode 100644 comparing-tile-sizes/compileGrapefruits.sh delete mode 100644 comparing-tile-sizes/context.png delete mode 100644 comparing-tile-sizes/ex_1x600x600wm-n-k_case1_searchSpace.csv delete mode 100644 comparing-tile-sizes/ex_mini.csv delete mode 100644 comparing-tile-sizes/generateTileSizeJSONFiles.py delete mode 100644 comparing-tile-sizes/merge.py delete mode 100644 comparing-tile-sizes/runGrapefruits.sh delete mode 100644 comparing-tile-sizes/run_experiment.sh delete mode 100644 comparing-tile-sizes/scrapeGrapefruits.sh delete mode 100644 comparing-tile-sizes/scrutinizeGrapefruits.sh delete mode 100644 runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt delete mode 100644 runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c delete mode 100644 runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h delete mode 100644 runtime/samples/fakeNN/CMakeLists.txt delete mode 100644 runtime/samples/fakeNN/fakeNN.py delete mode 100644 runtime/samples/fakeNN/fakeNN_util.c delete mode 100644 runtime/samples/fakeNN/fakeNN_util.h delete mode 100644 runtime/samples/fakeNN/requirements.txt delete mode 100644 runtime/samples/fakeNN/ts-answer.json delete mode 100644 runtime/samples/grapeFruit/CMakeLists.txt delete mode 100644 runtime/samples/grapeFruit/disp-9-bandaid.json delete mode 100644 runtime/samples/grapeFruit/grapeFruit.py delete mode 100644 runtime/samples/grapeFruit/grapeFruit_util.c delete mode 100644 runtime/samples/grapeFruit/grapeFruit_util.h delete mode 100644 runtime/samples/grapeFruit/requirements.txt delete mode 100644 runtime/samples/grapeFruit/ts-answer.json delete mode 100644 runtime/samples/nsnet2/0-40-100.json rename comparing-tile-sizes/old-tiling-schemes/0-40-100.json => runtime/samples/nsnet2/manually-chosen-tiles.json (100%) diff --git a/.gitignore b/.gitignore index 88428701..d5d8384a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ Testing/* build-notes/* build/* toolchain/* -fakeNN/* \ No newline at end of file +fakeNN/* + +comparing-tiling-schemes/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 975b8b13..a711236e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,3 @@ [submodule "xdsl"] path = xdsl url = https://github.com/xdslproject/xdsl -[submodule "myrtle"] - path = myrtle - url = https://github.com/CAPS-UMU/myrtle.git diff --git a/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp b/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp index c076a58f..063bae69 100644 --- a/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp +++ b/codegen/compiler/src/Quidditch/Conversion/ConvertSnitchToLLVM.cpp @@ -176,24 +176,6 @@ struct ComputeCoreIndexOpLowering : ConvertOpToLLVMPattern { } }; -struct MyrtleRecordCyclesOpLowering : ConvertOpToLLVMPattern { - - LLVM::LLVMFuncOp myrtleRecordCyclesFunc; - - MyrtleRecordCyclesOpLowering(LLVM::LLVMFuncOp myrtleRecordCyclesFunc, - const LLVMTypeConverter &converter) - : ConvertOpToLLVMPattern(converter), - myrtleRecordCyclesFunc(myrtleRecordCyclesFunc) {} - LogicalResult - matchAndRewrite(MyrtleRecordCyclesOp op, OpAdaptor adaptor, - ConversionPatternRewriter &rewriter) const override { - rewriter.replaceOpWithNewOp(op, myrtleRecordCyclesFunc, - ValueRange({op.getI(), op.getJ()})); - return success(); - } -}; - - } // namespace void quidditch::populateSnitchToLLVMConversionPatterns( @@ -201,26 +183,15 @@ void quidditch::populateSnitchToLLVMConversionPatterns( RewritePatternSet &patterns) { auto builder = OpBuilder::atBlockEnd(moduleOp.getBody()); - // snrt cluster core idx IntegerType i32 = builder.getI32Type(); auto computeCoreIndex = builder.create( builder.getUnknownLoc(), "snrt_cluster_core_idx", LLVM::LLVMFunctionType::get(i32, ArrayRef{})); computeCoreIndex->setAttr("hal.import.bitcode", builder.getUnitAttr()); - // myrtle record cycles - auto myrtleFnType = LLVM::LLVMFunctionType::get( - builder.getType(), - {i32, i32}, - /*isVarArg=*/false); - auto myrtleRecordCycles = builder.create( - builder.getUnknownLoc(), "myrtle_record_cycles", - myrtleFnType); - myrtleRecordCycles->setAttr("hal.import.bitcode", builder.getUnitAttr()); patterns.insert(typeConverter); patterns.insert(computeCoreIndex, typeConverter); - patterns.insert(myrtleRecordCycles, typeConverter); patterns.insert(SymbolTable(moduleOp), typeConverter); } diff --git a/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp b/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp index b66c959b..2ecd87ac 100644 --- a/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp +++ b/codegen/compiler/src/Quidditch/Conversion/ConvertToRISCV.cpp @@ -31,8 +31,6 @@ class ConvertToRISCV private: FailureOr convertToRISCVAssembly(MemRefMicrokernelOp kernelOp, StringAttr kernelName); - FailureOr convertToRISCVAssemblyMyrtle(MemRefMicrokernelOp kernelOp, - StringAttr kernelName); }; } // namespace @@ -55,97 +53,6 @@ static Type transformType(Type type) { strideReplacement, memRefType.getMemorySpace()); } -FailureOr -ConvertToRISCV::convertToRISCVAssemblyMyrtle(MemRefMicrokernelOp kernelOp, - StringAttr kernelName) { - if (!llvm::all_of(kernelOp.getBody().getArgumentTypes(), - CallMicrokernelOp::supportsArgumentType)) { - auto emit = assertCompiled ? &MemRefMicrokernelOp::emitError - : &MemRefMicrokernelOp::emitWarning; - - (kernelOp.*emit)("function inputs ") - << kernelOp.getBody().getArgumentTypes() - << " do not support bare-pointer calling convention required by " - "xDSL."; - return failure(); - } - - SmallVector argumentTypes = - llvm::map_to_vector(kernelOp.getBody().getArgumentTypes(), transformType); - - OpBuilder builder(&getContext()); - OwningOpRef tempFuncOp = - builder.create(kernelOp.getLoc(), kernelName, - builder.getFunctionType(argumentTypes, {})); - IRMapping mapping; - kernelOp.getBody().cloneInto(&tempFuncOp->getBody(), mapping); - for (BlockArgument argument : tempFuncOp->getArguments()) - argument.setType(transformType(argument.getType())); - - builder.setInsertionPointToEnd(&tempFuncOp->getBody().back()); - - builder.create(kernelOp.getLoc()); - - SmallString<64> stdinFile; - int stdinFd; - if (llvm::sys::fs::createTemporaryFile("xdsl-in", "mlir", stdinFd, stdinFile)) - return failure(); - - llvm::FileRemover stdinFileRemove(stdinFile); - { - llvm::raw_fd_ostream ss(stdinFd, /*shouldClose=*/true); - tempFuncOp->print(ss, OpPrintingFlags().useLocalScope()); - } - - SmallString<64> stdoutFile; - if (llvm::sys::fs::createTemporaryFile("xdsl-out", "S", stdoutFile)) - return failure(); - - llvm::FileRemover stdoutFileRemove(stdoutFile); - - SmallString<64> stderrFile; - if (llvm::sys::fs::createTemporaryFile("xdsl-diag", "S", stderrFile)) - return failure(); - - llvm::FileRemover stderrFileRemove(stderrFile); - - std::optional redirects[3] = {/*stdin=*/stdinFile.str(), - /*stdout=*/stdoutFile.str(), - /*stderr=*/stderrFile.str()}; - int ret = llvm::sys::ExecuteAndWait( - xDSLOptPath, - {xDSLOptPath, "-p", - "arith-add-fastmath," - "convert-linalg-to-memref-stream," - "test-optimise-memref-stream," // NOLINT(*-suspicious-missing-comma) - "test-lower-memref-stream-to-snitch-stream," - "test-lower-snitch-stream-to-asm", - "-t", "riscv-asm"}, - std::nullopt, redirects); - - - if (ret != 0) { - auto diagEmit = - assertCompiled ? &Operation::emitError : &Operation::emitWarning; - - InFlightDiagnostic diag = - ((kernelOp)->*diagEmit)("Failed to translate kernel with xDSL"); - - if (llvm::ErrorOr> buffer = - llvm::MemoryBuffer::getFile(stderrFile, /*IsText=*/true)) - diag.attachNote() << "stderr:\n" << buffer.get()->getBuffer(); - - return diag; - } - - llvm::ErrorOr> buffer = - llvm::MemoryBuffer::getFile(stdoutFile, /*IsText=*/true); - if (!buffer) - return kernelOp.emitError("failed to open ") << stdoutFile; - - return StringAttr::get(&getContext(), (*buffer)->getBuffer()); -} - FailureOr ConvertToRISCV::convertToRISCVAssembly(MemRefMicrokernelOp kernelOp, StringAttr kernelName) { @@ -238,7 +145,6 @@ ConvertToRISCV::convertToRISCVAssembly(MemRefMicrokernelOp kernelOp, void ConvertToRISCV::runOnOperation() { ModuleOp module = getOperation(); SymbolTable symbolTable(module); - // module.emitWarning() << "Trying to see if my snitch instruction survived! TURKEY\n"; std::size_t kernelIndex = 0; module.walk([&](MemRefMicrokernelOp kernelOp) { @@ -250,12 +156,8 @@ void ConvertToRISCV::runOnOperation() { FailureOr riscvAssembly = convertToRISCVAssembly(kernelOp, kernelName); - // FailureOr riscvAssembly = - // convertToRISCVAssemblyMyrtle(kernelOp, kernelName); // uncomment this line to see the streaming region and repeat value if (failed(riscvAssembly)) { - // module->emitWarning()<<"\nRADDISH: (convertToRISCV) convert to RISCV assembly failed\n"; if (assertCompiled) { - // module->emitWarning()<<"\nRADDISH: (convertToRISCV) assertion that I compiled ALSO failed\n"; signalPassFailure(); return WalkResult::interrupt(); } @@ -263,7 +165,6 @@ void ConvertToRISCV::runOnOperation() { auto builder = IRRewriter(kernelOp); builder.inlineBlockBefore(&kernelOp.getBody().front(), kernelOp, kernelOp.getInputs()); - //module->emitWarning()<<"\nRADDISH: (convertToRISCV) erasing the kernel op and continuing on...\n"; kernelOp.erase(); return WalkResult::advance(); } @@ -273,19 +174,6 @@ void ConvertToRISCV::runOnOperation() { builder.create(kernelOp.getLoc(), kernelName, kernelOp.getInputs(), *riscvAssembly); kernelOp.erase(); - //module.emitWarning() << "\nTURKEY\n does this show the riscv?"; // delete later return WalkResult::advance(); }); - - // uncomment this module walk to get the riscv for the operation vvvvvvvvvv - // module.walk([&](mlir::func::FuncOp funcOp) { // delete later - // if(funcOp.getName() == - // "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64"){ - - // funcOp->emitWarning() << "\nIs this the riscv we're looking for?"; - // // This is the rewritten kernel!!!!!\n"; - - // } - // }); //delete later - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp index 50fa4ff2..143cabf6 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.cpp @@ -410,15 +410,6 @@ void ComputeCoreIndexOp::replaceWithNoop(RewriterBase &rewriter) { rewriter.replaceOpWithNewOp(*this, 0); } -//===----------------------------------------------------------------------===// -// MyrtleRecordCyclesOp::DMACoreSpecializationOpInterface -//===----------------------------------------------------------------------===// - -void MyrtleRecordCyclesOp::replaceWithNoop(RewriterBase &rewriter) { - // Not sure this is how I should implement this function for MyrtleRecordCycles - rewriter.eraseOp(*this); -} - //===----------------------------------------------------------------------===// // PipelineOp //===----------------------------------------------------------------------===// diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td index 20265060..86122955 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/IR/QuidditchSnitchOps.td @@ -214,45 +214,6 @@ def QuidditchSnitch_ComputeCoreIndexOp }]; } -// AllTypesMatch<["init_args", "results"] -// let arguments = (ins -// Index:$lower_bound, -// Index:$upper_bound, -// Index:$step, -// Variadic:$init_args -// ); -//attr-dict $tensor_type `->` type($results) - // let assemblyFormat = [{ - // $lhs `,` $rhs `,` $acc attr-dict - // `:` type($lhs) `,` type($rhs) `into` type($acc) - // }]; - -def QuidditchSnitch_MyrtleRecordCyclesOp - : QuidditchSnitch_Op<"myrtle_record_cycles", [AllTypesMatch<["i", "j"]>, - QuidditchSnitch_DMACoreSpecializationOpInterface]> { - - let description = [{ - Saves current number of cycles in global array myrtle_actual_cycles[i][j], - where i represents a specific kernel, and j represents whether we are - at the start or end of that kernel. - }]; - - let arguments = (ins - I32:$i, - I32:$j - ); - - // let results = (outs Index:$result); - - let assemblyFormat = [{ - $i `,` $j attr-dict - }]; - - let extraClassDeclaration = [{ - void replaceWithNoop(mlir::RewriterBase& rewriter); - }]; -} - def QuidditchSnitch_PipelineOp : QuidditchSnitch_Op<"pipeline", [AllTypesMatch<["init_args", "results"]>, RecursivelySpeculatable, RecursiveMemoryEffects, diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td index 0da881eb..015ae863 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/Passes.td @@ -77,16 +77,7 @@ def SpecializeDMACodePass : Pass<"quidditch-specialize-dma-code", removed while the original version has all DMA transfer operations removed. Barriers are inserted where data dependencies require either transfers or computations to have finished. - - If the timeDispatch option is enabled, calls to myrtle_record_cycles will be - inserted at the beginning and end of the specified "dma" version. }]; - - let options = [ - Option<"timeDispatch", "time-dispatch", "std::string", /*default=*/"", - "Flag to time a dispatch. \'grapeFruit\' for every NsNet2 dispatch," - " or \'fakennMxNxK\' where M,N,K are integers"> - ]; } def LowerForallOpPass : Pass<"quidditch-lower-forall-op"> { diff --git a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp index fdfcf260..b7926ee0 100644 --- a/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp +++ b/codegen/compiler/src/Quidditch/Dialect/Snitch/Transforms/SpecializeDMACode.cpp @@ -16,15 +16,11 @@ class SpecializeDMACode SpecializeDMACode> { public: using Base::Base; - SpecializeDMACode(const quidditch::Snitch::SpecializeDMACodePassOptions &options) { - this->timeDispatch = options.timeDispatch; - } protected: void runOnOperation() override; private: -std::string timeDispatch = ""; }; } // namespace @@ -59,89 +55,6 @@ static void insertBarriers(FunctionOpInterface function) { }); } -static int myrtleKernelIndex(FunctionOpInterface funcOp, std::string timeDispatch) { - // check if timeDispatch setting is set to fakennMxNxK where M, N, K, are integers - if(timeDispatch.substr (0,6) == "fakenn"){ - std::string splittable = timeDispatch.substr(6,std::string::npos); - std::string onlyDisp = "main$async_dispatch_0_matmul_transpose_b_"+splittable+"_f64$dma"; - if (funcOp.getName() == - onlyDisp) { - return 0; - } - return -1; - - } - // use the below version for grapeFruit setting (timing 5 nsnet kernels) VVV - if(timeDispatch == "grapeFruit"){ - if (funcOp.getName() == - "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64$dma") { - return 0; - } - if (funcOp.getName() == - "main$async_dispatch_0_matmul_transpose_b_1x400x161_f64$dma") { - return 1; - } - if (funcOp.getName() == - "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64$dma") { - return 2; - } - if (funcOp.getName() == - "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64$dma") { - return 3; - } - if (funcOp.getName() == - "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64$dma") { - return 4; - } - return -1; - } - // use the above version for grapeFruit (timing 5 nsnet kernels) ^^^ - // otherwise return failure - return -1; -} - -static bool insertMyrtleRecordCycles(FunctionOpInterface function,std::string timeDispatch) { - // only time functions that we care about - int kernelIndex = myrtleKernelIndex(function, timeDispatch); - if (kernelIndex == -1) { - return false; - } - int first = 0; - // find the first operation and insert record_cycles - for (auto &block : function) { - for (auto &operation : block) { - first++; - if (first == 1) { - OpBuilder builder(operation.getContext()); - builder.setInsertionPoint(&operation); - Type i32Type = builder.getIntegerType(32); - Value i = builder.create( - operation.getLoc(), builder.getIntegerAttr(i32Type, kernelIndex)); - Value j = builder.create( - operation.getLoc(), builder.getIntegerAttr(i32Type, 0)); - builder.create(operation.getLoc(), i, j); - } - } - } - // find the last operation, and insert record_cycles - for (FunctionOpInterface::reverse_iterator it = function.rbegin(), - e = function.rend(); - it != e; ++it) { - OpBuilder builder(it->back().getContext()); - - builder.setInsertionPoint(&it->back()); - Type i32Type = builder.getIntegerType(32); - Value i = builder.create( - it->back().getLoc(), builder.getIntegerAttr(i32Type, kernelIndex)); - Value j = builder.create( - it->back().getLoc(), builder.getIntegerAttr(i32Type, 1)); - builder.create(it->back().getLoc(), i, j); - - break; - } - return true; -} - void SpecializeDMACode::runOnOperation() { auto *dialect = getContext().getLoadedDialect(); SymbolTable table(getOperation()); @@ -155,11 +68,6 @@ void SpecializeDMACode::runOnOperation() { FunctionOpInterface clone = function.clone(); clone.setName((clone.getName() + "$dma").str()); - // try to insert a call to our new myrtle_record_cycles function - // only insert timing functions if time-dispatch option has been enabled - if (timeDispatch != ""){ - insertMyrtleRecordCycles(clone,timeDispatch); - } table.insert(clone, std::next(function->getIterator())); dialect->getDmaSpecializationAttrHelper().setAttr( function, FlatSymbolRefAttr::get(clone)); diff --git a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp index c3657f73..06662503 100644 --- a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp +++ b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp @@ -2,7 +2,6 @@ #include #include -#include "Myrtle.h" #include "Quidditch/Dialect/Snitch/IR/QuidditchSnitchAttrs.h" #include "TilingScheme.h" #include "iree/compiler/Codegen/Dialect/Codegen/IR/IREECodegenAttrs.h" @@ -30,22 +29,14 @@ class ConfigureTiles using Base::Base; ConfigureTiles(const quidditch::ConfigureTilesOptions &options) { this->importTiles = options.importTiles; - this->myrtleOut = options.myrtleOut; - this->myrtleMode = options.myrtleMode; - this->myrtlePath = options.myrtlePath; this->tbl = (quidditch::TileInfoTbl *)options.tablePointer; } - std::string errs = ""; protected: void runOnOperation() override; private: std::string importTiles = ""; - std::string myrtlePath = ""; - std::string myrtleMode = ""; - std::string myrtleOut = ""; - int acc = 0; quidditch::TileInfoTbl *tbl; }; } // namespace @@ -60,41 +51,10 @@ static LogicalResult setTranslationInfo(FunctionOpInterface funcOp) { static LogicalResult setRootConfig(FunctionOpInterface funcOp, Operation *rootOp, - quidditch::TileInfoTbl *tbl, std::string myrtlePath, - std::string myrtleMode, std::string myrtleOut) { + quidditch::TileInfoTbl *tbl) { return TypeSwitch(rootOp) .Case([&](linalg::LinalgOp op) { - std::string tileSizesPath = myrtleOut; - std::string dispatchName = funcOp.getName().str(); - // if myrtle enabled, automatically generate tile sizes - if (myrtlePath != "") { - pid_t pid = fork(); - if (pid == 0) { - char *intrepreter = (char *)"python3"; - char *pythonPath = (char *)myrtlePath.c_str(); - char *pythonArgs[] = {intrepreter, - pythonPath, - (char *)dispatchName.c_str(), - (char *)myrtleMode.c_str(), - (char *)tileSizesPath.c_str(), - NULL}; - execvp(intrepreter, pythonArgs); - } - int status; - wait(&status); - if(status != 0){ - funcOp.emitWarning() << "\nMyrtle Failed with exit status "< workgroupTiles(3, 0); SmallVector l1Tiles(3, 0); SmallVector l1Interchange = {2, 0, 1}; @@ -104,13 +64,14 @@ setRootConfig(FunctionOpInterface funcOp, Operation *rootOp, funcOp.emitWarning() << "\nConfigureTiles: Table pointer is zero!!"; return failure(); } - // look up the tile size, interchange, and double buffering settings + + // Look up the tile size, interchange, and double buffering settings // from table auto search = tbl->find(funcOp.getName().str()); if (search == tbl->end()) { funcOp.emitWarning() - << "\nConfigureTiles: Root operation of this function " - "is missing tiling scheme!"; + << "\nConfigureTiles: Root operation of this dispatch " + "is a missing tiling scheme!"; return failure(); } quidditch::TilingScheme &ts = search->second; @@ -135,11 +96,6 @@ setRootConfig(FunctionOpInterface funcOp, Operation *rootOp, } void ConfigureTiles::runOnOperation() { - if (importTiles == "" && myrtlePath == "") { - // skip this pass when no arguments passed - return; - } - FunctionOpInterface funcOp = getOperation(); // TODO: un-comment out check for translationInfo, instead of blindly @@ -171,8 +127,7 @@ void ConfigureTiles::runOnOperation() { getLoweringConfig(rootOperation); // only add the lowering config if one does not exist already if (!loweringConfig) { - if (failed(setRootConfig(funcOp, rootOperation, tbl, myrtlePath, myrtleMode, - myrtleOut))) { + if (failed(setRootConfig(funcOp, rootOperation, tbl))) { funcOp.emitWarning() << "\nConfigureTiles: set root config failed\n"; return signalPassFailure(); diff --git a/codegen/compiler/src/Quidditch/Target/Passes.td b/codegen/compiler/src/Quidditch/Target/Passes.td index 51f2cdba..a784956a 100644 --- a/codegen/compiler/src/Quidditch/Target/Passes.td +++ b/codegen/compiler/src/Quidditch/Target/Passes.td @@ -35,18 +35,11 @@ def ConfigureTiles : InterfacePass<"quidditch-configure-tiles", "mlir::FunctionO let description = [{ Within each iree dispatch, annotate the root linalg operation with a tiling scheme (tile sizes + loop interchange). Caveat: only tiles linalg operations of type matmul_transpose_b (for now) - Set the importTiles option if you would like to specify tile sizes for some (or all) of the dispatches. - Set the myrtlePath, myrtleMode, and myrtleOut options if you would like myrtle to automatically pick the tile sizes. + Set the importTiles option to the path to the json file containing the tiling scheme for each dispatch. }]; let options = [ Option<"importTiles", "import-tiles", "std::string", /*default=*/"", "Name of a JSON file specifying loop bounds and order for each root linalg operation.">, - Option<"myrtleMode", "myrtle-mode", "std::string", /*default=*/"", - "Tile selection mode for myrtle: sflt, scyc, or svrcyc">, - Option<"myrtlePath", "myrtle", "std::string", /*default=*/"", - "Path to Myrtle executable">, - Option<"myrtleOut", "myrtle-out", "std::string", /*default=*/"", - "Path to Myrtle output json file">, Option<"tablePointer", "NeverPassAValueHere", "std::uintptr_t", /*default=*/"0", "Hacky way to prevent opening input file multiple times. Never pass a value to this option.">, ]; diff --git a/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp b/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp index 7a2fc620..46a23a34 100644 --- a/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp +++ b/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp @@ -84,14 +84,9 @@ struct QuidditchTargetOptions { std::string xDSLOptPath; std::string toolChainRoot; bool assertCompiled = false; - std::string timeDispatch = ""; // added for Specialize DMA Code Pass std::string importTiles = ""; // added for Configure Tiles Pass - std::string myrtleMode = ""; // added for Configure Tiles Pass - std::string myrtlePath = ""; // added for Configure Tiles Pass - std::string myrtleOut = ""; quidditch::TileInfoTbl tileInfo = quidditch::TileInfoTbl(); // added for Configure Tiles Pass - std::string tableInfoErrs = "FROG "; // TODO: This should actually be 112640 but DMA stack overflows. Ooopsie! unsigned l1MemoryBytes = 100000; @@ -119,26 +114,11 @@ struct QuidditchTargetOptions { "iree-quidditch-toolchain-root", toolChainRoot, llvm::cl::cat(category), llvm::cl::desc("Path to the root directory of the Quidditch toolchain " "(containing the toolchain file)")); - // added for SpecializeDMACode Pass (to record dispatch cycles for myrtle) - binder.opt( - "iree-quidditch-time-disp", timeDispatch, llvm::cl::cat(category), - llvm::cl::desc("Flag to enable timing dispatches for myrtle")); // added for Configure Tiles Pass binder.opt( "iree-quidditch-import-tiles", importTiles, llvm::cl::cat(category), llvm::cl::desc( "Path to a JSON file from which we import tiling schemes")); - binder.opt( - "iree-quidditch-myrtle-mode", myrtleMode, llvm::cl::cat(category), - llvm::cl::desc( - "Choose tile selection method with sflt, scyc, or svrcyc.")); - binder.opt("iree-quidditch-myrtle", myrtlePath, - llvm::cl::cat(category), - llvm::cl::desc("Complete path to myrtle script")); - binder.opt( - "iree-quidditch-myrtle-out", myrtleOut, llvm::cl::cat(category), - llvm::cl::desc( - "Path to json in which myrtle stores its chosen tile sizes.")); binder.opt( "iree-quidditch-assert-compiled", assertCompiled, llvm::cl::cat(category), @@ -210,14 +190,13 @@ class QuidditchTargetBackend final : public IREE::HAL::TargetBackend { std::string errs; quidditch::fillTileInfoTable(&targetOptions.tileInfo, targetOptions.importTiles, errs); + } // automatically tile the rest of the dispatches funcPassManager.addPass([&] { auto thePass = quidditch::createConfigureTiles( - {targetOptions.importTiles, targetOptions.myrtleMode, - targetOptions.myrtlePath, targetOptions.myrtleOut, - (std::uintptr_t)&targetOptions.tileInfo}); + {targetOptions.importTiles, (std::uintptr_t)&targetOptions.tileInfo}); return thePass; }); } @@ -302,8 +281,7 @@ class QuidditchTargetBackend final : public IREE::HAL::TargetBackend { .addPass(createLinalgGeneralizeNamedOpsPass) .addPass(quidditch::createRemoveTrivialLoopsPass); - modulePassManager.addPass(quidditch::Snitch::createSpecializeDMACodePass( - {targetOptions.timeDispatch})); + modulePassManager.addPass(quidditch::Snitch::createSpecializeDMACodePass()); FunctionLikeNest(modulePassManager) .addPass(quidditch::SnitchDMA::createLegalizeDMAOperationsPass) .addPass(createCanonicalizerPass) diff --git a/comparing-tile-sizes/.gitignore b/comparing-tile-sizes/.gitignore deleted file mode 100644 index ed021232..00000000 --- a/comparing-tile-sizes/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -./*/logs/ -./1x*x*wm-n-k/*/* -./*/cmakeOutput.txt -./*/buildOutput.txt -0-40-100/GrapeFruit -0-40-200/GrapeFruit -0-80-100/GrapeFruit -0-40-120/GrapeFruit -0-48-100/GrapeFruit -0-80-80/GrapeFruit -*/GrapeFruit -tile-sizes-to-test/* -0-24-100/* -0-24-120/* -0-32-100/* -0-40-100/* -0-40-100/* -0-40-80/* -0-48-80/* -0-56-50/* -0-64-50/* -0-64-80/* -0-72-50/* -0-72-80/* -0-80-40/* -0-80-50/* -0-80-80/* -0-80-80/* -0-88-80/* -0-96-40/* -emily-tile-sizes/* -emily-tile-sizes/* -ndb-0-40-100/* -ndb-0-40-120/* -ndb-0-40-200/* -ndb-0-48-100/* -ndb-0-80-100/* -ndb-0-80-80/* -rejected-0-24-200/* -rejected-0-32-120/* -rejected-0-48-100/* -rejected-0-56-80/* diff --git a/comparing-tile-sizes/README.md b/comparing-tile-sizes/README.md deleted file mode 100644 index 9aade6f3..00000000 --- a/comparing-tile-sizes/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# Specify Tile Sizes for a Particular Linear Layer of NsNet2 - -TODO: Better Diagram - -![](context.png) - -Each PyTorch Linear Layer inside NsNet2 eventually compiles to an IREE dispatch containing two linalg operations: a `linalg.matmul_tranpose_b` and a `linalg.generic` representing an element wise addition. In the case of these dispatches, the root operation is always the `linalg.matmul_tranpose_b` operation. - -**This guide explains how to *automatically*** - -- **COMPILE NsNet2 with a one of these five IREE dispatches customized with your requested tile sizes for tiling its root operation** -- **RUN this uniquely tiled NsNet2 on a verilator simulation of the Snitch Cluster** -- **EXPORT cycle count for each dispatch and the total cycle count to a CSV** -- **REPEAT these steps for many tiling configurations en masse** - -## I. Run NsNet2 with a Dispatch M-N-K using an m-n-k tiling scheme - -Navigate to the `Quidditch/comparing-tile-sizes directory` and do - -``` -. run_experiment.sh -``` - -where - -- `` is the name of the csv file assumed to be located inside directory `Quidditch/comparing-tiles-sizes/`. - - ``` - JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim - 0-24-8,24,8,3136,192,82464,1,45000,720000,360000,120000,240000,1,3,8,15000,3,8 - 0-24-10,24,10,3920,240,81680,1,36000,720000,360000,120000,240000,1,3,10,12000,3,10 - 0-24-12,24,12,4704,288,80896,1,30000,720000,360000,120000,240000,1,3,12,10000,3,12 - ``` - - ^^ example input csv file ^^ - - Each row of the spreadsheet represents a different tiling scheme for the same NsNet2 layer specified by ``. The rest of the layers inside NsNet2 will use a the default scheme defined in `generateTileSizeJSONFiles.py`. See section IV for more details. - - If you only want to run NsNet2 once, just provide a CSV with a single row like in `ex_mini.csv`. - -- `` is a flag to select whether to generate a JSON file for each tiling schemes specified in `searchSpace.csv` - - - `genJsons` enables this step - - `no` skips this step - -- `` is a flag to select whether to compile the fakeNN for each tiling scheme specified - - - `compile` enables this step - - `no` skips this step - - `status` checks for compilation errors - -- `` is a flag to select whether to run the fakeNN executable for each tiling scheme specified - - - `run` enables this step - - `no` skips this step - -- `` is a flag to select whether to export the results of each fakeNN to a csv file - - - `export` enables this step - - `no` skips this step - - `correctness` checks the output of each fakeNN run with the its corresponding golden output file. - -- `caseNo` is a legacy option that should always be passed a value of `1`. - -- `dispatchName` refers to the exact name of the IREE dispatch you would like to tile. There are only 5 dispatches in NsNet2 we can tile. - - | Possible dispatchName Values | - | ------------------------------------------------------------ | - | `"main\$async_dispatch_9_matmul_transpose_b_1x161x600_f64"` | - | `"main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64"` | - | `"main\$async_dispatch_0_matmul_transpose_b_1x400x161_f64"` | - | `"main\$async_dispatch_1_matmul_transpose_b_1x1200x400_f64"` | - | `"main\$async_dispatch_7_matmul_transpose_b_1x600x400_f64"` | - -## II. Example Runs - -It's recommended to perform one of the four steps, check the results, and then proceed to the next step. - -1. generate jsons - - ``` - . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" genJsons no no no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" - ``` - -2. compile - - ``` - . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no compile no no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" - ``` - -3. status (check for compilation errors) - - ``` - . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no status no no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" - ``` - -4. run - - ``` - . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no no run no 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" - ``` - -5. export results - - ``` - . run_experiment.sh "ex_1x600x600wm-n-k_case1_searchSpace.csv" "1x600x600wm-n-k" no no no export 1 "main\$async_dispatch_8_matmul_transpose_b_1x600x600_f64" - ``` - -## IV. Excerpt: Default Tiling Schemes for each Dispatch - -Excerpt from `generateTileSizeJSONFiles.py`: - -``` - # dispatch 1 - node = {} - node["tile-sizes"] = [[0], [40], [100]] - node["loop-order"] = [[2,0], [0,0], [1,0]] - node["dual-buffer"] = True - data["main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64"]=node - # dispatch 0 - node = {} - node["tile-sizes"] = [[0], [40], [0]] - node["loop-order"] = [[2,0], [0,0], [1,0]] - node["dual-buffer"] = False - data["main$async_dispatch_0_matmul_transpose_b_1x400x161_f64"]=node - # dispatch 7 - node = {} - node["tile-sizes"] = [[0], [40], [100]] - node["loop-order"] = [[2,0], [0,0], [1,0]] - node["dual-buffer"] = True - data["main$async_dispatch_7_matmul_transpose_b_1x600x400_f64"]=node - # dispatch 8 - node = {} - node["tile-sizes"] = [[0], [40], [100]] - node["loop-order"] = [[2,0], [0,0], [1,0]] - node["dual-buffer"] = True - data["main$async_dispatch_8_matmul_transpose_b_1x600x600_f64"]=node - # dispatch 9 - node = {} - node["tile-sizes"] = [[0], [56], [100]] - node["loop-order"] = [[2,0], [0,0], [1,0]] - node["dual-buffer"] = True - data["main$async_dispatch_9_matmul_transpose_b_1x161x600_f64"]=node -``` - diff --git a/comparing-tile-sizes/cmakelist-epilogue.txt b/comparing-tile-sizes/cmakelist-epilogue.txt deleted file mode 100644 index d9c979a7..00000000 --- a/comparing-tile-sizes/cmakelist-epilogue.txt +++ /dev/null @@ -1,42 +0,0 @@ -add_library(grapeFruit_util grapeFruit_util.c) -target_link_libraries(grapeFruit_util - PRIVATE - samples_util - snRuntimeInterface - Quidditch::dispatch::dispatch - Quidditch::time_dispatch::time_dispatch -) -target_include_directories(grapeFruit_util INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -macro(create_experiment_variant) - cmake_parse_arguments(_RULE "PRECOMMIT;NIGHTLY" "TARGET;IREE_MODULE;QUERY_FUNC" "" ${ARGN}) - - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c "\ -#include <${_RULE_IREE_MODULE}.h> - -#include \"grapeFruit_util.h\" - -int main() { - return run_grapeFruit_experiment(${_RULE_QUERY_FUNC}); -} -") - add_executable(${_RULE_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c) - target_link_libraries( - ${_RULE_TARGET} - PRIVATE - grapeFruit_util - ${_RULE_IREE_MODULE} - snRuntime - ) -endmacro() - -create_experiment_variant( - TARGET GrapeFruit - IREE_MODULE grapeFruit - QUERY_FUNC "quidditch_compiled_ns_net2_linked_quidditch_library_query" -) -create_experiment_variant( - TARGET GrapeFruitLLVM - IREE_MODULE grapeFruit_llvm - QUERY_FUNC "compiled_ns_net2_linked_llvm_cpu_library_query" -) \ No newline at end of file diff --git a/comparing-tile-sizes/cmakelist-middle-original.txt b/comparing-tile-sizes/cmakelist-middle-original.txt deleted file mode 100644 index 284af60a..00000000 --- a/comparing-tile-sizes/cmakelist-middle-original.txt +++ /dev/null @@ -1,2 +0,0 @@ -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DST grapeFruit FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc LLVM DST grapeFruit_llvm FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) diff --git a/comparing-tile-sizes/cmakelist-prologue.txt b/comparing-tile-sizes/cmakelist-prologue.txt deleted file mode 100644 index 827968d9..00000000 --- a/comparing-tile-sizes/cmakelist-prologue.txt +++ /dev/null @@ -1,2 +0,0 @@ -set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle.py) -iree_turbine(SRC grapeFruit.py DST ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DTYPE "f64" M 5 N 6 K 7) diff --git a/comparing-tile-sizes/compileGrapefruits.sh b/comparing-tile-sizes/compileGrapefruits.sh deleted file mode 100644 index 98cb7523..00000000 --- a/comparing-tile-sizes/compileGrapefruits.sh +++ /dev/null @@ -1,123 +0,0 @@ -echo -e "compileGrapefruits.sh: ATTN: This should only be run by run_experiment.sh" -here=$(pwd) # save current directory so we can return to it -# script-specific constants" -quidditchDir="/home/hoppip/Quidditch" -tileSizes="$quidditchDir/comparing-tile-sizes/tile-sizes-to-test/*.json" -prologueFile="$quidditchDir/comparing-tile-sizes/cmakelist-prologue.txt" -middleFile="$quidditchDir/comparing-tile-sizes/cmakelist-middle-original.txt" -epilogueFile="$quidditchDir/comparing-tile-sizes/cmakelist-epilogue.txt" -searchSpaceCSV="$here/$1" -echo "searchSpaceCSV is $searchSpaceCSV" -compileOutputDirectory="$here/$2" -tileSizesToTest="$here/$2/tile-sizes-to-test" -# build-specific constants -grapefruitDir="$quidditchDir/runtime/samples/grapeFruit" -buildDir="$quidditchDir/build" -grapefruitExec="$buildDir/runtime/samples/grapeFruit/GrapeFruit" -verilator="$quidditchDir/toolchain/bin" - -# debugging -function_name(){ - echo "yohoho $1 $2" -} - -# generate cmakelists.txt file given -# 1. the tile sizes json (as form basename.json) -# 2. directory in which to save the cmakelists.txt file (do NOT use a relative path!) -gen_cmakelists(){ - # echo "" - tile="$tileSizesToTest/$1" - # echo "directory to save in is $2" - if [[ "$1" == "original" ]]; - then - cat $prologueFile > "$2/CMakeLists.txt" - cat $middleFile >> "$2/CMakeLists.txt" - cat $epilogueFile >> "$2/CMakeLists.txt" - else - cat $prologueFile > "$2/CMakeLists.txt" - echo "quidditch_module(SRC \${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DST grapeFruit FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-import-tiles=$tile)" >> "$2/CMakeLists.txt" - echo "quidditch_module(SRC \${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc LLVM DST grapeFruit_llvm FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-import-tiles=$tile)" >> "$2/CMakeLists.txt" - cat $epilogueFile >> "$2/CMakeLists.txt" - fi -} - -## this script requires a search space csv file -res=$(ls $searchSpaceCSV 2>/dev/null) -if [[ $searchSpaceCSV != $res ]]; - then - echo "ERROR: search space file $searchSpaceCSV not found!" - exit 1 -fi - - -if [[ "$3" == "status" ]]; - then - ## check whether each build was successful - for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) - do - basename=$ts # TODO: rename basname as ts everywhere - echo "checking $basename.json build..." # inform user we are checking build associated with $basename.json - grep "kernel does not fit into L1 memory and cannot be compiled" "$compileOutputDirectory/$basename/buildOutput.txt" - grep "Troublesome file path is" "$compileOutputDirectory/$basename/buildOutput.txt" - grep "FAILED: runtime-prefix/src/runtime-stamp/runtime-build" "$compileOutputDirectory/$basename/buildOutput.txt" - cd $here - done - gen_cmakelists "original" $grapefruitDir # generate original CMakeLists.txt - else - echo "compileGrapefruits.sh: generating the cmake files and compiling..." - for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) - do - basename=$ts # TODO: rename basname as ts everywhere - mkdir -p "$compileOutputDirectory/$basename" # create a local subfolder for this set of tile sizes - echo "$basename.json" # inform user we are about to start processing $basename.json - #exportedCostFile="$compileOutputDirectory/$basename/tilingCosts.json" # using full path here - gen_cmakelists "$ts.json" $grapefruitDir # generate basename-specific CMakeLists.txt - gen_cmakelists "$ts.json" "$compileOutputDirectory/$basename" # save a copy of it in our local subfolder - cd $buildDir - cmake .. -GNinja \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -DQUIDDITCH_TOOLCHAIN_FILE=../toolchain/ToolchainFile.cmake &> "$compileOutputDirectory/$basename/cmakeOutput.txt" - ninja -j 20 &> "$compileOutputDirectory/$basename/buildOutput.txt" - grep "kernel does not fit into L1 memory and cannot be compiled" "$compileOutputDirectory/$basename/buildOutput.txt" - grep "Troublesome file path is" "$compileOutputDirectory/$basename/buildOutput.txt" - cd $here - # copy generated executable to local folder - cp $grapefruitExec "$compileOutputDirectory/$basename/GrapeFruit" # copy SRC to DST - done - gen_cmakelists "original" $grapefruitDir # generate original CMakeLists.txt - -fi - - - - - - -# old notes below... - -# clear;sh scrapeGrapefruits.sh case1_searchSpace.csv "case_1" -# clear; sh compileGrapefruits.sh case1_searchSpace.csv "case_1" -# sh runGrapefruits.sh case1_searchSpace.csv "case_1" - -# existingExperiments=() -# missingExperiments=() - -# for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) -# do -# experimentResults="$here/$ts/run_output.txt" -# res=$(ls $experimentResults 2>/dev/null) -# if [[ $experimentResults == $res ]]; -# then -# existingExperiments+=("$experimentResults") -# else -# missingExperiments+=("$experimentResults") -# fi -# done -# echo "compileGrapefruits.sh: generating json files from the search space..." -# python generateTileSizeJSONFiles.py $searchSpaceCSV - -# rm --f -R "$compileOutputDirectory" # delete previous outputs -# mkdir "$compileOutputDirectory" # create fresh output folder \ No newline at end of file diff --git a/comparing-tile-sizes/context.png b/comparing-tile-sizes/context.png deleted file mode 100644 index 0c0b400cb555963b67a77e772db394ae9c8c7735..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71949 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�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/dev/null) -if [[ $searchSpaceCSV != $res ]]; - then - echo "ERROR: search space file $searchSpaceCSV not found!" - exit 1 -fi - - -echo "runGrapefruits.sh: running executables with verilator..." -batchSize=10 -counter=0 - for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) - do - counter=$((counter+1)) - basename=$ts # TODO: rename basname as ts everywhere - myExecutable="$compileOutputDirectory/$basename/GrapeFruit" - echo $myExecutable - cd $verilator - rm -R "$compileOutputDirectory/$basename/logs/" - nohup ./snitch_cluster.vlt $myExecutable &> "$compileOutputDirectory/$basename/run_output.txt" & - cp -r logs "$compileOutputDirectory/$basename/logs" - cd $here - if (( $counter % $batchSize == 0 )); then - wait - echo "starting new batch..." - fi - done -wait &> /dev/null diff --git a/comparing-tile-sizes/run_experiment.sh b/comparing-tile-sizes/run_experiment.sh deleted file mode 100644 index b621e4f3..00000000 --- a/comparing-tile-sizes/run_experiment.sh +++ /dev/null @@ -1,53 +0,0 @@ -echo "run_experiment.sh: ATTN: Run this script INSIDE directory Quidditch/comparing-tile-sizes/" -here=$(pwd) # save current directory so we can return to it -# script-specific constants -searchSpaceCSV="$here/$1" -experimentName="$2" -finalOutputDirectory="$here/$experimentName" -jsonOutputDirectory="$here/$experimentName/tile-sizes-to-test" -## this script requires a search space csv file -res=$(ls $searchSpaceCSV 2>/dev/null) -if [[ $searchSpaceCSV != $res ]]; - then - echo "ERROR: search space file $searchSpaceCSV not found!" - exit 1 -fi - -## generate json files -if [[ "$3" == "genJsons" ]]; - then - echo "run_experiment.sh: generating json files from the search space..." - mkdir -p $jsonOutputDirectory - python generateTileSizeJSONFiles.py $1 $8 $jsonOutputDirectory -fi - -## compile -if [[ "$4" == "compile" ]]; - then - ## compile - . compileGrapefruits.sh $1 $experimentName - ## check compilation results - else if [[ "$4" == "status" ]]; - then - . compileGrapefruits.sh $1 $experimentName status - fi -fi - -## run -if [[ "$5" == "run" ]]; - then - . runGrapefruits.sh $1 $experimentName -fi - - -## export -if [[ "$6" == "correctness" ]]; - then - . scrutinizeGrapefruits.sh $1 $experimentName $7 $8 -fi -if [[ "$6" == "export" ]]; - then - . scrapeGrapefruits.sh $1 $experimentName $7 $8 -fi - - diff --git a/comparing-tile-sizes/scrapeGrapefruits.sh b/comparing-tile-sizes/scrapeGrapefruits.sh deleted file mode 100644 index fbbc4850..00000000 --- a/comparing-tile-sizes/scrapeGrapefruits.sh +++ /dev/null @@ -1,102 +0,0 @@ -echo "scrapeGrapefruits.sh: ATTN: NEVER run this script directly; instead, call it from run_experiment.sh" -here=$(pwd) # save current directory so we can return to it -# script-specific constants -quidditchDir="/home/hoppip/Quidditch" -tileSizes="$quidditchDir/comparing-tile-sizes/tile-sizes-to-test/*.json" -prologueFile="$quidditchDir/comparing-tile-sizes/cmakelist-prologue.txt" -middleFile="$quidditchDir/comparing-tile-sizes/cmakelist-middle-original.txt" -epilogueFile="$quidditchDir/comparing-tile-sizes/cmakelist-epilogue.txt" -scrapeName="$2" -parsedResultsCSV="$here/$scrapeName/csv_experiment_results.csv" -searchSpaceCSV="$here/$1" -# build-specific constants -grapefruitDir="$quidditchDir/runtime/samples/grapeFruit" -buildDir="$quidditchDir/build" -grapefruitExec="$buildDir/runtime/samples/grapeFruit/GrapeFruit" -verilator="$quidditchDir/toolchain/bin" - -## debugging -function_name(){ - echo "yohoho $1" -} - -## helper function -parse_exp_result(){ - filePath=$1 - dispatchNo=$2 - dispatchName=$3 - basename=`basename $(echo $filePath | sed 's/run_output.txt//') | sed 's/[.][^.]*$//'` - kernelTime=$(grep -E "^(dispatch) $dispatchNo: ([0-9]*) - ([0-9]*) = ([0-9]*)" "$filePath" | grep -oE '[^[:space:]]+$') - totalTime=$(grep -E "cycles ([0-9]*)" "$filePath" | grep -oE '[^[:space:]]+$') - echo "$basename,$dispatchName,$kernelTime,$totalTime" >> $parsedResultsCSV -} - -## this script requires a search space csv file -res=$(ls $searchSpaceCSV 2>/dev/null) -if [[ $searchSpaceCSV != $res ]]; - then - echo "ERROR: search space file $searchSpaceCSV not found!" - exit 1 -fi - -existingExperiments=() -missingExperiments=() - -## scrape experiments from the search space -for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) - do - experimentResults="$here/$scrapeName/$ts/run_output.txt" - res=$(ls $experimentResults 2>/dev/null) - if [[ $experimentResults == $res ]]; - then - existingExperiments+=("$experimentResults") - else - missingExperiments+=("$experimentResults") - fi -done - - -echo "we will skip the following missing experiments:" -for element in "${missingExperiments[@]}" -do - echo $element -done -echo "we will export the following experiment results to a csv:" -for element in "${existingExperiments[@]}" -do - echo $element -done - -## generate fresh CSV output file -rm "$parsedResultsCSV" -rm "$here/$scrapeName/graphing.csv" -# rmdir "$here/$scrapeName" -# mkdir "$here/$scrapeName" -touch "$parsedResultsCSV" -echo "JSON Name,Kernel Name,Kernel Time,Total Time" >> $parsedResultsCSV - -## parse each experiment's run_output.txt -## and append the parsed info to the CSV file -for element in "${existingExperiments[@]}" -do - parse_exp_result $element $3 $4 -done - -## merge search space info with parsed results for graphing -python merge.py $searchSpaceCSV $parsedResultsCSV "JSON Name" -cp "merged.csv" "$here/$scrapeName/graphing.csv" -rm "merged.csv" - - - - - -# notes below! -# some helpful grep patterns to remember, -# even if not all of them used in current script: -# echo "third grep" -# grep -E "(:alpha:|[0-9]*) = [0-9]*" "$filePath" -# echo "fourth grep" -# grep -E "^(dispatch) ([0-9]*): ([0-9]*) - ([0-9]*) = ([0-9]*)" "$filePath" -# echo "fifth grep" -# grep -E "cycles ([0-9]*)" "$filePath" \ No newline at end of file diff --git a/comparing-tile-sizes/scrutinizeGrapefruits.sh b/comparing-tile-sizes/scrutinizeGrapefruits.sh deleted file mode 100644 index 59ecb9fe..00000000 --- a/comparing-tile-sizes/scrutinizeGrapefruits.sh +++ /dev/null @@ -1,88 +0,0 @@ -echo "scrutinizeGrapefruits.sh: ATTN: Run this script INSIDE directory Quidditch/comparing-tile-sizes/" -here=$(pwd) # save current directory so we can return to it -# script-specific constants -quidditchDir="/home/hoppip/Quidditch" -tileSizes="$quidditchDir/comparing-tile-sizes/tile-sizes-to-test/*.json" -prologueFile="$quidditchDir/comparing-tile-sizes/cmakelist-prologue.txt" -middleFile="$quidditchDir/comparing-tile-sizes/cmakelist-middle-original.txt" -epilogueFile="$quidditchDir/comparing-tile-sizes/cmakelist-epilogue.txt" -scrapeName="$2" -parsedResultsCSV="$here/$scrapeName/csv_experiment_results.csv" -searchSpaceCSV="$here/$1" -goldenOutputFile="$quidditchDir/comparing-tile-sizes/golden_output.txt" -# build-specific constants -grapefruitDir="$quidditchDir/runtime/samples/grapeFruit" -buildDir="$quidditchDir/build" -grapefruitExec="$buildDir/runtime/samples/grapeFruit/GrapeFruit" -verilator="$quidditchDir/toolchain/bin" - -## debugging -function_name(){ - echo "yohoho $1" -} - -## helper function -parse_exp_result(){ - filePath=$1 - runOutputJustValues="./temp.txt" - rm $runOutputJustValues 2> /dev/null - lineCount=$(wc --lines $filePath | head -n1 | sed -e 's/\s.*$//') - theTail=$(($lineCount - 1)) - theHead=$(($lineCount - 19)) - tail -n $theTail $filePath | head -n $theHead > $runOutputJustValues - diffResult=$(diff $goldenOutputFile $runOutputJustValues) - if [[ $diffResult != "" ]]; - then - echo "ERROR: $filePath contains incorrect results!" - echo $diffResult - else - echo "$filePath OK" - fi -} - -## this script requires a search space csv file -res=$(ls $searchSpaceCSV 2>/dev/null) -if [[ $searchSpaceCSV != $res ]]; - then - echo "ERROR: search space file $searchSpaceCSV not found!" - exit 1 -fi - -existingExperiments=() -missingExperiments=() - - -## scrape experiments from the search space -for ts in $(grep -oE '^(0-([0-9]*)-([0-9]*))' $searchSpaceCSV) - do - experimentResults="$here/$scrapeName/$ts/run_output.txt" - res=$(ls $experimentResults 2>/dev/null) - if [[ $experimentResults == $res ]]; - then - existingExperiments+=("$experimentResults") - else - missingExperiments+=("$experimentResults") - fi -done - - -echo "we will skipt the following missing experiments:" -for element in "${missingExperiments[@]}" -do - echo $element -done -echo "we will check the following experiments for correctness:" -for element in "${existingExperiments[@]}" -do - echo $element -done - -echo "checking..." - -## parse each experiment's run_output.txt -## and check that it matches golden reference -for element in "${existingExperiments[@]}" -do - parse_exp_result $element $3 $4 #looks to me like we can remove the last two args from this statement -done - diff --git a/requirements.txt b/requirements.txt index f5ce621a..f3eb16ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,3 @@ -r runtime/requirements.txt -r runtime/samples/nsnet2/requirements.txt -e xdsl --e myrtle diff --git a/runtime/cmake/quidditch_module.cmake b/runtime/cmake/quidditch_module.cmake index c873f1f7..cce72522 100644 --- a/runtime/cmake/quidditch_module.cmake +++ b/runtime/cmake/quidditch_module.cmake @@ -155,17 +155,15 @@ endfunction() # The python script should take one positional argument, which is the 'DST' # argument and output the MLIR file there. Additionally the 'DTYPE' flag is # communicated via a `--dtype=` flag. -# For the FakeNN sample test case, 'M', 'N' and 'K' should be integers that -# set the dimensions of the single linear layer. We use --M, --N, and --K to specify them. macro(iree_turbine) - cmake_parse_arguments(_RULE "" "SRC;DTYPE;M;N;K;DST" "" ${ARGN}) + cmake_parse_arguments(_RULE "" "SRC;DTYPE;DST" "" ${ARGN}) cmake_path(GET _RULE_SRC STEM filename) cmake_path(ABSOLUTE_PATH _RULE_SRC BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE source_path) add_custom_command( OUTPUT ${_RULE_DST} - COMMAND ${Python3_EXECUTABLE} ${source_path} ${_RULE_DST} --dtype=${_RULE_DTYPE} --m=${_RULE_M} --n=${_RULE_N} --k=${_RULE_K} + COMMAND ${Python3_EXECUTABLE} ${source_path} ${_RULE_DST} --dtype=${_RULE_DTYPE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${_RULE_SRC} COMMENT "Translating ${filename} using iree-turbine" diff --git a/runtime/runtime/src/Quidditch/CMakeLists.txt b/runtime/runtime/src/Quidditch/CMakeLists.txt index cd678bfc..f7bdb8c3 100644 --- a/runtime/runtime/src/Quidditch/CMakeLists.txt +++ b/runtime/runtime/src/Quidditch/CMakeLists.txt @@ -3,4 +3,3 @@ add_subdirectory(device) add_subdirectory(dispatch) add_subdirectory(executable) add_subdirectory(loader) -add_subdirectory(time_dispatch) diff --git a/runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt b/runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt deleted file mode 100644 index e7312c74..00000000 --- a/runtime/runtime/src/Quidditch/time_dispatch/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ - -iree_cc_library( - NAME - time_dispatch - SRCS - time_dispatch.c - DEPS - snRuntimeInterface - iree::base - PUBLIC -) diff --git a/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c b/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c deleted file mode 100644 index faab7662..00000000 --- a/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.c +++ /dev/null @@ -1,12 +0,0 @@ - -#include "time_dispatch.h" - -#include -unsigned long myrtle_actual_cycles[5][2] = { - {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}; - -void myrtle_record_cycles(uint32_t kernel, uint32_t atEnd) { - uint32_t register r; - asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); - myrtle_actual_cycles[kernel][atEnd] = r; -} diff --git a/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h b/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h deleted file mode 100644 index 02034075..00000000 --- a/runtime/runtime/src/Quidditch/time_dispatch/time_dispatch.h +++ /dev/null @@ -1,7 +0,0 @@ - -#pragma once - -#include -#include - -inline void myrtle_record_cycles(uint32_t kernel, uint32_t atEnd); diff --git a/runtime/samples/CMakeLists.txt b/runtime/samples/CMakeLists.txt index 1876a7e8..04527805 100644 --- a/runtime/samples/CMakeLists.txt +++ b/runtime/samples/CMakeLists.txt @@ -2,10 +2,5 @@ include(quidditch_module) add_subdirectory(big_matvec) add_subdirectory(nsnet2) -add_subdirectory(grapeFruit) -add_subdirectory(fakeNN) -add_subdirectory(calabaza) -add_subdirectory(caqui) add_subdirectory(util) add_subdirectory(vec_multiply) -add_subdirectory(pomelo) diff --git a/runtime/samples/fakeNN/CMakeLists.txt b/runtime/samples/fakeNN/CMakeLists.txt deleted file mode 100644 index 62530701..00000000 --- a/runtime/samples/fakeNN/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) -iree_turbine(SRC fakeNN.py DST ${CMAKE_CURRENT_BINARY_DIR}/fakeNN.mlirbc DTYPE "f64" M 2 N 120 K 40) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/fakeNN.mlirbc DST fakeNN FLAGS --mlir-disable-threading --iree-quidditch-time-disp=fakenn2x120x40 --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-myrtle=${MYRTLE_PATH}) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/fakeNN.mlirbc LLVM DST fakeNN_llvm FLAGS --iree-quidditch-time-disp=fakenn2x120x40 --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-myrtle=${MYRTLE_PATH}) -add_library(fakeNN_util fakeNN_util.c) -target_link_libraries(fakeNN_util - PRIVATE - samples_util - snRuntimeInterface - Quidditch::dispatch::dispatch - Quidditch::time_dispatch::time_dispatch -) -target_include_directories(fakeNN_util INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -macro(create_experiment_variant) - cmake_parse_arguments(_RULE "PRECOMMIT;NIGHTLY" "TARGET;IREE_MODULE;QUERY_FUNC" "" ${ARGN}) - - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c "\ -#include <${_RULE_IREE_MODULE}.h> - -#include \"fakeNN_util.h\" - -int main() { - return run_fakeNN_experiment(${_RULE_QUERY_FUNC}); -} -") - add_executable(${_RULE_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c) - target_link_libraries( - ${_RULE_TARGET} - PRIVATE - fakeNN_util - ${_RULE_IREE_MODULE} - snRuntime - ) -endmacro() - -create_experiment_variant( - TARGET FakeNN - IREE_MODULE fakeNN - QUERY_FUNC "quidditch_compiled_fake_n_n_linked_quidditch_library_query" -) -create_experiment_variant( - TARGET FakeNNLLVM - IREE_MODULE fakeNN_llvm - QUERY_FUNC "compiled_fake_n_n_linked_llvm_cpu_library_query" -) \ No newline at end of file diff --git a/runtime/samples/fakeNN/fakeNN.py b/runtime/samples/fakeNN/fakeNN.py deleted file mode 100644 index f6f84d90..00000000 --- a/runtime/samples/fakeNN/fakeNN.py +++ /dev/null @@ -1,157 +0,0 @@ -import argparse -import os -import random -import torch -import torch.nn as nn -from iree.turbine import aot -import numpy as np - -seed = 1234 -random.seed(seed) -torch.manual_seed(seed) -os.environ['PYTHONHASHSEED'] = str(seed) - -WIN_LEN = 0.02 -HOP_FRAC = 0.5 -FS = 16000 -MIN_GAIN = -80 - -n_fft = int(WIN_LEN * FS) -frame_shift = WIN_LEN * HOP_FRAC -n_hop = n_fft * HOP_FRAC -spec_size = n_fft // 2 + 1 - -parser = argparse.ArgumentParser(prog='iree-turbine') -parser.add_argument('output', nargs='?') -parser.add_argument('--frames', dest='frames', metavar='N', type=int, default=1, nargs='?') -parser.add_argument('--m', dest='mDim', metavar='Mdim', type=int, default=1, nargs='?') -parser.add_argument('--n', dest='nDim', metavar='Ndim', type=int, default=1, nargs='?') -parser.add_argument('--k', dest='kDim', metavar='Kdim', type=int, default=1, nargs='?') -parser.add_argument('--dtype', dest='dtype', metavar='F', choices=['f32', 'f64'], default='f32') -parser.add_argument('-dump', dest='dump', action='store_true', default=False) -args = parser.parse_args() - -name_to_dtype = { - 'f32': torch.float32, - 'f64': torch.float64, -} -dtype = name_to_dtype[args.dtype] - -# beware! the name of your nn.Module subclass matters! -# use camel case starting with a capital letter, and remember that given a name like FakeNN, -# cmake generates a static linked library for the module called -# compiled_fake_n_n_linked_llvm_cpu_library_query etc. -class FakeNN(nn.Module): - def __init__(self, n_features, hidden_1): - super().__init__() - self.n_features = n_features - self.hidden_1 = hidden_1 - # self.hidden_2 = hidden_2 - # self.hidden_3 = hidden_3 - # fc1 - self.fc1 = nn.Linear(n_features, hidden_1, dtype=dtype) - # # rnn - # self.rnn1 = nn.GRU(input_size=hidden_1, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) - # self.rnn2 = nn.GRU(input_size=hidden_2, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) - # # fc2 - # self.fc2 = nn.Linear(hidden_2, hidden_3, dtype=dtype) - # # fc3 - # self.fc3 = nn.Linear(hidden_3, hidden_3, dtype=dtype) - # # fc4 - # self.fc4 = nn.Linear(hidden_3, n_features, dtype=dtype) - # other - self.eps = 1e-9 - - def forward(self, stft_noisy, *state_in): - mask_pred, *state_out = self._forward(stft_noisy, *state_in) - return mask_pred, *state_out - - def _forward(self, stft_noisy, *state_in): - # print("we are inside the foward function") - x = self.fc1(stft_noisy) - # for some reason, the "compiled_fake_n_n_linked_llvm_cpu_library_query" inside FakeNNLLVM.h - # does not get generated unless I pass these two intermediate tensor states inside the forward function - # and modify them - # TODO: Since I'm not using the global intermediate states, find a way to get rid of them but still - # generate the "compiled_fake_n_n_linked_llvm_cpu_library_query" needed. - state_out = [*state_in] - sevens = torch.full(state_out[0].shape, 7,dtype=state_out[0].dtype)#from_numpy(np.full(state_out[0].shape, 7,state_out[0].dtype)) - state_out[0] = torch.add(state_out[0],sevens) - #result_tuple= model(torch.from_numpy(blah),state1,state2) - #y, state_out[0] = self.rnn1(x, state_in[0]) - # x, state_out[1] = self.rnn2(x, state_in[1]) - # x = self.fc2(x) - - # x = nn.functional.relu(x) - # x = self.fc3(x) - # x = nn.functional.relu(x) - # x = self.fc4(x) - # x = torch.sigmoid(x) - # sort shape - #mask_pred = x.permute(0, 2, 1).unsqueeze(1) - # print(f'inside the forward funcion, about to return{state_out}') - return x, *state_out - - -model = FakeNN(n_features=args.kDim, hidden_1=args.nDim) -#print(model) -model.train(False) - - -def with_frames(n_frames): - size = 1, n_frames, model.n_features - # beware! the name of your aot.CompiledModule subclass matters! - # use camel case starting with a capital letter, and remember that given a name like FakeNN, - # cmake generates a static linked library for the module called - # compiled_fake_n_n_linked_llvm_cpu_library_query etc. - class CompiledFakeNN(aot.CompiledModule): - # Make the hidden state globals that persist as long as the IREE session does. - state1 = aot.export_global(torch.zeros(1, 1, 2, dtype=dtype), mutable=True, uninitialized=False) - state2 = aot.export_global(torch.zeros(1, 1, 2, dtype=dtype), mutable=True, uninitialized=False) - def main(self, x=aot.AbstractTensor(*size, dtype=dtype)): - y, out1, out2 = aot.jittable(model.forward)( - x, self.state1, self.state2, - constraints=[] - ) - self.state1 = out1 - self.state2 = out2 - return y - - return CompiledFakeNN - -# I'm not sure what Markus meant by n_frames, but it appears -# that n_frames is equivalent to the M dimension (row dimension) of the input. -exported = aot.export(with_frames(n_frames=args.mDim)) -if args.dump: - exported.print_readable() -else: - exported.save_mlir(args.output) - # emily's notes below vvvvvvvvvvvvvvvvv - # np_dtype = np.float32 - # if dtype == torch.float64: - # np_dtype = np.float64 - - # blah = np.full((1,args.mDim, args.kDim), 7,dtype=np_dtype) - # state1 = torch.zeros(1, 1, 400, dtype=dtype) - # state2 = torch.zeros(1, 1, 400, dtype=dtype) - # result_tuple= model(torch.from_numpy(blah),state1,state2) - # print("YODEL") - # print(result_tuple[0]) - # print(result_tuple[0].shape) - - -# THE FOLLOWING CODE RUNS THE NsNet2 NN IN PYTHON!! - # np_dtype = np.float32 - # if dtype == torch.float64: - # np_dtype = np.float64 - # print("yodelaheyyyhoooooooo") - # blah = np.full((1,1, 161), 7,dtype=np_dtype) - # blah = np.array([[list([i+1 for i in range (0,161)])]],dtype=np_dtype) - # state1 = torch.zeros(1, 1, 400, dtype=dtype) - # state2 = torch.zeros(1, 1, 400, dtype=dtype) - # result_tuple= model(torch.from_numpy(blah),state1,state2) - # print("YODEL") - # print(result_tuple) -# https://docs.pytorch.org/docs/stable/generated/torch.nn.Linear.html -# https://docs.pytorch.org/tutorials/beginner/basics/buildmodel_tutorial.html -# https://docs.kanaries.net/topics/Python/nn-linear \ No newline at end of file diff --git a/runtime/samples/fakeNN/fakeNN_util.c b/runtime/samples/fakeNN/fakeNN_util.c deleted file mode 100644 index 1db0e5ca..00000000 --- a/runtime/samples/fakeNN/fakeNN_util.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "fakeNN_util.h" - -#include -#include - -#include - -#include -#include - -// ../toolchain/bin/snitch_cluster.vlt /home/hoppip/Quidditch/build/runtime/samples/fakeNN/fakeNN - -iree_status_t compiled_fake_n_n_create(iree_vm_instance_t *, iree_allocator_t, - iree_vm_module_t **); - -// copied from Quidditch/snitch_cluster/sw/snRuntime/src/riscv.h -inline uint32_t snrt_mcycle() { - uint32_t register r; - asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); - return r; -} - -extern unsigned long myrtle_actual_cycles[5][2]; - - -int run_fakeNN_experiment( - iree_hal_executable_library_query_fn_t implementation) { - if (!snrt_is_dm_core()) return quidditch_dispatch_enter_worker_loop(); - - double(*inputData)[mDim*kDim] = aligned_alloc(64, (mDim*kDim) * sizeof(double)); - double(*outputData)[mDim*nDim] = aligned_alloc(64, (mDim*nDim) * sizeof(double)); - - // initialize input data - for (int i = 0; i < IREE_ARRAYSIZE(*inputData); i++) { - (*inputData)[i] = (i + 1); - } - - // initialize output data to all zeros - for (int i = 0; i < IREE_ARRAYSIZE(*outputData); i++) { - (*outputData)[i] = 0; - } - - model_config_t config = { - .libraries = (iree_hal_executable_library_query_fn_t[]){implementation}, - .num_libraries = 1, - .module_constructor = compiled_fake_n_n_create, - .main_function = iree_make_cstring_view("compiled_fake_n_n.main"), - - .element_type = IREE_HAL_ELEMENT_TYPE_FLOAT_64, - - .num_inputs = 1, - .input_data = (const void *[]){inputData, inputData}, - .input_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*inputData)}, - .input_ranks = (const iree_host_size_t[]){3}, - .input_shapes = (const iree_hal_dim_t *[]){(iree_hal_dim_t[]){1, mDim, kDim}}, - - .num_outputs = 1, - .output_data = (void *[]){outputData}, - .output_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*outputData)}, - }; - - unsigned long cycles = snrt_mcycle(); - IREE_CHECK_OK(run_model(&config)); - unsigned long cycles_after = snrt_mcycle(); - - for (int i = 0; i < IREE_ARRAYSIZE(*outputData); i++) { - double value = (*outputData)[i]; - printf("%f\n", value); - } - - int i = 0; - printf("dispatch 0: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - printf("\ncycles %lu\n", cycles_after - cycles); - free(inputData); - free(outputData); - return 0; -} diff --git a/runtime/samples/fakeNN/fakeNN_util.h b/runtime/samples/fakeNN/fakeNN_util.h deleted file mode 100644 index 6ccb1d65..00000000 --- a/runtime/samples/fakeNN/fakeNN_util.h +++ /dev/null @@ -1,11 +0,0 @@ - -#pragma once - -#include - -int run_fakeNN_experiment( - iree_hal_executable_library_query_fn_t implementation); - -#define mDim 2 -#define nDim 120 -#define kDim 40 \ No newline at end of file diff --git a/runtime/samples/fakeNN/requirements.txt b/runtime/samples/fakeNN/requirements.txt deleted file mode 100644 index 586f84b7..00000000 --- a/runtime/samples/fakeNN/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -torch==2.3.0 -iree-turbine==2.3.0 diff --git a/runtime/samples/fakeNN/ts-answer.json b/runtime/samples/fakeNN/ts-answer.json deleted file mode 100644 index dca3137c..00000000 --- a/runtime/samples/fakeNN/ts-answer.json +++ /dev/null @@ -1 +0,0 @@ -{"main$async_dispatch_0_matmul_transpose_b_2x120x40_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [120], [20]], "dual-buffer": true}} \ No newline at end of file diff --git a/runtime/samples/grapeFruit/CMakeLists.txt b/runtime/samples/grapeFruit/CMakeLists.txt deleted file mode 100644 index 04a32a81..00000000 --- a/runtime/samples/grapeFruit/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) -iree_turbine(SRC grapeFruit.py DST ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DTYPE "f64" M 5 N 6 K 7) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc DST grapeFruit FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/grapeFruit.mlirbc LLVM DST grapeFruit_llvm FLAGS --mlir-disable-threading --iree-quidditch-time-disp=grapeFruit --iree-quidditch-myrtle=${MYRTLE_PATH} --iree-quidditch-myrtle-mode=svrcyc --iree-quidditch-myrtle-out=${CMAKE_CURRENT_LIST_DIR}/ts-answer.json --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/disp-7-0-600-8.json) -add_library(grapeFruit_util grapeFruit_util.c) -target_link_libraries(grapeFruit_util - PRIVATE - samples_util - snRuntimeInterface - Quidditch::dispatch::dispatch - Quidditch::time_dispatch::time_dispatch -) -target_include_directories(grapeFruit_util INTERFACE ${CMAKE_CURRENT_LIST_DIR}) - -macro(create_experiment_variant) - cmake_parse_arguments(_RULE "PRECOMMIT;NIGHTLY" "TARGET;IREE_MODULE;QUERY_FUNC" "" ${ARGN}) - - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c "\ -#include <${_RULE_IREE_MODULE}.h> - -#include \"grapeFruit_util.h\" - -int main() { - return run_grapeFruit_experiment(${_RULE_QUERY_FUNC}); -} -") - add_executable(${_RULE_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${_RULE_TARGET}.c) - target_link_libraries( - ${_RULE_TARGET} - PRIVATE - grapeFruit_util - ${_RULE_IREE_MODULE} - snRuntime - ) -endmacro() - -create_experiment_variant( - TARGET GrapeFruit - IREE_MODULE grapeFruit - QUERY_FUNC "quidditch_compiled_ns_net2_linked_quidditch_library_query" -) -create_experiment_variant( - TARGET GrapeFruitLLVM - IREE_MODULE grapeFruit_llvm - QUERY_FUNC "compiled_ns_net2_linked_llvm_cpu_library_query" -) \ No newline at end of file diff --git a/runtime/samples/grapeFruit/disp-9-bandaid.json b/runtime/samples/grapeFruit/disp-9-bandaid.json deleted file mode 100644 index 0ad3ec7b..00000000 --- a/runtime/samples/grapeFruit/disp-9-bandaid.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64": { - "loop-order": [ - [ - 2, - 0 - ], - [ - 0, - 0 - ], - [ - 1, - 0 - ] - ], - "tile-sizes": [ - [ - 1 - ], - [ - 56 - ], - [ - 100 - ] - ], - "dual-buffer": true - } -} \ No newline at end of file diff --git a/runtime/samples/grapeFruit/grapeFruit.py b/runtime/samples/grapeFruit/grapeFruit.py deleted file mode 100644 index 64bf5c46..00000000 --- a/runtime/samples/grapeFruit/grapeFruit.py +++ /dev/null @@ -1,110 +0,0 @@ -import argparse -import os -import random -import torch -import torch.nn as nn -from iree.turbine import aot - -seed = 1234 -random.seed(seed) -torch.manual_seed(seed) -os.environ['PYTHONHASHSEED'] = str(seed) - -WIN_LEN = 0.02 -HOP_FRAC = 0.5 -FS = 16000 -MIN_GAIN = -80 - -n_fft = int(WIN_LEN * FS) -frame_shift = WIN_LEN * HOP_FRAC -n_hop = n_fft * HOP_FRAC -spec_size = n_fft // 2 + 1 - -parser = argparse.ArgumentParser(prog='iree-turbine') -parser.add_argument('output', nargs='?') -parser.add_argument('--frames', dest='frames', metavar='N', type=int, default=1, nargs='?') -parser.add_argument('--m', dest='mDim', metavar='Mdim', type=int, default=1, nargs='?') -parser.add_argument('--n', dest='nDim', metavar='Ndim', type=int, default=1, nargs='?') -parser.add_argument('--k', dest='kDim', metavar='Kdim', type=int, default=1, nargs='?') -parser.add_argument('--dtype', dest='dtype', metavar='F', choices=['f32', 'f64'], default='f32') -parser.add_argument('-dump', dest='dump', action='store_true', default=False) -args = parser.parse_args() - -name_to_dtype = { - 'f32': torch.float32, - 'f64': torch.float64, -} -dtype = name_to_dtype[args.dtype] - - -class NsNet2(nn.Module): - def __init__(self, n_features, hidden_1, hidden_2, hidden_3): - super().__init__() - self.n_features = n_features - self.hidden_1 = hidden_1 - self.hidden_2 = hidden_2 - self.hidden_3 = hidden_3 - # fc1 - self.fc1 = nn.Linear(n_features, hidden_1, dtype=dtype) - # rnn - self.rnn1 = nn.GRU(input_size=hidden_1, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) - self.rnn2 = nn.GRU(input_size=hidden_2, hidden_size=hidden_2, num_layers=1, batch_first=True, dtype=dtype) - # fc2 - self.fc2 = nn.Linear(hidden_2, hidden_3, dtype=dtype) - # fc3 - self.fc3 = nn.Linear(hidden_3, hidden_3, dtype=dtype) - # fc4 - self.fc4 = nn.Linear(hidden_3, n_features, dtype=dtype) - # other - self.eps = 1e-9 - - def forward(self, stft_noisy, *state_in): - mask_pred, *state_out = self._forward(stft_noisy, *state_in) - return mask_pred, *state_out - - def _forward(self, stft_noisy, *state_in): - x = self.fc1(stft_noisy) - state_out = [*state_in] - x, state_out[0] = self.rnn1(x, state_in[0]) - x, state_out[1] = self.rnn2(x, state_in[1]) - x = self.fc2(x) - x = nn.functional.relu(x) - x = self.fc3(x) - x = nn.functional.relu(x) - x = self.fc4(x) - x = torch.sigmoid(x) - # sort shape - mask_pred = x.permute(0, 2, 1).unsqueeze(1) - return mask_pred, *state_out - - -model = NsNet2(n_features=spec_size, hidden_1=400, hidden_2=400, hidden_3=600) -model.train(False) - - -def with_frames(n_frames): - size = 1, n_frames, model.n_features - - class CompiledNsNet2(aot.CompiledModule): - # Make the hidden state globals that persist as long as the IREE session does. - state1 = aot.export_global(torch.zeros(1, 1, 400, dtype=dtype), mutable=True, uninitialized=False) - state2 = aot.export_global(torch.zeros(1, 1, 400, dtype=dtype), mutable=True, uninitialized=False) - - def main(self, x=aot.AbstractTensor(*size, dtype=dtype)): - y, out1, out2 = aot.jittable(model.forward)( - x, self.state1, self.state2, - constraints=[] - ) - self.state1 = out1 - self.state2 = out2 - return y - - return CompiledNsNet2 - - -exported = aot.export(with_frames(n_frames=args.frames)) -if args.dump: - exported.print_readable() -else: - exported.save_mlir(args.output) - # exported.save_mlir("/home/hoppip/Quidditch/runtime/samples/grapeFruit/theMLIR.mlir") diff --git a/runtime/samples/grapeFruit/grapeFruit_util.c b/runtime/samples/grapeFruit/grapeFruit_util.c deleted file mode 100644 index 01ab1399..00000000 --- a/runtime/samples/grapeFruit/grapeFruit_util.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "grapeFruit_util.h" - -#include -#include - -#include - -#include -#include - - -iree_status_t compiled_ns_net2_create(iree_vm_instance_t *, iree_allocator_t, - iree_vm_module_t **); - -// copied from Quidditch/snitch_cluster/sw/snRuntime/src/riscv.h -inline uint32_t snrt_mcycle() { - uint32_t register r; - asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); - return r; -} - -extern unsigned long myrtle_actual_cycles[5][2]; - - -int run_grapeFruit_experiment( - iree_hal_executable_library_query_fn_t implementation) { - if (!snrt_is_dm_core()) return quidditch_dispatch_enter_worker_loop(); - - double(*data)[161] = aligned_alloc(64, 161 * sizeof(double)); - - for (int i = 0; i < IREE_ARRAYSIZE(*data); i++) { - (*data)[i] = (i + 1); - } - - model_config_t config = { - .libraries = (iree_hal_executable_library_query_fn_t[]){implementation}, - .num_libraries = 1, - .module_constructor = compiled_ns_net2_create, - .main_function = iree_make_cstring_view("compiled_ns_net2.main"), - - .element_type = IREE_HAL_ELEMENT_TYPE_FLOAT_64, - - .num_inputs = 1, - .input_data = (const void *[]){data, data}, - .input_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*data)}, - .input_ranks = (const iree_host_size_t[]){3}, - .input_shapes = (const iree_hal_dim_t *[]){(iree_hal_dim_t[]){1, 1, 161}}, - - .num_outputs = 1, - .output_data = (void *[]){data}, - .output_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*data)}, - }; - - unsigned long cycles = snrt_mcycle(); - IREE_CHECK_OK(run_model(&config)); - unsigned long cycles_after = snrt_mcycle(); - - // for (int i = 0; i < IREE_ARRAYSIZE(*data); i++) { - // double value = (*data)[i]; - // printf("%f\n", value); - // } - - // for(int i = 0; i < 5; i ++){ - // printf("%d: %lu - %lu = %lu\n", i, myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - // } - int i = 0; - printf("dispatch 9: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - i = 1; - printf("dispatch 0: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - i = 2; - printf("dispatch 7: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - i = 3; - printf("dispatch 8: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - i = 4; - printf("dispatch 1: %lu - %lu = %lu\n", myrtle_actual_cycles[i][1],myrtle_actual_cycles[i][0],myrtle_actual_cycles[i][1]-myrtle_actual_cycles[i][0]); - - - printf("\ncycles %lu\n", cycles_after - cycles); - free(data); - return 0; -} diff --git a/runtime/samples/grapeFruit/grapeFruit_util.h b/runtime/samples/grapeFruit/grapeFruit_util.h deleted file mode 100644 index 5b02ece4..00000000 --- a/runtime/samples/grapeFruit/grapeFruit_util.h +++ /dev/null @@ -1,7 +0,0 @@ - -#pragma once - -#include - -int run_grapeFruit_experiment( - iree_hal_executable_library_query_fn_t implementation); diff --git a/runtime/samples/grapeFruit/requirements.txt b/runtime/samples/grapeFruit/requirements.txt deleted file mode 100644 index 586f84b7..00000000 --- a/runtime/samples/grapeFruit/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -torch==2.3.0 -iree-turbine==2.3.0 diff --git a/runtime/samples/grapeFruit/ts-answer.json b/runtime/samples/grapeFruit/ts-answer.json deleted file mode 100644 index 54389ed1..00000000 --- a/runtime/samples/grapeFruit/ts-answer.json +++ /dev/null @@ -1 +0,0 @@ -{"main$async_dispatch_0_matmul_transpose_b_1x400x161_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [200], [23]], "dual-buffer": true}, "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [48], [80]], "dual-buffer": true}, "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [40], [100]], "dual-buffer": true}, "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [40], [120]], "dual-buffer": true}, "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [144], [40]], "dual-buffer": true}} \ No newline at end of file diff --git a/runtime/samples/nsnet2/0-40-100.json b/runtime/samples/nsnet2/0-40-100.json deleted file mode 100644 index 04d4decf..00000000 --- a/runtime/samples/nsnet2/0-40-100.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64" : { - "tile-sizes":[[0], [40], [100]], - "loop-order":[[2,0], [0,0], [1,0]], - "dual-buffer": true - }, - "main$async_dispatch_0_matmul_transpose_b_1x400x161_f64" : { - "tile-sizes":[[0],[40],[0]], - "loop-order":[[2,0], [0,0], [1,0]], - "dual-buffer": false - }, - "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64" : { - "tile-sizes":[[0], [40], [100]], - "loop-order":[[2,0], [0,0], [1,0]], - "dual-buffer": true - }, - "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64" : { - "tile-sizes":[[0], [40], [100]], - "loop-order":[[2,0], [0,0], [1,0]], - "dual-buffer": true - }, - "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64" : { - "tile-sizes":[[0], [56], [100]], - "loop-order":[[2,0], [0,0], [1,0]], - "dual-buffer": true - } -} diff --git a/runtime/samples/nsnet2/CMakeLists.txt b/runtime/samples/nsnet2/CMakeLists.txt index d66097ef..1e205ee9 100644 --- a/runtime/samples/nsnet2/CMakeLists.txt +++ b/runtime/samples/nsnet2/CMakeLists.txt @@ -1,8 +1,8 @@ set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) iree_turbine(SRC NsNet2.py DST ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DTYPE "f64" M 5 N 6 K 7) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DST nsnet2 FLAGS --mlir-disable-threading --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/0-40-100.json) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc LLVM DST nsnet2_llvm FLAGS --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/0-40-100.json) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DST nsnet2 FLAGS --mlir-disable-threading --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/manually-chosen-tiles.json) +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc LLVM DST nsnet2_llvm FLAGS --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/manually-chosen-tiles.json) add_library(nsnet2_util nsnet2_util.c) target_link_libraries(nsnet2_util diff --git a/comparing-tile-sizes/old-tiling-schemes/0-40-100.json b/runtime/samples/nsnet2/manually-chosen-tiles.json similarity index 100% rename from comparing-tile-sizes/old-tiling-schemes/0-40-100.json rename to runtime/samples/nsnet2/manually-chosen-tiles.json diff --git a/runtime/snitch_cluster/CMakeLists.txt b/runtime/snitch_cluster/CMakeLists.txt index 06ea70c6..fcce6b2b 100644 --- a/runtime/snitch_cluster/CMakeLists.txt +++ b/runtime/snitch_cluster/CMakeLists.txt @@ -58,7 +58,6 @@ target_include_directories(snRuntimeInterface ${snRuntimeSrc}/sw/deps/riscv-opcodes ${CMAKE_CURRENT_LIST_DIR}/api ) -#/home/hoppip/Quidditch/snitch_cluster/target/snitch_cluster/sw/runtime/rtl/src/snrt.h RADDISH # Required while snRuntime uses 'inline' qualifiers for declarations. target_compile_options(snRuntimeInterface INTERFACE -Wno-undefined-inline) diff --git a/runtime/tests/CMakeLists.txt b/runtime/tests/CMakeLists.txt index ca3b8ec4..7704a11d 100644 --- a/runtime/tests/CMakeLists.txt +++ b/runtime/tests/CMakeLists.txt @@ -82,10 +82,6 @@ endmacro() test_executable(HelloWorld PRECOMMIT) test_executable(vec_multiply PRECOMMIT) -test_executable(caqui PRECOMMIT) -test_executable(pomelo PRECOMMIT) -test_executable(calabaza PRECOMMIT) -test_executable(GrapeFruit NIGHTLY) test_executable(big_matvec NIGHTLY) test_executable(NsNet2 NIGHTLY) test_executable(NsNet2LLVM NIGHTLY) From b01e1976b0e7f9e5986089812a91f252c6f1f020 Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Wed, 23 Jul 2025 12:50:11 +0200 Subject: [PATCH 4/8] remove myrtle submodule --- myrtle/.gitignore | 4 - myrtle/LICENSE | 201 ------- .../{README.md => myrtle.egg-info/PKG-INFO} | 16 + myrtle/myrtle.egg-info/SOURCES.txt | 19 + myrtle/myrtle.egg-info/dependency_links.txt | 1 + myrtle/myrtle.egg-info/requires.txt | 7 + myrtle/myrtle.egg-info/top_level.txt | 1 + myrtle/myrtle/__init__.py | 0 myrtle/myrtle/graphing/__init__.py | 0 myrtle/myrtle/graphing/context2.png | Bin 71949 -> 0 bytes myrtle/myrtle/graphing/graph_utils.py | 519 ------------------ myrtle/myrtle/graphing/graphing-refactored.py | 257 --------- myrtle/myrtle/myrtle.py | 115 ---- myrtle/myrtle/tile_SA/__init__.py | 0 .../myrtle/tile_SA/quidditch_load_counting.py | 160 ------ myrtle/myrtle/tile_SA/utils.py | 54 -- myrtle/myrtle/tile_gen/__init__.py | 0 .../tile_gen/peek_at_snitch_assembly.py | 229 -------- myrtle/myrtle/tile_gen/tile_size_generator.py | 246 --------- myrtle/myrtle/tile_sel/README.md | 3 - myrtle/myrtle/tile_sel/dispatch-8-svr.pickle | Bin 2775 -> 0 bytes myrtle/myrtle/tile_sel/linesOfBestFit.pickle | Bin 4660 -> 0 bytes myrtle/pyproject.toml | 20 - myrtle/requirements.txt | 5 - .../holistic-data/1x1200x400wm-n-k-timed.csv | 48 -- .../holistic-data/1x400x161wm-n-k-timed.csv | 4 - .../holistic-data/1x600x400wm-n-k-timed.csv | 32 -- .../holistic-data/1x600x600wm-n-k-timed.csv | 48 -- .../dispatch_1_case1_everything.csv | 18 - .../dispatch_1_case2_everything.csv | 42 -- .../dispatch_7_case1_everything.csv | 15 - .../dispatch_7_case2_everything.csv | 34 -- .../dispatch_8_case1_everything.csv | 31 -- .../dispatch_8_case2_everything.csv | 35 -- .../pivoted.cost_model_30_thru_201.csv | 316 ----------- myrtle/test_output.json | 1 - 36 files changed, 44 insertions(+), 2437 deletions(-) delete mode 100644 myrtle/.gitignore delete mode 100644 myrtle/LICENSE rename myrtle/{README.md => myrtle.egg-info/PKG-INFO} (63%) create mode 100644 myrtle/myrtle.egg-info/SOURCES.txt create mode 100644 myrtle/myrtle.egg-info/dependency_links.txt create mode 100644 myrtle/myrtle.egg-info/requires.txt create mode 100644 myrtle/myrtle.egg-info/top_level.txt delete mode 100644 myrtle/myrtle/__init__.py delete mode 100644 myrtle/myrtle/graphing/__init__.py delete mode 100644 myrtle/myrtle/graphing/context2.png delete mode 100644 myrtle/myrtle/graphing/graph_utils.py delete mode 100644 myrtle/myrtle/graphing/graphing-refactored.py delete mode 100644 myrtle/myrtle/myrtle.py delete mode 100644 myrtle/myrtle/tile_SA/__init__.py delete mode 100644 myrtle/myrtle/tile_SA/quidditch_load_counting.py delete mode 100644 myrtle/myrtle/tile_SA/utils.py delete mode 100644 myrtle/myrtle/tile_gen/__init__.py delete mode 100644 myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py delete mode 100644 myrtle/myrtle/tile_gen/tile_size_generator.py delete mode 100644 myrtle/myrtle/tile_sel/README.md delete mode 100644 myrtle/myrtle/tile_sel/dispatch-8-svr.pickle delete mode 100644 myrtle/myrtle/tile_sel/linesOfBestFit.pickle delete mode 100644 myrtle/pyproject.toml delete mode 100644 myrtle/requirements.txt delete mode 100644 myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv delete mode 100644 myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv delete mode 100644 myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv delete mode 100644 myrtle/test_output.json diff --git a/myrtle/.gitignore b/myrtle/.gitignore deleted file mode 100644 index 7c14388b..00000000 --- a/myrtle/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -**/__pycache__ -myrtle.egg-info/* -uv.lock -1x600x400wm-n-k_case1_searchSpace.csv \ No newline at end of file diff --git a/myrtle/LICENSE b/myrtle/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/myrtle/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/myrtle/README.md b/myrtle/myrtle.egg-info/PKG-INFO similarity index 63% rename from myrtle/README.md rename to myrtle/myrtle.egg-info/PKG-INFO index 6e3c9458..76a87e79 100644 --- a/myrtle/README.md +++ b/myrtle/myrtle.egg-info/PKG-INFO @@ -1,3 +1,19 @@ +Metadata-Version: 2.4 +Name: myrtle +Version: 0.1.0 +Summary: Tiling Cost Model for Snitch +Requires-Python: ==3.11.* +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: numpy==2.3.1 +Requires-Dist: pandas==2.2.2 +Requires-Dist: scikit-learn==1.7.0 +Requires-Dist: matplotlib>=3.10.1 +Requires-Dist: pyqt6==6.9.1 +Requires-Dist: pyqt6-qt6==6.9.1 +Requires-Dist: pyqt6-sip==13.10.2 +Dynamic: license-file + # myrtle tiling cost model for the snitch cluster! diff --git a/myrtle/myrtle.egg-info/SOURCES.txt b/myrtle/myrtle.egg-info/SOURCES.txt new file mode 100644 index 00000000..3f3ebec3 --- /dev/null +++ b/myrtle/myrtle.egg-info/SOURCES.txt @@ -0,0 +1,19 @@ +LICENSE +README.md +pyproject.toml +myrtle/__init__.py +myrtle/myrtle.py +myrtle.egg-info/PKG-INFO +myrtle.egg-info/SOURCES.txt +myrtle.egg-info/dependency_links.txt +myrtle.egg-info/requires.txt +myrtle.egg-info/top_level.txt +myrtle/graphing/__init__.py +myrtle/graphing/graph_utils.py +myrtle/graphing/graphing-refactored.py +myrtle/tile_SA/__init__.py +myrtle/tile_SA/quidditch_load_counting.py +myrtle/tile_SA/utils.py +myrtle/tile_gen/__init__.py +myrtle/tile_gen/peek_at_snitch_assembly.py +myrtle/tile_gen/tile_size_generator.py \ No newline at end of file diff --git a/myrtle/myrtle.egg-info/dependency_links.txt b/myrtle/myrtle.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/myrtle/myrtle.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/myrtle/myrtle.egg-info/requires.txt b/myrtle/myrtle.egg-info/requires.txt new file mode 100644 index 00000000..13b56eef --- /dev/null +++ b/myrtle/myrtle.egg-info/requires.txt @@ -0,0 +1,7 @@ +numpy==2.3.1 +pandas==2.2.2 +scikit-learn==1.7.0 +matplotlib>=3.10.1 +pyqt6==6.9.1 +pyqt6-qt6==6.9.1 +pyqt6-sip==13.10.2 diff --git a/myrtle/myrtle.egg-info/top_level.txt b/myrtle/myrtle.egg-info/top_level.txt new file mode 100644 index 00000000..73847a0a --- /dev/null +++ b/myrtle/myrtle.egg-info/top_level.txt @@ -0,0 +1 @@ +myrtle diff --git a/myrtle/myrtle/__init__.py b/myrtle/myrtle/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/myrtle/myrtle/graphing/__init__.py b/myrtle/myrtle/graphing/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/myrtle/myrtle/graphing/context2.png b/myrtle/myrtle/graphing/context2.png deleted file mode 100644 index 0c0b400cb555963b67a77e772db394ae9c8c7735..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71949 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�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 ranked.shape[0]): - q = ranked.shape[0] - top = getBestXFrom(ranked, "rank", q, True) - cm = CustomMarker(marker=lambda x: f'${x["rank"]}$', label=lambda y: y["JSON Name"]) - return Graph2D( - keys=Keys2D( - x=x, - x_label=x, - x_unit=x_unit, - y=y, - y_label=y, - y_unit=y_unit, - ), - title=dispatchTitle, - scatterSets=[(top, cm)], - legend=True, - legend_pos="upper right", - ) - - -def getComparisonOfActualTimeVsEstimated( - dfs, x, x_unit, y, y_unit, z, lowIsGoodZ, dispatchNo, dispatchTitle, q, c -): - # rankings - ranked = rankBy(dfs, (dispatchNo, 1), z, lowIsGoodZ) - print(ranked) - - # if Q is invalid, graph ALL the rows instead. - if (q <= 0) or (q > ranked.shape[0]): - q = ranked.shape[0] - - top = getBestXFrom(ranked, "rank", q, True) - cm = CustomMarker( - marker=lambda x: f'${x["rank"]}$', - label=lambda y: y["JSON Name"], - stroke=lambda y: c, - ) - - return Graph2D( - keys=Keys2D( - x=x, - x_label=x, - x_unit=x_unit, - y=y, - y_label=y, - y_unit=y_unit, - ), - title=dispatchTitle, - scatterSets=[ - (top, cm) # , (top2, cm2) - ], - legend=False, - legend_pos="upper right", - ) - - -def frog(dfs, dispatchNo, dispatchTitle): - # dfs,(dispatchNo,1) - ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time", True) - est_ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time Estimate", True) - cm2 = CustomMarker( - marker=lambda x: "o", - label=lambda y: y["JSON Name"], - fill=lambda y: "YellowGreen", - stroke=lambda y: "Black", - ) - cm1 = CustomMarker(fill=lambda y: "Purple") - return Graph2D( - keys=Keys2D( - x="Row Dim", - x_label="Row Dim", - x_unit="8 byte elts", - y="Kernel Time", - y_label="Kernel Time", - y_unit="8 byte elts", - ), - title=dispatchTitle, - scatterSets=[(ranked, cm1)], - legend=True, - legend_pos="upper right", - ) - - -def toad(dfs, dispatchNo, dispatchTitle): - # dfs,(dispatchNo,1) - ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time", True) - est_ranked = rankBy(dfs, (dispatchNo, 1), "Kernel Time Estimate", True) - cm2 = CustomMarker( - marker=lambda x: "o", - label=lambda y: y["JSON Name"], - fill=lambda y: "YellowGreen", - stroke=lambda y: "Black", - ) - cm1 = CustomMarker(fill=lambda y: "Purple") - return Graph2D( - keys=Keys2D( - x="Row Dim", - x_label="Row Dim", - x_unit="8 byte elts", - y="Kernel Time", - y_label="Kernel Time", - y_unit="8 byte elts", - ), - title=dispatchTitle, - scatterSets=[(est_ranked, cm2)], - legend=True, - legend_pos="upper right", - ) - - -def dimsVsQuidditchTime(dfs, dispNo, dispTitle): - ranked = rankBy(dfs, (dispNo, 1), "Kernel Time", True) - best = getBestXFrom(ranked, "Kernel Time", 10, True) - a = Graph2D( - keys=Keys2D( - x="Reduction Dim", - x_label="Reduction Dim", - x_unit="elements", - y="Kernel Time", - y_label="Kernel Time", - y_unit="cycles", - ), - title=dispTitle, - scatterSets=[ - ( - best, - CustomMarker( - label=lambda y: f'({y["Row Dim"]},{y["Reduction Dim"]})', - size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2) * 2, - marker=lambda x: f'${x["rank"]}$', - ), - ) - ], - legend=True, - legend_pos="upper left", - legend_bb=(1,1) - ) - return a - - -# def rankBy(dfs, id, by, lowIsGood): -# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) -# df_sorted["rank"] = range(1, df_sorted.shape[0] + 1) -# return df_sorted -label: Callable[[T], str] = lambda y="no label": "_no label" - - -# def rankBy(dfs, id, by, lowIsGood): -# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) -# df_sorted["rank"] = range(1, df_sorted.shape[0] + 1) -# return df_sorted - - -def dimsVsEstimatedTime(dfs, dispNo, dispTitle): - ranked = rankBy(dfs, (dispNo, 1), "Kernel Time Estimate", True) - best = getBestXFrom(ranked, "Kernel Time Estimate", 10, True) - b = Graph2D( - keys=Keys2D( - x="Reduction Dim", - x_label="Reduction Dim", - x_unit="elements", - y="Kernel Time Estimate", - y_label="Kernel Time Estimate", - y_unit="cycles", - ), - title=dispTitle, - scatterSets=[ - ( - best, - CustomMarker( - label= lambda y: f'({y["Row Dim"]},{y["Reduction Dim"]})', - marker=lambda x: f'${x["rank"]}$', - size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2) * 2, - stroke=lambda x: "Purple", - ), - ) - ], - legend=True, - legend_pos="upper left", - legend_bb=(1,1) - ) - return b - - -def main(): - titles = [ - "Dispatch 1\nmatvec: <1x400>, <1200x400> -> <1x1200>", - "Dispatch 8\nmatvec: <1x600>, <600x600> -> <1x600>", - "Dispatch 7\nmatvec: <1x400>, <600x400> -> <1x600>", - ] - # graphs = [] - # dfs = shortcutToData("../estimated_cycles_out") - # top10 = {} - # for dispNo, dispTitle in zip([1, 8, 7], titles): - # quid = dimsVsQuidditchTime(dfs, dispNo, dispTitle) - # est = dimsVsEstimatedTime(dfs, dispNo, dispTitle) - # graphs.append(quid) - # graphs.append(est) - # top10[dispNo] = (quid,est) - # #graphEmAll((3, 2), graphs) - - # g = top10[7] - # graphEmAll((1,2), (g[0],g[1])) - # data = g[0].scatterSets[0][0] - # print(data) - # top5=getBestXFrom(data, "Kernel Time", 5, True) - # print(top5) - # print("hooodle") - # for i in range(0,5): - # better = top5.iloc[i]["Kernel Time"] - # print(top5.iloc[i]["JSON Name"],end=" compared to\n") - # for j in range(i+1,5): - # worse = top5.iloc[j]["Kernel Time"] - # percentage = ((worse + 0.0) - (better + 0.0) )/ (worse + 0.0) * 100.0 - # print("\t",end=f'{top5.iloc[j]["JSON Name"]} is {percentage:.2f} % better\n') - - -if __name__ == "__main__": - main() diff --git a/myrtle/myrtle/graphing/graphing-refactored.py b/myrtle/myrtle/graphing/graphing-refactored.py deleted file mode 100644 index 15c6d33f..00000000 --- a/myrtle/myrtle/graphing/graphing-refactored.py +++ /dev/null @@ -1,257 +0,0 @@ -from mpl_toolkits.mplot3d.axes3d import Axes3D -from mpl_toolkits.mplot3d import proj3d -import sys -import pandas as pd -from graph_utils import graphEmAll, loadDFsDispatchCaseNo, deriveMoreData,addSVMPrediction,trimToTopX, Graph2D, Keys2D, CustomMarker, MySVM -import matplotlib as mpl -import matplotlib.pyplot as plt -from itertools import product, islice -from PIL import Image -import numpy as np -from sklearn.inspection import DecisionBoundaryDisplay -from sklearn.svm import SVC, SVR - -# example run: python graphing-refactored.py "/home/hoppip/myrtle/estimated_cycles_no_overhead" -# example run: python graphing-refactored.py /home/emily/myrtle/estimated_cycles_overhead -def main(): - args = sys.argv[1:] - if len(args) != 1: - print("USAGE: python3 graphing-refactored.py ") - print("\twhere contains static characteristics, \n\testimated time based on microkernels, and actual time") - exit(1) - print("HOLA") - caseNos = [1] # We only graph case 1; no padding anywhere. - dispatchOrder = [8,7,1] # All dispatches will be graphed in the order 8, 7, 1. - titles = { - 8:"Dispatch 8\nmatvec: <1x600>, <600x600> -> <1x600>", - 7:"Dispatch 7\nmatvec: <1x400>, <600x400> -> <1x600>", - 1:"Dispatch 1\nmatvec: <1x400>, <1200x400> -> <1x1200>", - } - print(f"Reading data from {args[0]}...") - # load dispatch data - dfs = loadDFsDispatchCaseNo(args[0], dispatchOrder, caseNos) - # derive more data about each dispatch - deriveMoreData(dfs,dispatchOrder,caseNos) - # train some SVMs to predict runtime - tunedOverhead = tryToFindOverHeadConstant(dfs,8,1,"tryToTuneOverheadCoef") - generalSVM = tryGeneralSVM(dfs,8,1,"tryGeneralSVM") - addSVMPrediction(dfs,dispatchOrder,caseNos,tunedOverhead) - addSVMPrediction(dfs,dispatchOrder,caseNos,generalSVM) - - # graph ALL data points - # Quidditch Time (FLOP/cycle) vs Attributes - keysFLOPs = Keys2D( - x="rank", - x_label="Rank", - x_unit="fastest to slowest", - y="FLOP Per Cycle", - y_label="Throughput", - y_unit="FLOPs per cycle", - ) - lookForTrendsDispatchCase(dfs,dispatchOrder,caseNos,titles,keysFLOPs,"hoodle","LFT-dispatches-rank-x-axis.png") - keysFLOPs.x="Kernel Time" - keysFLOPs.x_label="Kernel Time" - keysFLOPs.x_unit="cycles" - lookForTrendsDispatchCase(dfs,dispatchOrder,caseNos,titles,keysFLOPs,"hoodle","LFT-dispatches-kernel-time-x-axis.png") - # Actual Quidditch Time (cycles) VS Predicted Quidditch time (cycles) - keysActualVsReal = Keys2D( - x="rank", - x_label="Rank", - x_unit="fastest to slowest", - y="Kernel Time", - y_label="Kernel Time", - y_unit="cycles", - ) - actualVsPredictedTimeDispatchCase(dfs,dispatchOrder,caseNos,titles,"Kernel Time","Kernel Time Estimate",keysActualVsReal,"Estimate with Microkernel Time * Microkernel Runs",'ActualVsEstimate-3-dispatches-rank-x-axis.png') - actualVsPredictedTimeDispatchCase(dfs,dispatchOrder,caseNos,titles,"Kernel Time",tunedOverhead.predName,keysActualVsReal,"Estimate with SVR-tuned overhead constant",'SVR-overhead-estimate-3-dispatches.png') - actualVsPredictedTimeDispatchCase(dfs,dispatchOrder,caseNos,titles,"Kernel Time",generalSVM.predName,keysActualVsReal,"Estimate with general SVR ",'SVR-general-estimate-3-dispatches.png') - - # graph TOP TEN data points - dfs = trimToTopX(dfs, dispatchOrder, caseNos, "rank", 10) # destructive!! - lookForTrendsDispatchCase(dfs,dispatchOrder,caseNos,titles,keysFLOPs,"hoodle","LFT-dispatches-kernel-time-x-axis-top-10.png") - print("HASTA LUEGO") - -def tryToFindOverHeadConstant(dfs, dispNo, caseNo,predName="tryToTuneOverheadCoef"): - ranked = dfs[(dispNo,caseNo)] - feature_names = ["Kernel Time Estimate","UnrollAndJam Outer Loops"] - target_name = "Kernel Time" - train = ranked[feature_names] - X = np.array(train) - y = np.array(ranked[target_name]) - # Build the model - svm = SVR(kernel="linear", gamma=0.5, C=1.0) - # Train the model - svm.fit(X, y) - return MySVM(svm=svm,predName=predName,featureNames=feature_names) - -def tryGeneralSVM(dfs, dispNo, caseNo,predName="tryGeneralSVM"): - ranked = dfs[(dispNo,caseNo)] - feature_names = ["Microkernel Count","Regular Loads","Reused Streaming Loads","Space Needed in L1","Row Dim","Reduction Dim", "UnrollAndJam Factor"] - target_name = "Kernel Time" - train = ranked[feature_names] - X = np.array(train) - y = np.array(ranked[target_name]) - # Build the model - svm = SVR(kernel="linear", gamma=0.5, C=1.0) - # Train the model - svm.fit(X, y) - return MySVM(svm=svm,predName=predName,featureNames=feature_names) - -def combineDispatchesWithContext(graphs,img_title,img_name): - graphImages = [] - width = 0.0 - biggest = None - # find graph with max width and use this to set the width of the canvas - for g in graphs: - gImg = Image.open(f'{g.imagePath}.png') - if gImg.size[0] > width: - width = gImg.size[0] - biggest = gImg - graphImages.append(gImg) - # resize context header to fit canvas - orig = Image.open('context2.png') # hard coded - resized = orig.copy() - resized.thumbnail(biggest.size, Image.Resampling.LANCZOS) - resized.save("resized.png") - # place each of the three dispatches on the canvas - top = Image.open('resized.png') - g0 = graphImages[0] - g1 = graphImages[1] - g2 = graphImages[2] - canvas = Image.new('RGBA', (biggest.size[0], top.size[1]+g0.size[1]+g1.size[1]+g2.size[1]), (255, 255, 255, 255)) - canvas.paste(top, (0, 0), top) - canvas.paste(g0, (0, top.size[1]), g0) - canvas.paste(g1, (0, top.size[1]+g0.size[1]), g1) - canvas.paste(g2, (0, top.size[1]+g0.size[1]+g1.size[1]), g2) - canvas.save(img_name) - top.close() - g0.close() - g1.close() - g2.close() - -def lookForTrendsDispatchCase(dfs, dispatchNos, caseNos, titles, keys=Keys2D( - x="rank", - x_label="Rank", - x_unit="fastest to slowest", - y="FLOP Per Cycle", - y_label="Throughput", - y_unit="FLOPs per cycle", - ),imgTitle="Looking for Trends",imgName='LFT-3-dispatches.png',): - graphs = [] - for dispNo in dispatchNos: - for caseNo in caseNos: - title = titles[dispNo] - lftGraph = lookForTrends(dfs, dispNo, caseNo, title,keys) - graphs.append(lftGraph) - graphEmAll((1, 1), [lftGraph]) - combineDispatchesWithContext(graphs,imgTitle,imgName) - -def actualVsPredictedTimeDispatchCase(dfs, dispatchNos, caseNos, titles,y1="Kernel Time",y2="Kernel Time Estimate",keys=Keys2D( - x="rank", - x_label="Rank", - x_unit="fastest to slowest", - y="Kernel Time", - y_label="Kernel Time", - y_unit="cycles", - ),imgTitle="Actual vs Predicted Time",imgName='ActualVsEstimate-3-dispatches.png'): - graphs = [] - for dispNo in dispatchNos: - for caseNo in caseNos: - title = titles[dispNo] - lftGraph = actualVsPredictedTime(dfs, dispNo, caseNo, title,y1,y2,keys) - graphs.append(lftGraph) - graphEmAll((1, 1), [lftGraph]) - combineDispatchesWithContext(graphs,imgTitle,imgName) - - -def actualVsPredictedTime(dfs, dispNo, caseNo, title, y1="Kernel Time",y2="Kernel Time Estimate",keys=Keys2D( - x="rank", - x_label="Rank", - x_unit="fastest to slowest", - y="Kernel Time", - y_label="Kernel Time", - y_unit="cycles", - )): - tableData = dfs[(dispNo,caseNo)][["rankAsStr","Microkernel Row Dim","UnrollAndJam Factor","UnrollAndJam Outer Loops","Microkernel Count","Row Dim","Reduction Dim"]] - colLabels = ["rank","n'","U&J Factor","CC Outer Loops","Micro Runs","n","k"] - defW = (1/(len(colLabels)*3)) # default width - tableColWidths = [defW,defW,defW*1.5,defW*1.5,defW*1.25,defW*0.5,defW*0.5]#[defW]*len(colLabels) - return Graph2D( - imagePath=f'dispatch-{dispNo}-case-{1}', - keys=keys, - title=title, - scatterSets=[ - ( - dfs[(dispNo,caseNo)], - CustomMarker( - y=y1, - label= lambda y: f' {y["JSON Name"]}', - marker=lambda x: f'${x["rank"]}$', - size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2)*2, - stroke=lambda x: 'Black', - fill=lambda x: 'Black' - ), - ), - ( - dfs[(dispNo,caseNo)], - CustomMarker( - y=y2, - label= lambda y: f' {y["JSON Name"]}', - marker=lambda x: f'${x["rank"]}$', - size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2)*2, - stroke=lambda x: "Purple", - fill=lambda x: "Purple", - ), - ) - ], - legend = False, - table = True, - table_pos="right", - table_bb=(1.01,0,1,1), - table_col_widths = tableColWidths, - table_col_labels=colLabels, - table_row_labels=[], - table_data=tableData - ) - -def lookForTrends(dfs, dispNo, caseNo, dispTitle, keys=Keys2D( - x="rank", - x_label="Rank", - x_unit="fastest to slowest", - y="FLOP Per Cycle", - y_label="Throughput", - y_unit="FLOPs per cycle", - )): - tableData = dfs[(dispNo,caseNo)][["rankAsStr","Microkernel Row Dim","UnrollAndJam Factor","UnrollAndJam Outer Loops","Microkernel Count","Regular Loads","Reused Streaming Loads","Space Needed in L1","Row Dim","Reduction Dim"]] - colLabels = ["rank","n'","U&J","CC OLs","Micro Runs","RLs","Reused SLs","L1 Usage","n","k"] - defW = (1/(len(colLabels)*3)) # default width - tableColWidths = [defW*0.6,defW*0.5,defW*0.4,defW,defW*1.25,defW,defW*1.5,defW*1.6,defW*0.5,defW*0.5] - return Graph2D( - imagePath=f'LFT-dispatch-{dispNo}-case-{caseNo}', - keys=keys, - title=dispTitle, - scatterSets=[ - ( - dfs[(dispNo,caseNo)], - CustomMarker( - y="FLOP Per Cycle", - label= lambda y: f' {y["JSON Name"]}', - marker=lambda x: f'${x["rank"]}$', - size=lambda y=0: (mpl.rcParams["lines.markersize"] ** 2)*2, - stroke=lambda x: 'Black', - fill=lambda x: 'Black' - ), - ) - ], - legend = False, - table = True, - table_pos="right", - table_bb=(1.01,0,1,1), - table_col_widths = tableColWidths, - table_col_labels=colLabels, - table_row_labels=[], - table_data=tableData - ) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/myrtle/myrtle/myrtle.py b/myrtle/myrtle/myrtle.py deleted file mode 100644 index 9326f7ae..00000000 --- a/myrtle/myrtle/myrtle.py +++ /dev/null @@ -1,115 +0,0 @@ -import sys -import json -import tile_gen.tile_size_generator as tsg -import re -import pickle -import pandas as pd -import sklearn.svm -from graphing.graph_utils import Curve -import os - - -# def rankBy(dfs, id, by, lowIsGood): -# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) -# df_sorted["rank"] = range(1, int(df_sorted.shape[0] + 1)) -# return df_sorted - -# def getBestX(dfs, id, by, x, lowIsGood): -# df_sorted = dfs[id].sort_values(by=by, ascending=lowIsGood) -# df_best_5 = df_sorted.iloc[range(0, x)] -# return df_best_5 - -def get_simple_cycle_estimate(timeEstimateFuncs, row_dim, col_dim, outerLoopIters, microCount): #, n, k): - if outerLoopIters == 1: - return timeEstimateFuncs[row_dim](col_dim) * microCount - else: # for ex, microkernel tile of 10 x 50 will have - # outer loop iters = unroll and jam outer loops = 2 - # unroll and jam factor of 5 - # so select function that estimates execution of microkernel - # with row dimension 10 / 2 = 5, and multiply that by outer loops - # TODO: ALSO, ADD A CONSTANT TO ACCOUNT FOR OVERHEAD OF SETTING UP STREAMING REGISTERS - return timeEstimateFuncs[row_dim/outerLoopIters](col_dim)*microCount #+ outerLoopIters*100 - -def tileSelection(csvFile, mode): - df = pd.read_csv(csvFile) - myLoc=os.path.abspath(__file__)[:-(len("myrtle.py"))] - if mode == "svrcyc": - file = open(f'{myLoc}/tile_sel/dispatch-8-svr.pickle', 'rb') - svr=pickle.load(file) - df["Predicted Kernel Time"] = df.apply(lambda y: svr.predict([y[["Microkernel Count","Regular Loads","Reused Streaming Loads","Space Needed in L1","Row Dim","Reduction Dim"]]])[0], axis=1) - ranked = df.sort_values("Predicted Kernel Time", ascending=True) - df = ranked - else: - if mode == "scyc": - linearApproxFilePath = f'{myLoc}/tile_sel/linesOfBestFit.pickle' - file = open(linearApproxFilePath, 'rb') - curves = pickle.load(file) - lines = {} - for c in curves: - lines[c.id]=c.func - df["Kernel Time Estimate"] = df.apply(lambda x: get_simple_cycle_estimate(lines,x["Microkernel Row Dim"], x["Microkernel Reduction Dim"],x["Outer Loop Iters"],x["Microkernel Count"]), axis=1) - ranked = df.sort_values("Kernel Time Estimate", ascending=True) - df = ranked - else: - # minimize microkernel runs - df_sorted = df.sort_values("Microkernel Count", ascending=True) - df_sorted = df_sorted.iloc[range(0, len(df_sorted)//3)] - # maximize space used in L1 - df_sorted = df_sorted.sort_values("Space Needed in L1", ascending=False) - df_sorted = df_sorted.iloc[range(0, len(df_sorted)//2)] - # minimize regular loads - final_ranking = df_sorted.sort_values("Regular Loads", ascending=False) - df = final_ranking - print(f'df is {df}') - m = 1 #TODO: expand tiling to matmul!! - n = int(df.iloc[0]["Row Dim"]) - k = int(df.iloc[0]["Reduction Dim"]) - dualBuffer = True - return (m,n,k,dualBuffer) - -# arg 1 is dispatchName as a string -# arg 2 is tile selection mode -# arg 3 is file to write tile scheme to -def main(): - # print("myrtle: ",end='') - dispatchName = sys.argv[1] - # print (dispatchName) - # for e in sys.argv: - # print(f'arg is {e}.') - dispatchRegex=re.compile(r'main\$async_dispatch_\d+_matmul_transpose_b_(\d+)x(\d+)x(\d+)_f64') - M,N,K = dispatchRegex.search(dispatchName).groups() - # query myrtle! - # generate options - jen = tsg.TileSizeGenerator(int(N),int(K),dispatchName) - options = jen.validOptions() - print("myrtle : ",end='') - jen.exportOptionsToCSV(f'{M}x{N}x{K}wm-n-k', 1, options) - m,n,k,dualBuffer = tileSelection(f'{M}x{N}x{K}wm-n-k_case1_searchSpace.csv',sys.argv[2]) - if sys.argv[2] == "sflt": - print("myrtle : ",end='') - print("We used simple filtering to select tiles.") - if sys.argv[2] == "scyc": - print("myrtle : ",end='') - print("We used a simple cycle estimation to select tiles.") - if sys.argv[2] == "svrcyc": - print("myrtle : ",end='') - print("We used an SVR to select tiles.") - # default values - with open(sys.argv[3], 'r') as file: - data = json.load(file) - node = {} - node["loop-order"] = [[2,0], [0,0], [1,0]] - - # set node values and export to JSON - node["tile-sizes"] = [[m], [n], [k]] - node["dual-buffer"] = dualBuffer - data[dispatchName]=node - f = open(sys.argv[3], "w") - f.write(f"{json.dumps(data)}") - f.close() - - - -if __name__ == "__main__": - main() - diff --git a/myrtle/myrtle/tile_SA/__init__.py b/myrtle/myrtle/tile_SA/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/myrtle/myrtle/tile_SA/quidditch_load_counting.py b/myrtle/myrtle/tile_SA/quidditch_load_counting.py deleted file mode 100644 index 7124b210..00000000 --- a/myrtle/myrtle/tile_SA/quidditch_load_counting.py +++ /dev/null @@ -1,160 +0,0 @@ -from tile_SA.utils import roundUpToNearestMultipleOf, InputMatrix, TileSizes, HardwareLoop, unrollAndJamFactor, EnclosingSCFLoop, unrollAndJamOuterLoops -#from myrtle.peek_at_snitch_assembly import peek_at_lowered_matvec_tiling - - -def getLogicalSizeAfterPadding(mat: InputMatrix, sizes: TileSizes): - logicalSize = InputMatrix(n=mat.n, k=mat.k) - if mat.n % sizes.n != 0: - logicalSize.n = roundUpToNearestMultipleOf(mat.n, sizes.n) - if mat.k % sizes.k != 0: - logicalSize.k = roundUpToNearestMultipleOf(mat.k, sizes.k) - return logicalSize - - -"""Given a tile of size sz, what is the subtile size when there are 8 subtiles?""" - - -def coreTile(sz: int): - if sz % 8 != 0: - raise Exception(f"tile size MUST be divisible by 8, yet I have {sz}!") - else: - return sz // 8 - -# return total number of times microkernel runs to complete execution of linalg kernel -def getMicroKernelCount(mat: InputMatrix, sizes: TileSizes): - k_count = mat.k // sizes.k # tile the k dimension once - n_count = mat.n // sizes.n # tile the n dimension once - # tile the n dimension again (for each computer core) - micro_n_sz = coreTile(sizes.n) - per_cluster_count = sizes.n // micro_n_sz - assert per_cluster_count == 8 - micro_k_sz = mat.k // k_count - cluster_tile = InputMatrix(n=sizes.n, k=micro_k_sz) - microkernel_tile = InputMatrix(n=micro_n_sz, k=micro_k_sz) - microkernel_count = k_count * n_count * per_cluster_count - # ONLY FOR DEBUGGING VV - # print(f"sizes is {sizes}") - # print(f'per_cluster_count is {per_cluster_count}') - # print(f"microkernel tile: {microkernel_tile}") - # print(f"cluster tile: {cluster_tile}") - # print(f"k_count: {k_count}. n_count: {n_count}. per_cluster_count: {per_cluster_count}") - # print(f"how many microkernel tiles are there, then?") - # print(f"{k_count*n_count*per_cluster_count} because {k_count*n_count*per_cluster_count*micro_k_sz*micro_n_sz} = {mat.k * mat.n}") - # print(f"each core processes (k_count * n_count) = {k_count*n_count} of the {k_count*n_count*per_cluster_count} tiles, because {k_count*n_count*per_cluster_count}/{8}={k_count*n_count*per_cluster_count/8}") - # ^^ ONLY FOR DEBUGGING - # reality check - left = k_count*n_count*per_cluster_count*micro_k_sz*micro_n_sz - right = mat.k * mat.n - if left != right: - raise Exception(f'{left} should = {right}') - return (microkernel_count, microkernel_tile, cluster_tile) - - -def yodel(): - print("yodelayheehooooo~~~~~~!") - - -def LoadCountingAnnColumnNames(): - columns = [ - "Regular Loads", - "Total Streaming Loads", - "Other Streaming Loads", - "Start Reuse Streaming Loads", - "Reused Streaming Loads", - "Outer Loop Iters", - "HW Loop Body", - "HW Loop Iters", - "Microkernel Count", - "Microkernel Row Dim", - "Microkernel Reduction Dim", - ] - return columns - - - -# @dataclass -# class HardwareLoop: -# """Class for keeping track of hardware loop characteristics""" -# name: str = "frepOuter"#FrepOuter.name -# loop_repeats: int = 1 # number of times loop executes -# body_size: int = 1 # number of instructions in body of the loop -# #unrollAndJamFactor - -# @dataclass -# class EnclosingSCFLoop: -# """Class for keeping track of a potential loop surrounding the hardware""" -# name: str = "an enclosing loop" -# iters : int = 1 # number of times the enclosing loop executes -# exists : bool = False # whether the hardware loop is in fact enclosed by another loop - -def simulate_peek_at_lowered_matvec_tiling(matvec: InputMatrix): - # for potential_factor in range(1, self.pipeline_depth * 2): - expectedFMADDs = matvec.n * matvec.k - # create linalg, then lower to assembly - #linalg_mod, asm_mod, m, n, k = createMatmulTransposeB(1, matvec.n, matvec.k) - m = 1 - if m == 0: - m = 1 - k = matvec.k - n = matvec.n // 8 - if (n) * 8 != matvec.n: - raise Exception("Sorry, only row dimensions divisible by 8 allowed!") - - # look for a hardware loop - hLoop=HardwareLoop(loop_repeats=k,body_size=unrollAndJamFactor(n)) - # look for an enclosing loop - oLoopIters = unrollAndJamOuterLoops(n) - oLoop=EnclosingSCFLoop(iters=oLoopIters,exists=not(oLoopIters==1)) - res = expectedFMADDs == ( - (hLoop.body_size * hLoop.loop_repeats) * oLoop.iters * 8 - ) - if not oLoop.exists: - res = expectedFMADDs == (n * k * 8) - return (res, hLoop, oLoop) - -# matrix-vector transpose with type `, -> ` where `M = 1` -def getLoadCountingAnn(mat: InputMatrix, sizes: TileSizes): - logicalInput = getLogicalSizeAfterPadding(mat, sizes) - # logicalCount = the number of times a core-sized tile gets processed = (8 * outer L1 tiling loops) - # this number could be different than the number of microkernel runs per core, - # because if unroll and jam is performed, a microkernel must run more than once to process a single core-sized tile. - logicalCount, microkernel_tile, cluster_tile = getMicroKernelCount(logicalInput, sizes) - left = microkernel_tile.n * microkernel_tile.k * logicalCount - right = logicalInput.n * logicalInput.k - # reality check - if left != right: - raise Exception(f'after getMicroKernelCount: {left} should = {right}') - res, hLoop, oLoop = simulate_peek_at_lowered_matvec_tiling(cluster_tile) - if not res: - raise Exception("Lowering to snitch hardware loop failed!") - # outer_loop_iters is equivalent to the number of micro-kernel runs needed to process one core-sized tile - # if unroll and jam was performed, this value > 1. - outer_loop_iters = oLoop.iters if oLoop.exists else 1 - # loads during micro kernel execution(s) per core - regular_loads_per_core = outer_loop_iters*hLoop.body_size - other_streaming_loads_per_core = outer_loop_iters*(hLoop.body_size)*hLoop.loop_repeats - total_streaming_loads_per_core = outer_loop_iters*(hLoop.body_size*2)*hLoop.loop_repeats - start_reuse_streaming_loads_per_core = outer_loop_iters*(1)*hLoop.loop_repeats - reused_streaming_loads_per_core = outer_loop_iters*(hLoop.body_size-1)*hLoop.loop_repeats - assert total_streaming_loads_per_core == (start_reuse_streaming_loads_per_core+reused_streaming_loads_per_core+other_streaming_loads_per_core) - # per cluster - regular_loads = regular_loads_per_core * logicalCount - other_streaming_loads = other_streaming_loads_per_core * logicalCount - total_streaming_loads = total_streaming_loads_per_core * logicalCount - start_reuse_streaming_loads = start_reuse_streaming_loads_per_core * logicalCount - reused_streaming_loads = reused_streaming_loads_per_core * logicalCount - return(regular_loads,total_streaming_loads,other_streaming_loads,start_reuse_streaming_loads,reused_streaming_loads,outer_loop_iters, hLoop.body_size, hLoop.loop_repeats, logicalCount,microkernel_tile.n ,microkernel_tile.k) - -def main(): - input = InputMatrix(n=1200, k=400) - tiles = TileSizes(n=56, k=100) - print() - print(f"input: {input}") - print(f"tiles: {tiles}") - print() - res = getLoadCountingAnn(input, tiles) - print(res) - - -if __name__ == "__main__": - main() diff --git a/myrtle/myrtle/tile_SA/utils.py b/myrtle/myrtle/tile_SA/utils.py deleted file mode 100644 index 0cb36bde..00000000 --- a/myrtle/myrtle/tile_SA/utils.py +++ /dev/null @@ -1,54 +0,0 @@ -from dataclasses import dataclass -#from xdsl.dialects.riscv_snitch import FrepOuter - -def roundUpToNearestMultipleOf(num, row_dim): - remainder = num % row_dim - if (remainder != 0): - return (num//row_dim) * row_dim + row_dim - else: - return num - -@dataclass -class InputMatrix: - """Class for keeping track of matrix dimensions in""" - """matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul)""" - n: int = 1200 - k: int = 400 - -@dataclass -class TileSizes: - """Class for keeping track of matrix-vector transpose tiling in each dimension;""" - """matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul)""" - m: int = 1 - n : int = 40 - k : int = 100 - -@dataclass -class HardwareLoop: - """Class for keeping track of hardware loop characteristics""" - name: str = "frepOuter"#FrepOuter.name - loop_repeats: int = 1 # number of times loop executes - body_size: int = 1 # number of instructions in body of the loop - -@dataclass -class EnclosingSCFLoop: - """Class for keeping track of a potential loop surrounding the hardware""" - name: str = "an enclosing loop" - iters : int = 1 # number of times the enclosing loop executes - exists : bool = False # whether the hardware loop is in fact enclosed by another loop - -def unrollAndJamFactor(rowDim): - options = [7,6,5,4,3,2] - factor = 1 - for option in options: - if rowDim % option == 0: - factor = option - break - return factor - -def unrollAndJamOuterLoops(rowDim): - # print(f'outer loops is {rowDim} / {unrollAndJamFactor(rowDim)} which is {rowDim / unrollAndJamFactor(rowDim)}') - if unrollAndJamFactor(rowDim) != 1: - return int(rowDim / unrollAndJamFactor(rowDim)) - else: - return 1 \ No newline at end of file diff --git a/myrtle/myrtle/tile_gen/__init__.py b/myrtle/myrtle/tile_gen/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py b/myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py deleted file mode 100644 index 85f84592..00000000 --- a/myrtle/myrtle/tile_gen/peek_at_snitch_assembly.py +++ /dev/null @@ -1,229 +0,0 @@ -from xdsl.backend.riscv.lowering import ( - convert_arith_to_riscv, - convert_func_to_riscv_func, - convert_memref_to_riscv, - convert_scf_to_riscv_scf, -) -from xdsl.backend.riscv.lowering.convert_riscv_scf_to_riscv_cf import ( - ConvertRiscvScfToRiscvCfPass, -) -from xdsl.backend.riscv.lowering.convert_snitch_stream_to_snitch import ( - ConvertSnitchStreamToSnitch, -) -from xdsl.builder import ImplicitBuilder -from xdsl.context import MLContext -from xdsl.dialects import arith, func, linalg -from xdsl.dialects.builtin import AffineMap, AffineMapAttr, MemRefType, ModuleOp, f64 -from xdsl.dialects.riscv import riscv_code -#from xdsl.interpreters.utils.ptr import TypedPtr -from xdsl.ir import Attribute, Block, Region, SSAValue -from xdsl.passes import PipelinePass -from xdsl.tools.command_line_tool import get_all_dialects -from xdsl.transforms import ( - arith_add_fastmath, - convert_linalg_to_loops, - convert_linalg_to_memref_stream, - convert_memref_stream_to_loops, - convert_memref_stream_to_snitch_stream, - convert_riscv_scf_for_to_frep, - dead_code_elimination, - loop_hoist_memref, - lower_affine, - memref_streamify, - reconcile_unrealized_casts, -) -from xdsl.transforms.canonicalize import CanonicalizePass -from xdsl.transforms.lower_snitch import LowerSnitchPass -from xdsl.transforms.mlir_opt import MLIROptPass -from xdsl.transforms.riscv_register_allocation import RISCVRegisterAllocation -from xdsl.transforms.riscv_scf_loop_range_folding import ( - RiscvScfLoopRangeFoldingPass, -) -from xdsl.transforms.snitch_register_allocation import SnitchRegisterAllocation -from collections import Counter -from dataclasses import dataclass, field -from xdsl.transforms.test_lower_snitch_stream_to_asm import ( - TEST_LOWER_SNITCH_STREAM_TO_ASM_PASSES, -) -from xdsl.transforms.test_lower_memref_stream_to_snitch_stream import ( - TEST_LOWER_MEMREF_STREAM_TO_SNITCH_STREAM -) -from xdsl.transforms.test_optimise_memref_stream import ( - TEST_OPTIMISE_MEMREF_STREAM -) -from io import StringIO -from xdsl.dialects.riscv import RISCVAsmOperation, RsRsOffIntegerOperation, MulOp, LiOp -from xdsl.dialects.riscv_cf import ConditionalBranchOperation -from xdsl.dialects.riscv_func import FuncOp -from xdsl.dialects.riscv_snitch import FrepOuter -from xdsl.passes import ModulePass -from myrtle.utils import InputMatrix, HardwareLoop, EnclosingSCFLoop - - -def createMatmulTransposeB(m=0, outputVectorEltCount=40, inputVectorEltCount=100): - if m == 0: - m = 1 - k = inputVectorEltCount - n = outputVectorEltCount // 8 - if (n) * 8 != outputVectorEltCount: - raise Exception("Sorry, only row dimensions divisible by 8 allowed!") - ctx = MLContext() - for dialect_name, dialect_factory in get_all_dialects().items(): - ctx.register_dialect(dialect_name, dialect_factory) - a_shape2 = (m, k) - b_shape2 = (n, k) - c_shape2 = (m, n) - # Let's try to recreate the parsed matmultranspose_b using an xDSL IR builder... - a_type2 = MemRefType(f64, a_shape2) - b_type2 = MemRefType(f64, b_shape2) - c_type2 = MemRefType(f64, c_shape2) - kernel_op2 = func.FuncOp("matmul_transpose_b", ((a_type2, b_type2, c_type2), ())) - with ImplicitBuilder(kernel_op2.body) as (a2, b2, c2): - # Add name hints to make it easier to track how values are lowered - a2.name_hint = "A" - b2.name_hint = "B" - c2.name_hint = "C" - body2 = Region(Block(arg_types=(f64, f64, f64))) - linalg.Generic( - inputs=(a2, b2), - outputs=(c2,), - body=body2, - indexing_maps=( - AffineMapAttr(AffineMap.from_callable(lambda m, n, k: (m, k))), - AffineMapAttr(AffineMap.from_callable(lambda m, n, k: (n, k))), - AffineMapAttr(AffineMap.from_callable(lambda m, n, k: (m, n))), - ), - iterator_types=( - linalg.IteratorTypeAttr.parallel(), - linalg.IteratorTypeAttr.parallel(), - linalg.IteratorTypeAttr.reduction(), - ), - ) - with ImplicitBuilder(body2) as (a_val2, b_val2, acc_old_val2): - prod_val2 = arith.Mulf(a_val2, b_val2).result - acc_new_val2 = arith.Addf(acc_old_val2, prod_val2).result - linalg.YieldOp(acc_new_val2) - # Add more name hints to make it easier to track how values are lowered - a_val2.name_hint = "a" - b_val2.name_hint = "b" - acc_old_val2.name_hint = "acc_old" - prod_val2.name_hint = "prod" - acc_new_val2.name_hint = "acc_new" - func.Return() - - linalg_module = ModuleOp((kernel_op2,)) - my_linalg_to_snitch = PipelinePass( - [ - convert_linalg_to_memref_stream.ConvertLinalgToMemrefStreamPass(), - arith_add_fastmath.AddArithFastMathFlagsPass(), - *TEST_OPTIMISE_MEMREF_STREAM, - *TEST_LOWER_MEMREF_STREAM_TO_SNITCH_STREAM, - convert_riscv_scf_for_to_frep.ConvertRiscvScfForToFrepPass(), - *TEST_LOWER_SNITCH_STREAM_TO_ASM_PASSES, - ] - ) - - my_asm_module = run_thru_all_passes( - my_linalg_to_snitch.passes, - linalg_module, - ctx, - ) - - return (linalg_module, my_asm_module, m, n, k) - - -def look_for_frep(module: ModuleOp, output=StringIO()) -> (list, str): - freps = [] - frepsAsHLs = [] - for op in module.body.walk(): - assert isinstance(op, RISCVAsmOperation) or (isinstance(op, FuncOp)), f"{op}" - if op.name == FrepOuter.name: - freps.append(op) - for op in freps: - asm = op.assembly_line() - if asm is not None: - print(asm, file=output) - if "immediate" in op.max_rep.op.attributes: - imm = op.max_rep.op.attributes["immediate"].value.data - print(f"this frep was passed an immediate value of {imm}", file=output) - frepsAsHLs.append( - HardwareLoop(loop_repeats=imm + 1, body_size=op.max_inst) - ) - else: - print(f"ERROR: op.max_rep.op: {op.max_rep.op}", file=output) - print(f"max_inst is {op.max_inst}", file=output) - if len(freps) == 0: - print(f"ERROR no frep found in lowering!!", file=output) - return (frepsAsHLs, output.getvalue()) - - -def look_for_enclosing_loop( - module: ModuleOp, output=StringIO() -) -> (EnclosingSCFLoop, str): - mulsBefore = [] - mulsAfter = [] - sawFrep = False - frepCount = 0 - branches = [] - for op in module.body.walk(): - assert isinstance(op, RISCVAsmOperation) or (isinstance(op, FuncOp)), f"{op}" - if op.name == FrepOuter.name: - frepCount = frepCount + 1 - if isinstance(op, ConditionalBranchOperation): - branches.append(op) - if len(branches) > 1: - print(f"ERROR: more than one branch found!!", file=output) - if frepCount == 0: - print(f"ERROR no frep found in lowering!!", file=output) - if frepCount > 1: - print(f"ERROR more than one frep found in lowering!!", file=output) - if len(branches) == 0: - return (EnclosingSCFLoop(exists=False), output.getvalue()) - limb = branches[0] - print(f"rs1: {limb.rs1}, rs2: {limb.rs2}", file=output) - print(f"rs1.op: {limb.rs1.op}, rs2.op: {limb.rs2.op}", file=output) - if limb.rs2.op.name != LiOp.name: - return (EnclosingSCFLoop(exists=False), output.getvalue()) - imm = limb.rs2.op.attributes["immediate"].value.data - return (EnclosingSCFLoop(limb.name, iters=imm, exists=True), output.getvalue()) - - -def peek_at_lowered_matvec_tiling(matvec: InputMatrix): - expectedFMADDs = matvec.n * matvec.k - # create linalg, then lower to assembly - linalg_mod, asm_mod, m, n, k = createMatmulTransposeB(1, matvec.n, matvec.k) - # look for a hardware loop - loops, commentary = look_for_frep(asm_mod) - if len(loops) != 1: - raise Exception( - f"ERROR: expected only ONE hardware loop, but found {len(loops)} {commentary}" - ) - # look for an enclosing loop - oLoop, commentary2 = look_for_enclosing_loop(asm_mod) - # print(riscv_code(asm_mod)) # DEBUGGING ONLY - # check the number of FMADDs is as expected - res = expectedFMADDs == ( - (loops[0].body_size * loops[0].loop_repeats) * oLoop.iters * 8 - ) - if not oLoop.exists: - res = expectedFMADDs == (n * k * 8) - return (res, loops[0], oLoop) - - -def run_thru_all_passes( - passes: tuple[ModulePass, ...], module: ModuleOp, ctx: MLContext -) -> ModuleOp: - res = module.clone() - for p in passes: - p.apply(ctx, res) - return res - - -def main(): - print("\n|----------- HOODLE ----------|", end="\n") - print(check_lowered_matvec_tiling(0, 40, 100)) - print(check_lowered_matvec_tiling(0, 64, 80)) - - -if __name__ == "__main__": - main() diff --git a/myrtle/myrtle/tile_gen/tile_size_generator.py b/myrtle/myrtle/tile_gen/tile_size_generator.py deleted file mode 100644 index e63500f3..00000000 --- a/myrtle/myrtle/tile_gen/tile_size_generator.py +++ /dev/null @@ -1,246 +0,0 @@ -from dataclasses import dataclass, field -import pandas as pd -import sys -from itertools import product -import tile_SA.quidditch_load_counting as qlc -from tile_SA.utils import InputMatrix, TileSizes, roundUpToNearestMultipleOf -# @dataclass -# class InputMatrix: -# """Class for keeping track of matrix dimensions in""" -# """matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul)""" -# n: int = 1200 -# k: int = 400 - - -class TileSizeGenerator: - def __init__(self, outputVectorEltCount, inputVectorEltCount, dispatchName=""): - self.me = InputMatrix(n=outputVectorEltCount, k=inputVectorEltCount) - self.dispatchName = dispatchName - - def dividesIntoN(self, num): - return self.me.n % num == 0 - - def dividesIntoK(self, num): - return self.me.k % num == 0 - - def myfunc(self): - print(f"I am {self.me}") - self.validOptions() - - def rowDimOptions(self): - hardware_loop_body_options = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - byEight = list(map(lambda x: 8 * x, hardware_loop_body_options)) - max = self.me.n - min = byEight[0] - exhaustive = list(range(min, max+1, 8)) - return exhaustive - - def reductionDimOptions(self): - max = self.me.k - min = 8 - exhaustive = list(range(min, max + 1)) - if (self.me.k % 2) != 0: - print(f"WARNING: K = {self.me.k} is NOT divisible by 2!") - return exhaustive - - def paddedNDimOptions(self): - hardware_loop_body_options = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - byEight = list(map(lambda x: 8 * x, hardware_loop_body_options)) - max = roundUpToNearestMultipleOf(self.me.n, 8) # "pad" to nearest multiple of 8 - min = byEight[0] - exhaustive = list(range(min, max+1, 8)) - return exhaustive - - def paddedKDimOptions(self): - step = 1 - if self.me.k < 40: - return [self.me.k] - else: # we want about 40 tile sizes to pick from - step = self.me.k // 40 - max = self.me.k - min = 8 - exhaustive = list(range(min, max + 1,step)) - if (self.me.k % 2) != 0: - print(f"WARNING: K = {self.me.k} is NOT divisible by 2!") - - return exhaustive - - def validOptions(self): - # all possible values for n and k - little_n_options = self.rowDimOptions() - little_k_options = self.reductionDimOptions() - # print(f'n options are {list(little_n_options)}') - # print(f'k options are {list(little_k_options)}') - # filter for n's and k's that divide evenly into N and K - little_n_no_pad = list(filter(lambda x: self.dividesIntoN(x), little_n_options)) - if len(little_n_no_pad) <= 1: # prime N dimension, or not divisible by 8 - n_options = self.paddedNDimOptions() - else: - n_options = little_n_no_pad - little_k_no_pad = list(filter(lambda x: self.dividesIntoK(x), little_k_options)) - if len(little_k_no_pad) == 1: # prime K dimension - k_options = self.paddedKDimOptions() - else: - k_options = little_k_no_pad - # have k dim options for double buffering - k_options = list( - filter(lambda x: x <= (self.me.k // 2) + 1, k_options)) - # print(f'now n options are {list(n_options)}') - # print(f'now k options are {list(k_options)}') - options_as_pairs = list(product(n_options, k_options)) - annotated_options = list(map(lambda tup: self.annotateOption(tup), options_as_pairs)) - # filter by size - valid_options = list( - filter(lambda tup: self.smallEnough(tup[0][0], tup[0][1]), annotated_options) - ) - # print(valid_options) - return valid_options - # filter by size - # sizeInfo = list(map(lambda tup: jen.annotateOption(tup), valid_options)) - # # add load counting information - # sizeAndLoadInfo = jen.exportOptionsToCSV(dispatchName, caseNo, sizeInfo) - # extras = jen.addMoreColsForConvenience(sizeAndLoadInfo) - # return(extras) - # return list(product(little_n_no_pad, little_k_no_pad)) - #return list(product(little_n_no_pad, k_dim_halve_options_for_double_buffering)) - - # print("litlle n no padding: [", end="") - # for i in little_n_no_pad: - # print(i, end=", ") - # print("]") - # print("little k no padding:[", end="") - # for i in little_k_no_pad: - # print(i, end=", ") - # print("]") - # # return (little_n_no_pad,little_k_no_pad) - # return list(product(little_n_no_pad, little_k_no_pad)) - - def weightMatTileSize(self, row_dim, reduction_dim): - return row_dim * reduction_dim - - def spaceForTiles(self, row_dim, reduction_dim): - # space in element count - inputVectorTile = 1 * reduction_dim - weightMatTiles = 2 * self.weightMatTileSize(row_dim, reduction_dim) - space = inputVectorTile + weightMatTiles - # space in bytes - spaceInBytes = space * 8 # number of elements * 8 bytes per element - # print(f'0-{48}-{100}:\ninputVectorTile elts: {inputVectorTile} * 8 = {inputVectorTile*8}. \nweightMatTiles elts: {weightMatTiles} * 8 = {weightMatTiles*8}.\ntotal in bytes: {spaceInBytes}.') - return spaceInBytes - - def spaceRemaining(self, row_dim, reduction_dim): - l1MemoryBytes = 100000 - outputMatVec = roundUpToNearestMultipleOf(self.me.n, row_dim) * 8 - # if we padded the row dimension, - # we allocate an extra (unused) buffer of size n - remainder = self.me.n % row_dim - if (remainder != 0): - outputMatVec = outputMatVec + self.me.n*8 - outputFusedAdd = self.me.n * 8 - inputFusedAdd = self.me.n * 8 - remaining = ( - l1MemoryBytes - - outputMatVec - - outputFusedAdd - - inputFusedAdd - - self.spaceForTiles(row_dim, reduction_dim) - ) - return remaining - - def smallEnough(self,row_dim, red_dim): - return self.spaceRemaining(row_dim, red_dim) > 0 - - # annotate a (row_dim, reduction_dim) pair with - # total L1 space used for tiles - # weight matrix tile size - # total spaced used in L1 - # space remaining, etc. - def annotateOption(self, tup): - return ( - tup, - self.spaceForTiles(tup[0], tup[1]), - self.weightMatTileSize(tup[0], tup[1]), - self.spaceRemaining(tup[0], tup[1]), - ) - - # annotate a (row_dim, reduction_dim) pair with - # quidditch load counting information - # flatten tuple - # matrix-vector transpose with type `, -> ` where `M = 1` (otherwise matmul) - # - # outputVectorEltCount = N (AKA "row_dim") - # inputVectorEltCount = K (AKA "reduction dim") - # - # ex. python3 tile_size_gen.py "dispatch_1" 1200 400 - def flattenThenAnnotateMore(self, ann, caseNo: int): - input = InputMatrix(n=self.me.n, k=self.me.k) - tiles = TileSizes(n=ann[0][0], k=ann[0][1]) - flat = self.convertAnnotationToFlatTuple(ann) - loadInfo = (caseNo,) + qlc.getLoadCountingAnn(input, tiles) - concatted = flat + loadInfo - return concatted - - # helper for converting to CSV - def convertAnnotationToFlatTuple(self, elt): - return ( - f"{0}-{elt[0][0]}-{elt[0][1]}", - elt[0][0], - elt[0][1], - elt[1], - elt[2], - elt[3], - ) - - # helper for converting to CSV - def annotationColumnNames(self): - columns = [ - "JSON Name", - "Row Dim", - "Reduction Dim", - "Space Needed in L1", - "Weight Matrix Tile Size", - "Space Remaining", - ] - return columns - - # export annotated options to CSV - def exportOptionsToCSV(self, dispatchName, caseNo, options): - flat = list(map(lambda tup: self.flattenThenAnnotateMore(tup, caseNo), options)) - cols = ( - self.annotationColumnNames() + ["Case"] - ) + qlc.LoadCountingAnnColumnNames() - df = pd.DataFrame(flat, columns=cols) - df.to_csv( - f"./{dispatchName}_case{caseNo}_searchSpace.csv", - index=False, - ) - print( - f"Saved CSV to ./{dispatchName}_case{caseNo}_searchSpace.csv" - ) - return df - - def tupleIze(a, b): - return (a, b) - - def get_tile_shape(row_dim, col_dim): - if row_dim > col_dim: - shape = "tall" - if row_dim < col_dim: - shape = "wide" - if row_dim == col_dim: - shape = "square" - return shape - - def addMoreColsForConvenience(df): - df["Total Loads"] = df["Regular Loads"] + df["Total Streaming Loads"] - df["Tile Shape"] = df.apply( - lambda x: get_tile_shape(x["Row Dim"], x["Reduction Dim"]), axis=1 - ) - return df - -def main(): - args = sys.argv[1:] - - -if __name__ == "__main__": - main() diff --git a/myrtle/myrtle/tile_sel/README.md b/myrtle/myrtle/tile_sel/README.md deleted file mode 100644 index 6b6f69a7..00000000 --- a/myrtle/myrtle/tile_sel/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# TODO - -take tile selection logic out of the top level `myrtle.py` file and put it in its own a module here. \ No newline at end of file diff --git a/myrtle/myrtle/tile_sel/dispatch-8-svr.pickle b/myrtle/myrtle/tile_sel/dispatch-8-svr.pickle deleted file mode 100644 index 9add666bf5ba37b00a70a00cf35802a43e86c6ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2775 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.10.1", - "pyqt6==6.9.1", - "pyqt6-qt6==6.9.1", - "pyqt6-sip==13.10.2" -] - -[dependency-groups] -dev = [ - "marimo==0.13.11" -] diff --git a/myrtle/requirements.txt b/myrtle/requirements.txt deleted file mode 100644 index 2c902d48..00000000 --- a/myrtle/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -numpy==2.1.2 -pandas==2.2.2 -PyQt6 -scikit-learn==1.7.0 -matplotlib>=3.10.1 \ No newline at end of file diff --git a/myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv deleted file mode 100644 index d67da3c4..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/1x1200x400wm-n-k-timed.csv +++ /dev/null @@ -1,48 +0,0 @@ -JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time -0-24-8,24,8,3136,192,68064,1,60000,960000,480000,160000,320000,1,3,8,20000,3,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,352490,2162791 -0-24-10,24,10,3920,240,67280,1,48000,960000,480000,160000,320000,1,3,10,16000,3,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,297450,1940589 -0-24-16,24,16,6272,384,64928,1,30000,960000,480000,160000,320000,1,3,16,10000,3,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,235366,1695104 -0-24-20,24,20,7840,480,63360,1,24000,960000,480000,160000,320000,1,3,20,8000,3,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,193627,1526480 -0-24-25,24,25,9800,600,61400,1,19200,960000,480000,160000,320000,1,3,25,6400,3,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,172486,1441904 -0-24-40,24,40,15680,960,55520,1,12000,960000,480000,160000,320000,1,3,40,4000,3,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,139631,1310807 -0-24-50,24,50,19600,1200,51600,1,9600,960000,480000,160000,320000,1,3,50,3200,3,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,128962,1268178 -0-24-80,24,80,31360,1920,39840,1,6000,960000,480000,160000,320000,1,3,80,2000,3,80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,120108,1233181 -0-24-100,24,100,39200,2400,32000,1,4800,960000,480000,160000,320000,1,3,100,1600,3,100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,105934,1178348 -0-40-8,40,8,5184,320,66016,1,60000,960000,480000,96000,384000,1,5,8,12000,5,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,256089,1778007 -0-40-10,40,10,6480,400,64720,1,48000,960000,480000,96000,384000,1,5,10,9600,5,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,221806,1640892 -0-40-16,40,16,10368,640,60832,1,30000,960000,480000,96000,384000,1,5,16,6000,5,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,192875,1525120 -0-40-20,40,20,12960,800,58240,1,24000,960000,480000,96000,384000,1,5,20,4800,5,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,153091,1366126 -0-40-25,40,25,16200,1000,55000,1,19200,960000,480000,96000,384000,1,5,25,3840,5,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,138354,1307151 -0-40-40,40,40,25920,1600,45280,1,12000,960000,480000,96000,384000,1,5,40,2400,5,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,108216,1184379 -0-40-50,40,50,32400,2000,38800,1,9600,960000,480000,96000,384000,1,5,50,1920,5,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,110769,1196184 -0-40-80,40,80,51840,3200,19360,1,6000,960000,480000,96000,384000,1,5,80,1200,5,80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,107146,1180489 -0-40-100,40,100,64800,4000,6400,1,4800,960000,480000,96000,384000,1,5,100,960,5,100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,89720,1111478 -0-48-8,48,8,6208,384,64992,1,60000,960000,480000,80000,400000,1,6,8,10000,6,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,240836,1714663 -0-48-10,48,10,7760,480,63440,1,48000,960000,480000,80000,400000,1,6,10,8000,6,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,204027,1576709 -0-48-16,48,16,12416,768,58784,1,30000,960000,480000,80000,400000,1,6,16,5000,6,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,186784,1500582 -0-48-20,48,20,15520,960,55680,1,24000,960000,480000,80000,400000,1,6,20,4000,6,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,141453,1317217 -0-48-25,48,25,19400,1200,51800,1,19200,960000,480000,80000,400000,1,6,25,3200,6,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,129862,1270904 -0-48-40,48,40,31040,1920,40160,1,12000,960000,480000,80000,400000,1,6,40,2000,6,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106211,1176321 -0-48-50,48,50,38800,2400,32400,1,9600,960000,480000,80000,400000,1,6,50,1600,6,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109976,1191954 -0-48-80,48,80,62080,3840,9120,1,6000,960000,480000,80000,400000,1,6,80,1000,6,80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106735,1177052 -0-80-8,80,8,10304,640,60896,1,60000,960000,480000,96000,384000,2,5,8,6000,10,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,354364,2169091 -0-80-10,80,10,12880,800,58320,1,48000,960000,480000,96000,384000,2,5,10,4800,10,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,285619,1896981 -0-80-16,80,16,20608,1280,50592,1,30000,960000,480000,96000,384000,2,5,16,3000,10,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,190237,1512510 -0-80-20,80,20,25760,1600,45440,1,24000,960000,480000,96000,384000,2,5,20,2400,10,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,153729,1369370 -0-80-25,80,25,32200,2000,39000,1,19200,960000,480000,96000,384000,2,5,25,1920,10,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,135496,1293577 -0-80-40,80,40,51520,3200,19680,1,12000,960000,480000,96000,384000,2,5,40,1200,10,40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106161,1174645 -0-80-50,80,50,64400,4000,6800,1,9600,960000,480000,96000,384000,2,5,50,960,10,50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,108924,1187560 -0-120-8,120,8,15424,960,55776,1,60000,960000,480000,96000,384000,3,5,8,4000,15,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,328343,2066876 -0-120-10,120,10,19280,1200,51920,1,48000,960000,480000,96000,384000,3,5,10,3200,15,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,265093,1813422 -0-120-16,120,16,30848,1920,40352,1,30000,960000,480000,96000,384000,3,5,16,2000,15,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,169280,1429097 -0-120-20,120,20,38560,2400,32640,1,24000,960000,480000,96000,384000,3,5,20,1600,15,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,141036,1317755 -0-120-25,120,25,48200,3000,23000,1,19200,960000,480000,96000,384000,3,5,25,1280,15,25,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,125798,1256802 -0-200-8,200,8,25664,1600,45536,1,60000,960000,480000,96000,384000,5,5,8,2400,25,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,322060,2040075 -0-200-10,200,10,32080,2000,39120,1,48000,960000,480000,96000,384000,5,5,10,1920,25,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,262347,1800347 -0-200-16,200,16,51328,3200,19872,1,30000,960000,480000,96000,384000,5,5,16,1200,25,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,165065,1411802 -0-200-20,200,20,64160,4000,7040,1,24000,960000,480000,96000,384000,5,5,20,960,25,20,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,138296,1304769 -0-240-8,240,8,30784,1920,40416,1,60000,960000,480000,96000,384000,6,5,8,2000,30,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,324115,2047464 -0-240-10,240,10,38480,2400,32720,1,48000,960000,480000,96000,384000,6,5,10,1600,30,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,264770,1810659 -0-240-16,240,16,61568,3840,9632,1,30000,960000,480000,96000,384000,6,5,16,1000,30,16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,167242,1422605 -0-400-8,400,8,51264,3200,19936,1,60000,960000,480000,96000,384000,10,5,8,1200,50,8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,344388,2129393 -0-400-10,400,10,64080,4000,7120,1,48000,960000,480000,96000,384000,10,5,10,960,50,10,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,283504,1885686 diff --git a/myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv deleted file mode 100644 index cdc256e4..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/1x400x161wm-n-k-timed.csv +++ /dev/null @@ -1,4 +0,0 @@ -JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time -0-40-23,40,23,14904,920,75496,1,2800,128800,64400,12880,51520,1,5,23,560,5,23,main$async_dispatch_0_matmul_transpose_b_1x400x161_f64,90606,1128198 -0-80-23,80,23,29624,1840,60776,1,2800,128800,64400,12880,51520,2,5,23,280,10,23,main$async_dispatch_0_matmul_transpose_b_1x400x161_f64,89953,1125444 -0-200-23,200,23,73784,4600,16616,1,2800,128800,64400,12880,51520,5,5,23,112,25,23,main$async_dispatch_0_matmul_transpose_b_1x400x161_f64,90235,1124716 diff --git a/myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv deleted file mode 100644 index 677260c6..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/1x600x400wm-n-k-timed.csv +++ /dev/null @@ -1,32 +0,0 @@ -JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time -0-24-8,24,8,3136,192,82464,1,30000,480000,240000,80000,160000,1,3,8,10000,3,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90614,1248502 -0-24-10,24,10,3920,240,81680,1,24000,480000,240000,80000,160000,1,3,10,8000,3,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90614,1221491 -0-24-16,24,16,6272,384,79328,1,15000,480000,240000,80000,160000,1,3,16,5000,3,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90506,1188868 -0-24-20,24,20,7840,480,77760,1,12000,480000,240000,80000,160000,1,3,20,4000,3,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90558,1167912 -0-24-25,24,25,9800,600,75800,1,9600,480000,240000,80000,160000,1,3,25,3200,3,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90645,1155776 -0-24-40,24,40,15680,960,69920,1,6000,480000,240000,80000,160000,1,3,40,2000,3,40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90059,1136797 -0-24-50,24,50,19600,1200,66000,1,4800,480000,240000,80000,160000,1,3,50,1600,3,50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90019,1132348 -0-24-80,24,80,31360,1920,54240,1,3000,480000,240000,80000,160000,1,3,80,1000,3,80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90100,1125616 -0-24-100,24,100,39200,2400,46400,1,2400,480000,240000,80000,160000,1,3,100,800,3,100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90083,1119527 -0-24-200,24,200,78400,4800,7200,1,1200,480000,240000,80000,160000,1,3,200,400,3,200,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,90378,1115541 -0-40-8,40,8,5184,320,80416,1,30000,480000,240000,48000,192000,1,5,8,6000,5,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1202191 -0-40-10,40,10,6480,400,79120,1,24000,480000,240000,48000,192000,1,5,10,4800,5,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1181151 -0-40-16,40,16,10368,640,75232,1,15000,480000,240000,48000,192000,1,5,16,3000,5,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1165214 -0-40-20,40,20,12960,800,72640,1,12000,480000,240000,48000,192000,1,5,20,2400,5,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1144447 -0-40-25,40,25,16200,1000,69400,1,9600,480000,240000,48000,192000,1,5,25,1920,5,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89802,1133899 -0-40-40,40,40,25920,1600,59680,1,6000,480000,240000,48000,192000,1,5,40,1200,5,40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89802,1118890 -0-40-50,40,50,32400,2000,53200,1,4800,480000,240000,48000,192000,1,5,50,960,5,50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89640,1121403 -0-40-80,40,80,51840,3200,33760,1,3000,480000,240000,48000,192000,1,5,80,600,5,80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89871,1121450 -0-40-100,40,100,64800,4000,20800,1,2400,480000,240000,48000,192000,1,5,100,480,5,100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89871,1111783 -0-120-8,120,8,15424,960,70176,1,30000,480000,240000,48000,192000,3,5,8,2000,15,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89827,1240472 -0-120-10,120,10,19280,1200,66320,1,24000,480000,240000,48000,192000,3,5,10,1600,15,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89777,1209024 -0-120-16,120,16,30848,1920,54752,1,15000,480000,240000,48000,192000,3,5,16,1000,15,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89777,1157826 -0-120-20,120,20,38560,2400,47040,1,12000,480000,240000,48000,192000,3,5,20,800,15,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89790,1141866 -0-120-25,120,25,48200,3000,37400,1,9600,480000,240000,48000,192000,3,5,25,640,15,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89827,1131593 -0-120-40,120,40,77120,4800,8480,1,6000,480000,240000,48000,192000,3,5,40,400,15,40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89780,1117408 -0-200-8,200,8,25664,1600,59936,1,30000,480000,240000,48000,192000,5,5,8,1200,25,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89855,1245643 -0-200-10,200,10,32080,2000,53520,1,24000,480000,240000,48000,192000,5,5,10,960,25,10,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89855,1213836 -0-200-16,200,16,51328,3200,34272,1,15000,480000,240000,48000,192000,5,5,16,600,25,16,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89771,1156004 -0-200-20,200,20,64160,4000,21440,1,12000,480000,240000,48000,192000,5,5,20,480,25,20,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89783,1143313 -0-200-25,200,25,80200,5000,5400,1,9600,480000,240000,48000,192000,5,5,25,384,25,25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89842,1133160 -0-600-8,600,8,76864,4800,8736,1,30000,480000,240000,48000,192000,15,5,8,400,75,8,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89771,1196145 diff --git a/myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv b/myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv deleted file mode 100644 index 6a46ca45..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/1x600x600wm-n-k-timed.csv +++ /dev/null @@ -1,48 +0,0 @@ -JSON Name,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Kernel Name,Kernel Time,Total Time -0-24-8,24,8,3136,192,82464,1,45000,720000,360000,120000,240000,1,3,8,15000,3,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90278,1317337 -0-24-10,24,10,3920,240,81680,1,36000,720000,360000,120000,240000,1,3,10,12000,3,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90276,1277927 -0-24-12,24,12,4704,288,80896,1,30000,720000,360000,120000,240000,1,3,12,10000,3,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90272,1258100 -0-24-15,24,15,5880,360,79720,1,24000,720000,360000,120000,240000,1,3,15,8000,3,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90143,1222605 -0-24-20,24,20,7840,480,77760,1,18000,720000,360000,120000,240000,1,3,20,6000,3,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90272,1200568 -0-24-24,24,24,9408,576,76192,1,15000,720000,360000,120000,240000,1,3,24,5000,3,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90234,1178265 -0-24-25,24,25,9800,600,75800,1,14400,720000,360000,120000,240000,1,3,25,4800,3,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90267,1178109 -0-24-30,24,30,11760,720,73840,1,12000,720000,360000,120000,240000,1,3,30,4000,3,30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90143,1166853 -0-24-40,24,40,15680,960,69920,1,9000,720000,360000,120000,240000,1,3,40,3000,3,40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90279,1150626 -0-24-50,24,50,19600,1200,66000,1,7200,720000,360000,120000,240000,1,3,50,2400,3,50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90272,1151103 -0-24-60,24,60,23520,1440,62080,1,6000,720000,360000,120000,240000,1,3,60,2000,3,60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90189,1137990 -0-24-75,24,75,29400,1800,56200,1,4800,720000,360000,120000,240000,1,3,75,1600,3,75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90114,1130408 -0-24-100,24,100,39200,2400,46400,1,3600,720000,360000,120000,240000,1,3,100,1200,3,100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90066,1125137 -0-24-120,24,120,47040,2880,38560,1,3000,720000,360000,120000,240000,1,3,120,1000,3,120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90115,1123133 -0-24-150,24,150,58800,3600,26800,1,2400,720000,360000,120000,240000,1,3,150,800,3,150,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90292,1120558 -0-24-200,24,200,78400,4800,7200,1,1800,720000,360000,120000,240000,1,3,200,600,3,200,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90305,1117767 -0-40-8,40,8,5184,320,80416,1,45000,720000,360000,72000,288000,1,5,8,9000,5,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1245536 -0-40-10,40,10,6480,400,79120,1,36000,720000,360000,72000,288000,1,5,10,7200,5,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1217497 -0-40-12,40,12,7776,480,77824,1,30000,720000,360000,72000,288000,1,5,12,6000,5,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89834,1194662 -0-40-15,40,15,9720,600,75880,1,24000,720000,360000,72000,288000,1,5,15,4800,5,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89772,1180010 -0-40-20,40,20,12960,800,72640,1,18000,720000,360000,72000,288000,1,5,20,3600,5,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89768,1159836 -0-40-24,40,24,15552,960,70048,1,15000,720000,360000,72000,288000,1,5,24,3000,5,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89834,1150147 -0-40-25,40,25,16200,1000,69400,1,14400,720000,360000,72000,288000,1,5,25,2880,5,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1149929 -0-40-30,40,30,19440,1200,66160,1,12000,720000,360000,72000,288000,1,5,30,2400,5,30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1140039 -0-40-40,40,40,25920,1600,59680,1,9000,720000,360000,72000,288000,1,5,40,1800,5,40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89814,1125071 -0-40-50,40,50,32400,2000,53200,1,7200,720000,360000,72000,288000,1,5,50,1440,5,50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89762,1127722 -0-40-60,40,60,38880,2400,46720,1,6000,720000,360000,72000,288000,1,5,60,1200,5,60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89834,1122734 -0-40-75,40,75,48600,3000,37000,1,4800,720000,360000,72000,288000,1,5,75,960,5,75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89813,1117539 -0-40-100,40,100,64800,4000,20800,1,3600,720000,360000,72000,288000,1,5,100,720,5,100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89871,1111783 -0-40-120,40,120,77760,4800,7840,1,3000,720000,360000,72000,288000,1,5,120,600,5,120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89990,1112946 -0-120-8,120,8,15424,960,70176,1,45000,720000,360000,72000,288000,3,5,8,3000,15,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1307012 -0-120-10,120,10,19280,1200,66320,1,36000,720000,360000,72000,288000,3,5,10,2400,15,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91364,1262146 -0-120-12,120,12,23136,1440,62464,1,30000,720000,360000,72000,288000,3,5,12,2000,15,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,90327,1224677 -0-120-15,120,15,28920,1800,56680,1,24000,720000,360000,72000,288000,3,5,15,1600,15,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91364,1192663 -0-120-20,120,20,38560,2400,47040,1,18000,720000,360000,72000,288000,3,5,20,1200,15,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1155408 -0-120-24,120,24,46272,2880,39328,1,15000,720000,360000,72000,288000,3,5,24,1000,15,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1139593 -0-120-25,120,25,48200,3000,37400,1,14400,720000,360000,72000,288000,3,5,25,960,15,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89729,1142824 -0-120-30,120,30,57840,3600,27760,1,12000,720000,360000,72000,288000,3,5,30,800,15,30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91419,1137534 -0-120-40,120,40,77120,4800,8480,1,9000,720000,360000,72000,288000,3,5,40,600,15,40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,91366,1122419 -0-200-8,200,8,25664,1600,59936,1,45000,720000,360000,72000,288000,5,5,8,1800,25,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89761,1312768 -0-200-10,200,10,32080,2000,53520,1,36000,720000,360000,72000,288000,5,5,10,1440,25,10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89790,1266186 -0-200-12,200,12,38496,2400,47104,1,30000,720000,360000,72000,288000,5,5,12,1200,25,12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89598,1227463 -0-200-15,200,15,48120,3000,37480,1,24000,720000,360000,72000,288000,5,5,15,960,25,15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89757,1197700 -0-200-20,200,20,64160,4000,21440,1,18000,720000,360000,72000,288000,5,5,20,720,25,20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89598,1158318 -0-200-24,200,24,76992,4800,8608,1,15000,720000,360000,72000,288000,5,5,24,600,25,24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89598,1141555 -0-200-25,200,25,80200,5000,5400,1,14400,720000,360000,72000,288000,5,5,25,576,25,25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89769,1143879 -0-600-8,600,8,76864,4800,8736,1,45000,720000,360000,72000,288000,15,5,8,600,75,8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,89757,1243185 diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv deleted file mode 100644 index 91d8682a..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case1_everything.csv +++ /dev/null @@ -1,18 +0,0 @@ -JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape -0-24-100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,105051,1172888,24,100,39200,2400,32000,1,4800,960000,480000,160000,320000,1,3,100,1600,3,100,964800,wide -0-24-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,139659,1308333,24,40,15680,960,55520,1,12000,960000,480000,160000,320000,1,3,40,4000,3,40,972000,wide -0-24-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,139659,1308333,24,50,19600,1200,51600,1,9600,960000,480000,160000,320000,1,3,50,3200,3,50,969600,wide -0-24-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,351842,2158774,24,8,3136,192,68064,1,60000,960000,480000,160000,320000,1,3,8,20000,3,8,1020000,tall -0-24-80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,120533,1231430,24,80,31360,1920,39840,1,6000,960000,480000,160000,320000,1,3,80,2000,3,80,966000,wide -0-40-100,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,89658,1110948,40,100,64800,4000,6400,1,4800,960000,480000,96000,384000,1,5,100,960,5,100,964800,wide -0-40-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,108303,1183714,40,40,25920,1600,45280,1,12000,960000,480000,96000,384000,1,5,40,2400,5,40,972000,square -0-40-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,110811,1194951,40,50,32400,2000,38800,1,9600,960000,480000,96000,384000,1,5,50,1920,5,50,969600,wide -0-40-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,256068,1776377,40,8,5184,320,66016,1,60000,960000,480000,96000,384000,1,5,8,12000,5,8,1020000,tall -0-40-80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,107160,1180624,40,80,51840,3200,19360,1,6000,960000,480000,96000,384000,1,5,80,1200,5,80,966000,wide -0-48-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106206,1176438,48,40,31040,1920,40160,1,12000,960000,480000,80000,400000,1,6,40,2000,6,40,972000,tall -0-48-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109639,1190676,48,50,38800,2400,32400,1,9600,960000,480000,80000,400000,1,6,50,1600,6,50,969600,wide -0-48-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,239465,1708958,48,8,6208,384,64992,1,60000,960000,480000,80000,400000,1,6,8,10000,6,8,1020000,tall -0-48-80,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106720,1177628,48,80,62080,3840,9120,1,6000,960000,480000,80000,400000,1,6,80,1000,6,80,966000,wide -0-80-40,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,106157,1176118,80,40,51520,3200,19680,1,12000,960000,480000,96000,384000,2,5,40,1200,10,40,972000,tall -0-80-50,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109169,1188538,80,50,64400,4000,6800,1,9600,960000,480000,96000,384000,2,5,50,960,10,50,969600,tall -0-80-8,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,354369,2169751,80,8,10304,640,60896,1,60000,960000,480000,96000,384000,2,5,8,6000,10,8,1020000,tall diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv deleted file mode 100644 index b7a69c73..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/dispatch_1_case2_everything.csv +++ /dev/null @@ -1,42 +0,0 @@ -JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape -0-24-104,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,133167,1284223,24,104,40768,2496,30432,2,4800,998400,499200,166400,332800,1,3,104,1600,3,104,1003200,wide -0-24-112,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,158363,1398894,24,112,43904,2688,27296,2,4800,1075200,537600,179200,358400,1,3,112,1600,3,112,1080000,wide -0-24-120,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,167288,1419104,24,120,47040,2880,24160,2,4800,1152000,576000,192000,384000,1,3,120,1600,3,120,1156800,wide -0-24-128,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,180448,1490723,24,128,50176,3072,21024,2,4800,1228800,614400,204800,409600,1,3,128,1600,3,128,1233600,wide -0-24-136,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,129958,1271622,24,136,53312,3264,17888,2,3600,979200,489600,163200,326400,1,3,136,1200,3,136,982800,wide -0-24-144,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,149984,1351389,24,144,56448,3456,14752,2,3600,1036800,518400,172800,345600,1,3,144,1200,3,144,1040400,wide -0-24-152,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,160151,1391286,24,152,59584,3648,11616,2,3600,1094400,547200,182400,364800,1,3,152,1200,3,152,1098000,wide -0-24-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,305685,1965176,24,16,6272,384,64928,2,30000,960000,480000,160000,320000,1,3,16,10000,3,16,990000,tall -0-24-160,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,171613,1438302,24,160,62720,3840,8480,2,3600,1152000,576000,192000,384000,1,3,160,1200,3,160,1155600,wide -0-24-168,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,174951,1450470,24,168,65856,4032,5344,2,3600,1209600,604800,201600,403200,1,3,168,1200,3,168,1213200,wide -0-24-176,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109169,1188538,24,176,68992,4224,2208,2,3600,1267200,633600,211200,422400,1,3,176,1200,3,176,1270800,wide -0-24-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,305685,1965176,24,24,9408,576,61792,2,20400,979200,489600,163200,326400,1,3,24,6800,3,24,999600,square -0-24-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,302597,1962088,24,32,12544,768,58656,2,15600,998400,499200,166400,332800,1,3,32,5200,3,32,1014000,wide -0-24-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,244422,1729603,24,48,18816,1152,52384,2,10800,1036800,518400,172800,345600,1,3,48,3600,3,48,1047600,wide -0-24-56,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,228434,1665727,24,56,21952,1344,49248,2,9600,1075200,537600,179200,358400,1,3,56,3200,3,56,1084800,wide -0-24-64,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,244324,1729091,24,64,25088,1536,46112,2,8400,1075200,537600,179200,358400,1,3,64,2800,3,64,1083600,wide -0-24-72,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,200557,1554774,24,72,28224,1728,42976,2,7200,1036800,518400,172800,345600,1,3,72,2400,3,72,1044000,wide -0-24-88,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,155517,1397695,24,88,34496,2112,36704,2,6000,1056000,528000,176000,352000,1,3,88,2000,3,88,1062000,wide -0-24-96,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,229844,1672015,24,96,37632,2304,33568,2,6000,1152000,576000,192000,384000,1,3,96,2000,3,96,1158000,wide -0-40-104,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,133189,1283634,40,104,67392,4160,3808,2,4800,998400,499200,99840,399360,1,5,104,960,5,104,1003200,wide -0-40-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,193104,1522559,40,16,10368,640,60832,2,30000,960000,480000,96000,384000,1,5,16,6000,5,16,990000,tall -0-40-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,238987,1706789,40,24,15552,960,55648,2,20400,979200,489600,97920,391680,1,5,24,4080,5,24,999600,tall -0-40-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,263308,1791252,40,32,20736,1280,50464,2,15600,998400,499200,99840,399360,1,5,32,3120,5,32,1014000,tall -0-40-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,207318,1581715,40,48,31104,1920,40096,2,10800,1036800,518400,103680,414720,1,5,48,2160,5,48,1047600,wide -0-40-56,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,156134,1375192,40,56,36288,2240,34912,2,9600,1075200,537600,107520,430080,1,5,56,1920,5,56,1084800,wide -0-40-64,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,214576,1610608,40,64,41472,2560,29728,2,8400,1075200,537600,107520,430080,1,5,64,1680,5,64,1083600,wide -0-40-72,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,147559,1343601,40,72,46656,2880,24544,2,7200,1036800,518400,103680,414720,1,5,72,1440,5,72,1044000,wide -0-40-88,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,149984,1351389,40,88,57024,3520,14176,2,6000,1056000,528000,105600,422400,1,5,88,1200,5,88,1062000,wide -0-40-96,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,175225,1452056,40,96,62208,3840,8992,2,6000,1152000,576000,115200,460800,1,5,96,1200,5,96,1158000,wide -0-48-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,186746,1498002,48,16,12416,768,58784,2,30000,960000,480000,80000,400000,1,6,16,5000,6,16,990000,tall -0-48-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,219658,1633488,48,24,18624,1152,52576,2,20400,979200,489600,81600,408000,1,6,24,3400,6,24,999600,tall -0-48-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,257192,1780662,48,32,24832,1536,46368,2,15600,998400,499200,83200,416000,1,6,32,2600,6,32,1014000,tall -0-48-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,201405,1554686,48,48,37248,2304,33952,2,10800,1036800,518400,86400,432000,1,6,48,1800,6,48,1047600,square -0-48-56,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,155593,1373233,48,56,43456,2688,27744,2,9600,1075200,537600,89600,448000,1,6,56,1600,6,56,1084800,wide -0-48-64,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,213824,1606760,48,64,49664,3072,21536,2,8400,1075200,537600,89600,448000,1,6,64,1400,6,64,1083600,wide -0-48-72,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,148111,1344135,48,72,55872,3456,15328,2,7200,1036800,518400,86400,432000,1,6,72,1200,6,72,1044000,wide -0-48-88,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,109169,1188538,48,88,68288,4224,2912,2,6000,1056000,528000,88000,440000,1,6,88,1000,6,88,1062000,wide -0-80-16,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,190305,1512268,80,16,20608,1280,50592,2,30000,960000,480000,96000,384000,2,5,16,3000,10,16,990000,tall -0-80-24,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,200557,1554774,80,24,30912,1920,40288,2,20400,979200,489600,97920,391680,2,5,24,2040,10,24,999600,tall -0-80-32,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,212468,1602133,80,32,41216,2560,29984,2,15600,998400,499200,99840,399360,2,5,32,1560,10,32,1014000,tall -0-80-48,main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64,182434,1481577,80,48,61824,3840,9376,2,10800,1036800,518400,103680,414720,2,5,48,1080,10,48,1047600,tall diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv deleted file mode 100644 index 8fc9fd9d..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case1_everything.csv +++ /dev/null @@ -1,15 +0,0 @@ -JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape -0-120-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,67997,1131399,120,25,48200,3000,37400,1,9600,480000,240000,48000,192000,3,5,25,640,15,25,489600,tall -0-120-40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,53171,1116304,120,40,77120,4800,8480,1,6000,480000,240000,48000,192000,3,5,40,400,15,40,486000,tall -0-200-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,69553,1131953,200,25,80200,5000,5400,1,9600,480000,240000,48000,192000,5,5,25,384,25,25,489600,tall -0-24-100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,54441,1118477,24,100,39200,2400,46400,1,2400,480000,240000,80000,160000,1,3,100,800,3,100,482400,wide -0-24-200,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,49150,1112587,24,200,78400,4800,7200,1,1200,480000,240000,80000,160000,1,3,200,400,3,200,481200,wide -0-24-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89734,1153158,24,25,9800,600,75800,1,9600,480000,240000,80000,160000,1,3,25,3200,3,25,489600,wide -0-24-40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,72175,1134625,24,40,15680,960,69920,1,6000,480000,240000,80000,160000,1,3,40,2000,3,40,486000,wide -0-24-50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,66757,1128811,24,50,19600,1200,66000,1,4800,480000,240000,80000,160000,1,3,50,1600,3,50,484800,wide -0-24-80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,61675,1123274,24,80,31360,1920,54240,1,3000,480000,240000,80000,160000,1,3,80,1000,3,80,483000,wide -0-40-100,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,46812,1110948,40,100,64800,4000,20800,1,2400,480000,240000,48000,192000,1,5,100,480,5,100,482400,wide -0-40-25,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,71741,1133934,40,25,16200,1000,69400,1,9600,480000,240000,48000,192000,1,5,25,1920,5,25,489600,tall -0-40-40,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,56816,1118816,40,40,25920,1600,59680,1,6000,480000,240000,48000,192000,1,5,40,1200,5,40,486000,square -0-40-50,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,57551,1120483,40,50,32400,2000,53200,1,4800,480000,240000,48000,192000,1,5,50,960,5,50,484800,wide -0-40-80,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,55623,1119061,40,80,51840,3200,33760,1,3000,480000,240000,48000,192000,1,5,80,600,5,80,483000,wide diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv deleted file mode 100644 index 564e1123..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/dispatch_7_case2_everything.csv +++ /dev/null @@ -1,34 +0,0 @@ -JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape -0-120-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,105501,1169813,120,22,42416,2640,43184,2,11400,501600,250800,50160,200640,3,5,22,760,15,22,513000,tall -0-120-33,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,93178,1157490,120,33,63624,3960,21976,2,7800,514800,257400,51480,205920,3,5,33,520,15,33,522600,tall -0-120-44,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,87587,1152136,120,44,84832,5280,768,2,6000,528000,264000,52800,211200,3,5,44,400,15,44,534000,tall -0-200-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,111430,1179508,200,22,70576,4400,15024,2,11400,501600,250800,50160,200640,5,5,22,456,25,22,513000,tall -0-24-107,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,79894,1143608,24,107,41944,2568,43656,2,2400,513600,256800,85600,171200,1,3,107,800,3,107,516000,wide -0-24-117,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,85151,1147681,24,117,45864,2808,39736,2,2400,561600,280800,93600,187200,1,3,117,800,3,117,564000,wide -0-24-127,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,91451,1153981,24,127,49784,3048,35816,2,2400,609600,304800,101600,203200,1,3,127,800,3,127,612000,wide -0-24-137,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,68663,1131824,24,137,53704,3288,31896,2,1800,493200,246600,82200,164400,1,3,137,600,3,137,495000,wide -0-24-147,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,71033,1134194,24,147,57624,3528,27976,2,1800,529200,264600,88200,176400,1,3,147,600,3,147,531000,wide -0-24-157,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,85364,1147762,24,157,61544,3768,24056,2,1800,565200,282600,94200,188400,1,3,157,600,3,157,567000,wide -0-24-167,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89767,1152928,24,167,65464,4008,20136,2,1800,601200,300600,100200,200400,1,3,167,600,3,167,603000,wide -0-24-177,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,95301,1158097,24,177,69384,4248,16216,2,1800,637200,318600,106200,212400,1,3,177,600,3,177,639000,wide -0-24-187,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,99394,1163888,24,187,73304,4488,12296,2,1800,673200,336600,112200,224400,1,3,187,600,3,187,675000,wide -0-24-197,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,104523,1167319,24,197,77224,4728,8376,2,1800,709200,354600,118200,236400,1,3,197,600,3,197,711000,wide -0-24-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,161762,1225886,24,22,8624,528,76976,2,11400,501600,250800,83600,167200,1,3,22,3800,3,22,513000,tall -0-24-33,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,140831,1203903,24,33,12936,792,72664,2,7800,514800,257400,85800,171600,1,3,33,2600,3,33,522600,wide -0-24-44,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,126053,1190165,24,44,17248,1056,68352,2,6000,528000,264000,88000,176000,1,3,44,2000,3,44,534000,wide -0-24-55,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,117045,1179445,24,55,21560,1320,64040,2,4800,528000,264000,88000,176000,1,3,55,1600,3,55,532800,wide -0-24-65,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,114246,1177077,24,65,25480,1560,60120,2,4200,546000,273000,91000,182000,1,3,65,1400,3,65,550200,wide -0-24-75,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,103858,1167953,24,75,29400,1800,56200,2,3600,540000,270000,90000,180000,1,3,75,1200,3,75,543600,wide -0-24-86,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,80593,1144043,24,86,33712,2064,51888,2,3000,516000,258000,86000,172000,1,3,86,1000,3,86,519000,wide -0-24-96,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,116965,1180497,24,96,37632,2304,47968,2,3000,576000,288000,96000,192000,1,3,96,1000,3,96,579000,wide -0-40-107,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,71827,1136510,40,107,69336,4280,16264,2,2400,513600,256800,51360,205440,1,5,107,480,5,107,516000,wide -0-40-117,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,81756,1146434,40,117,75816,4680,9784,2,2400,561600,280800,56160,224640,1,5,117,480,5,117,564000,wide -0-40-127,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,87309,1151987,40,127,82296,5080,3304,2,2400,609600,304800,60960,243840,1,5,127,480,5,127,612000,wide -0-40-22,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,120672,1182193,40,22,14256,880,71344,2,11400,501600,250800,50160,200640,1,5,22,2280,5,22,513000,tall -0-40-33,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,112161,1175801,40,33,21384,1320,64216,2,7800,514800,257400,51480,205920,1,5,33,1560,5,33,522600,tall -0-40-44,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,103664,1165185,40,44,28512,1760,57088,2,6000,528000,264000,52800,211200,1,5,44,1200,5,44,534000,wide -0-40-55,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,84042,1146926,40,55,35640,2200,49960,2,4800,528000,264000,52800,211200,1,5,55,960,5,55,532800,wide -0-40-65,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,84394,1149077,40,65,42120,2600,43480,2,4200,546000,273000,54600,218400,1,5,65,840,5,65,550200,wide -0-40-75,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,81960,1146638,40,75,48600,3000,37000,2,3600,540000,270000,54000,216000,1,5,75,720,5,75,543600,wide -0-40-86,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,77634,1142317,40,86,55728,3440,29872,2,3000,516000,258000,51600,206400,1,5,86,600,5,86,519000,wide -0-40-96,main$async_dispatch_7_matmul_transpose_b_1x600x400_f64,89566,1153034,40,96,62208,3840,23392,2,3000,576000,288000,57600,230400,1,5,96,600,5,96,579000,wide diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv deleted file mode 100644 index b72c8025..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case1_everything.csv +++ /dev/null @@ -1,31 +0,0 @@ -JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape -0-24-10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,243499,1284237,24,10,3920,240,81680,1,36000,720000,360000,120000,240000,1,3,10,12000,3,10,756000,tall -0-24-100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,80994,1122125,24,100,39200,2400,46400,1,3600,720000,360000,120000,240000,1,3,100,1200,3,100,723600,wide -0-24-12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,217262,1258257,24,12,4704,288,80896,1,30000,720000,360000,120000,240000,1,3,12,10000,3,12,750000,tall -0-24-120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,79292,1119386,24,120,47040,2880,38560,1,3000,720000,360000,120000,240000,1,3,120,1000,3,120,723000,wide -0-24-15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,191694,1232673,24,15,5880,360,79720,1,24000,720000,360000,120000,240000,1,3,15,8000,3,15,744000,tall -0-24-150,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,76490,1118136,24,150,58800,3600,26800,1,2400,720000,360000,120000,240000,1,3,150,800,3,150,722400,wide -0-24-20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,148969,1190920,24,20,7840,480,77760,1,18000,720000,360000,120000,240000,1,3,20,6000,3,20,738000,tall -0-24-200,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,72988,1112697,24,200,78400,4800,7200,1,1800,720000,360000,120000,240000,1,3,200,600,3,200,721800,wide -0-24-24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,134705,1174568,24,24,9408,576,76192,1,15000,720000,360000,120000,240000,1,3,24,5000,3,24,735000,square -0-24-25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,144643,1185966,24,25,9800,600,75800,1,14400,720000,360000,120000,240000,1,3,25,4800,3,25,734400,wide -0-24-30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,130217,1171196,24,30,11760,720,73840,1,12000,720000,360000,120000,240000,1,3,30,4000,3,30,732000,wide -0-24-40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,117565,1159445,24,40,15680,960,69920,1,9000,720000,360000,120000,240000,1,3,40,3000,3,40,729000,wide -0-24-50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,105382,1146377,24,50,19600,1200,66000,1,7200,720000,360000,120000,240000,1,3,50,2400,3,50,727200,wide -0-24-60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,93493,1133403,24,60,23520,1440,62080,1,6000,720000,360000,120000,240000,1,3,60,2000,3,60,726000,wide -0-24-75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,88898,1129237,24,75,29400,1800,56200,1,4800,720000,360000,120000,240000,1,3,75,1600,3,75,724800,wide -0-24-8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,310238,1353027,24,8,3136,192,82464,1,45000,720000,360000,120000,240000,1,3,8,15000,3,8,765000,tall -0-40-10,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,174619,1214935,40,10,6480,400,79120,1,36000,720000,360000,72000,288000,1,5,10,7200,5,10,756000,tall -0-40-100,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,69121,1110948,40,100,64800,4000,20800,1,3600,720000,360000,72000,288000,1,5,100,720,5,100,723600,wide -0-40-12,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,152380,1192216,40,12,7776,480,77824,1,30000,720000,360000,72000,288000,1,5,12,6000,5,12,750000,tall -0-40-120,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,70917,1111317,40,120,77760,4800,7840,1,3000,720000,360000,72000,288000,1,5,120,600,5,120,723000,wide -0-40-15,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,137285,1178124,40,15,9720,600,75880,1,24000,720000,360000,72000,288000,1,5,15,4800,5,15,744000,tall -0-40-20,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,119287,1159603,40,20,12960,800,72640,1,18000,720000,360000,72000,288000,1,5,20,3600,5,20,738000,tall -0-40-24,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,108786,1148622,40,24,15552,960,70048,1,15000,720000,360000,72000,288000,1,5,24,3000,5,24,735000,tall -0-40-25,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,107000,1147316,40,25,16200,1000,69400,1,14400,720000,360000,72000,288000,1,5,25,2880,5,25,734400,tall -0-40-30,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,98109,1138425,40,30,19440,1200,66160,1,12000,720000,360000,72000,288000,1,5,30,2400,5,30,732000,tall -0-40-40,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,84547,1123932,40,40,25920,1600,59680,1,9000,720000,360000,72000,288000,1,5,40,1800,5,40,729000,square -0-40-50,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,85386,1126892,40,50,32400,2000,53200,1,7200,720000,360000,72000,288000,1,5,50,1440,5,50,727200,wide -0-40-60,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,80221,1120055,40,60,38880,2400,46720,1,6000,720000,360000,72000,288000,1,5,60,1200,5,60,726000,wide -0-40-75,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,76421,1117436,40,75,48600,3000,37000,1,4800,720000,360000,72000,288000,1,5,75,960,5,75,724800,wide -0-40-8,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,204744,1246250,40,8,5184,320,80416,1,45000,720000,360000,72000,288000,1,5,8,9000,5,8,765000,tall diff --git a/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv b/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv deleted file mode 100644 index 9ad9f5d2..00000000 --- a/myrtle/sensitivity-analysis/holistic-data/dispatch_8_case2_everything.csv +++ /dev/null @@ -1,35 +0,0 @@ -JSON Name,Kernel Name,Kernel Time,Total Time,Row Dim,Reduction Dim,Space Needed in L1,Weight Matrix Tile Size,Space Remaining,Case,Regular Loads,Total Streaming Loads,Other Streaming Loads,Start Reuse Streaming Loads,Reused Streaming Loads,Outer Loop Iters,HW Loop Body,HW Loop Iters,Microkernel Count,Microkernel Row Dim,Microkernel Reduction Dim,Total Loads,Tile Shape -0-24-104,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,109765,1151146,24,104,40768,2496,44832,2,3600,748800,374400,124800,249600,1,3,104,1200,3,104,752400,wide -0-24-112,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,134151,1175532,24,112,43904,2688,41696,2,3600,806400,403200,134400,268800,1,3,112,1200,3,112,810000,wide -0-24-128,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,121256,1161864,24,128,50176,3072,35424,2,3000,768000,384000,128000,256000,1,3,128,1000,3,128,771000,wide -0-24-136,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,117771,1157479,24,136,53312,3264,32288,2,3000,816000,408000,136000,272000,1,3,136,1000,3,136,819000,wide -0-24-144,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,124479,1164202,24,144,56448,3456,29152,2,3000,864000,432000,144000,288000,1,3,144,1000,3,144,867000,wide -0-24-152,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,98810,1138878,24,152,59584,3648,26016,2,2400,729600,364800,121600,243200,1,3,152,800,3,152,732000,wide -0-24-16,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,287527,1328554,24,16,6272,384,79328,2,22800,729600,364800,121600,243200,1,3,16,7600,3,16,752400,tall -0-24-160,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,113007,1153075,24,160,62720,3840,22880,2,2400,768000,384000,128000,256000,1,3,160,800,3,160,770400,wide -0-24-168,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,115977,1156029,24,168,65856,4032,19744,2,2400,806400,403200,134400,268800,1,3,168,800,3,168,808800,wide -0-24-176,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,121563,1161599,24,176,68992,4224,16608,2,2400,844800,422400,140800,281600,1,3,176,800,3,176,847200,wide -0-24-184,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,126018,1166054,24,184,72128,4416,13472,2,2400,883200,441600,147200,294400,1,3,184,800,3,184,885600,wide -0-24-192,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,132705,1171465,24,192,75264,4608,10336,2,2400,921600,460800,153600,307200,1,3,192,800,3,192,924000,wide -0-24-208,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,99032,1139284,24,208,81536,4992,4064,2,1800,748800,374400,124800,249600,1,3,208,600,3,208,750600,wide -0-24-216,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,100894,1141146,24,216,84672,5184,928,2,1800,777600,388800,129600,259200,1,3,216,600,3,216,779400,wide -0-24-32,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,224213,1263544,24,32,12544,768,73056,2,11400,729600,364800,121600,243200,1,3,32,3800,3,32,741000,wide -0-24-48,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,179036,1220109,24,48,18816,1152,66784,2,7800,748800,374400,124800,249600,1,3,48,2600,3,48,756600,wide -0-24-56,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,161788,1201559,24,56,21952,1344,63648,2,6600,739200,369600,123200,246400,1,3,56,2200,3,56,745800,wide -0-24-64,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,176112,1216464,24,64,25088,1536,60512,2,6000,768000,384000,128000,256000,1,3,64,2000,3,64,774000,wide -0-24-72,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,154947,1195820,24,72,28224,1728,57376,2,5400,777600,388800,129600,259200,1,3,72,1800,3,72,783000,wide -0-24-80,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,157295,1197742,24,80,31360,1920,54240,2,4800,768000,384000,128000,256000,1,3,80,1600,3,80,772800,wide -0-24-88,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,115001,1156337,24,88,34496,2112,51104,2,4200,739200,369600,123200,246400,1,3,88,1400,3,88,743400,wide -0-24-96,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,163048,1203343,24,96,37632,2304,47968,2,4200,806400,403200,134400,268800,1,3,96,1400,3,96,810600,wide -0-40-104,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,102441,1144325,40,104,67392,4160,18208,2,3600,748800,374400,74880,299520,1,5,104,720,5,104,752400,wide -0-40-112,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,129677,1170250,40,112,72576,4480,13024,2,3600,806400,403200,80640,322560,1,5,112,720,5,112,810000,wide -0-40-128,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,110867,1151688,40,128,82944,5120,2656,2,3000,768000,384000,76800,307200,1,5,128,600,5,128,771000,wide -0-40-16,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,230363,1271509,40,16,10368,640,75232,2,22800,729600,364800,72960,291840,1,5,16,4560,5,16,752400,tall -0-40-32,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,190282,1231428,40,32,20736,1280,64864,2,11400,729600,364800,72960,291840,1,5,32,2280,5,32,741000,tall -0-40-48,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,151657,1192577,40,48,31104,1920,54496,2,7800,748800,374400,74880,299520,1,5,48,1560,5,48,756600,wide -0-40-56,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,113118,1154263,40,56,36288,2240,49312,2,6600,739200,369600,73920,295680,1,5,56,1320,5,56,745800,wide -0-40-64,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,154903,1196590,40,64,41472,2560,44128,2,6000,768000,384000,76800,307200,1,5,64,1200,5,64,774000,wide -0-40-72,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,112895,1154786,40,72,46656,2880,38944,2,5400,777600,388800,77760,311040,1,5,72,1080,5,72,783000,wide -0-40-80,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,140450,1180955,40,80,51840,3200,33760,2,4800,768000,384000,76800,307200,1,5,80,960,5,80,772800,wide -0-40-88,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,103232,1144011,40,88,57024,3520,28576,2,4200,739200,369600,73920,295680,1,5,88,840,5,88,743400,wide -0-40-96,main$async_dispatch_8_matmul_transpose_b_1x600x600_f64,122001,1163885,40,96,62208,3840,23392,2,4200,806400,403200,80640,322560,1,5,96,840,5,96,810600,wide diff --git a/myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv b/myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv deleted file mode 100644 index 6632fc69..00000000 --- a/myrtle/sensitivity-analysis/microkernel-data/pivoted.cost_model_30_thru_201.csv +++ /dev/null @@ -1,316 +0,0 @@ -kernels,linalg_xdsl -matmul_transb 1x100x1xf64,437 -matmul_transb 1x100x2xf64,454 -matmul_transb 1x100x3xf64,450 -matmul_transb 1x100x4xf64,462 -matmul_transb 1x100x5xf64,560 -matmul_transb 1x100x6xf64,667 -matmul_transb 1x100x7xf64,766 -matmul_transb 1x100x8xf64,885 -matmul_transb 1x100x9xf64,1281 -matmul_transb 1x105x1xf64,457 -matmul_transb 1x105x2xf64,474 -matmul_transb 1x105x3xf64,470 -matmul_transb 1x105x4xf64,481 -matmul_transb 1x105x5xf64,585 -matmul_transb 1x105x6xf64,697 -matmul_transb 1x105x7xf64,801 -matmul_transb 1x105x8xf64,925 -matmul_transb 1x105x9xf64,1341 -matmul_transb 1x110x1xf64,477 -matmul_transb 1x110x2xf64,494 -matmul_transb 1x110x3xf64,490 -matmul_transb 1x110x4xf64,501 -matmul_transb 1x110x5xf64,610 -matmul_transb 1x110x6xf64,727 -matmul_transb 1x110x7xf64,836 -matmul_transb 1x110x8xf64,965 -matmul_transb 1x110x9xf64,1401 -matmul_transb 1x115x1xf64,497 -matmul_transb 1x115x2xf64,514 -matmul_transb 1x115x3xf64,510 -matmul_transb 1x115x4xf64,521 -matmul_transb 1x115x5xf64,635 -matmul_transb 1x115x6xf64,757 -matmul_transb 1x115x7xf64,871 -matmul_transb 1x115x8xf64,1005 -matmul_transb 1x115x9xf64,1461 -matmul_transb 1x120x1xf64,517 -matmul_transb 1x120x2xf64,534 -matmul_transb 1x120x3xf64,530 -matmul_transb 1x120x4xf64,541 -matmul_transb 1x120x5xf64,660 -matmul_transb 1x120x6xf64,787 -matmul_transb 1x120x7xf64,906 -matmul_transb 1x120x8xf64,1045 -matmul_transb 1x120x9xf64,1521 -matmul_transb 1x125x1xf64,537 -matmul_transb 1x125x2xf64,554 -matmul_transb 1x125x3xf64,550 -matmul_transb 1x125x4xf64,561 -matmul_transb 1x125x5xf64,685 -matmul_transb 1x125x6xf64,817 -matmul_transb 1x125x7xf64,941 -matmul_transb 1x125x8xf64,1085 -matmul_transb 1x125x9xf64,1581 -matmul_transb 1x130x1xf64,557 -matmul_transb 1x130x2xf64,574 -matmul_transb 1x130x3xf64,590 -matmul_transb 1x130x4xf64,581 -matmul_transb 1x130x5xf64,706 -matmul_transb 1x130x6xf64,847 -matmul_transb 1x130x7xf64,976 -matmul_transb 1x130x8xf64,1125 -matmul_transb 1x130x9xf64,1636 -matmul_transb 1x135x1xf64,577 -matmul_transb 1x135x2xf64,594 -matmul_transb 1x135x3xf64,610 -matmul_transb 1x135x4xf64,601 -matmul_transb 1x135x5xf64,731 -matmul_transb 1x135x6xf64,877 -matmul_transb 1x135x7xf64,1011 -matmul_transb 1x135x8xf64,1165 -matmul_transb 1x135x9xf64,1696 -matmul_transb 1x140x1xf64,597 -matmul_transb 1x140x2xf64,614 -matmul_transb 1x140x3xf64,630 -matmul_transb 1x140x4xf64,621 -matmul_transb 1x140x5xf64,756 -matmul_transb 1x140x6xf64,907 -matmul_transb 1x140x7xf64,1046 -matmul_transb 1x140x8xf64,1205 -matmul_transb 1x140x9xf64,1756 -matmul_transb 1x145x1xf64,617 -matmul_transb 1x145x2xf64,634 -matmul_transb 1x145x3xf64,650 -matmul_transb 1x145x4xf64,641 -matmul_transb 1x145x5xf64,781 -matmul_transb 1x145x6xf64,937 -matmul_transb 1x145x7xf64,1081 -matmul_transb 1x145x8xf64,1245 -matmul_transb 1x145x9xf64,1816 -matmul_transb 1x150x1xf64,637 -matmul_transb 1x150x2xf64,654 -matmul_transb 1x150x3xf64,670 -matmul_transb 1x150x4xf64,661 -matmul_transb 1x150x5xf64,806 -matmul_transb 1x150x6xf64,967 -matmul_transb 1x150x7xf64,1116 -matmul_transb 1x150x8xf64,1285 -matmul_transb 1x150x9xf64,1876 -matmul_transb 1x155x1xf64,657 -matmul_transb 1x155x2xf64,674 -matmul_transb 1x155x3xf64,690 -matmul_transb 1x155x4xf64,681 -matmul_transb 1x155x5xf64,835 -matmul_transb 1x155x6xf64,997 -matmul_transb 1x155x7xf64,1151 -matmul_transb 1x155x8xf64,1325 -matmul_transb 1x155x9xf64,1936 -matmul_transb 1x160x1xf64,677 -matmul_transb 1x160x2xf64,694 -matmul_transb 1x160x3xf64,710 -matmul_transb 1x160x4xf64,701 -matmul_transb 1x160x5xf64,860 -matmul_transb 1x160x6xf64,1027 -matmul_transb 1x160x7xf64,1186 -matmul_transb 1x160x8xf64,1365 -matmul_transb 1x160x9xf64,1996 -matmul_transb 1x165x1xf64,697 -matmul_transb 1x165x2xf64,714 -matmul_transb 1x165x3xf64,730 -matmul_transb 1x165x4xf64,721 -matmul_transb 1x165x5xf64,885 -matmul_transb 1x165x6xf64,1057 -matmul_transb 1x165x7xf64,1221 -matmul_transb 1x165x8xf64,1405 -matmul_transb 1x165x9xf64,2056 -matmul_transb 1x170x1xf64,717 -matmul_transb 1x170x2xf64,734 -matmul_transb 1x170x3xf64,750 -matmul_transb 1x170x4xf64,741 -matmul_transb 1x170x5xf64,915 -matmul_transb 1x170x6xf64,1087 -matmul_transb 1x170x7xf64,1256 -matmul_transb 1x170x8xf64,1445 -matmul_transb 1x170x9xf64,2116 -matmul_transb 1x175x1xf64,737 -matmul_transb 1x175x2xf64,753 -matmul_transb 1x175x3xf64,770 -matmul_transb 1x175x4xf64,761 -matmul_transb 1x175x5xf64,935 -matmul_transb 1x175x6xf64,1117 -matmul_transb 1x175x7xf64,1291 -matmul_transb 1x175x8xf64,1485 -matmul_transb 1x175x9xf64,2176 -matmul_transb 1x180x1xf64,757 -matmul_transb 1x180x2xf64,773 -matmul_transb 1x180x3xf64,790 -matmul_transb 1x180x4xf64,781 -matmul_transb 1x180x5xf64,960 -matmul_transb 1x180x6xf64,1147 -matmul_transb 1x180x7xf64,1326 -matmul_transb 1x180x8xf64,1525 -matmul_transb 1x180x9xf64,2236 -matmul_transb 1x185x1xf64,777 -matmul_transb 1x185x2xf64,793 -matmul_transb 1x185x3xf64,810 -matmul_transb 1x185x4xf64,801 -matmul_transb 1x185x5xf64,985 -matmul_transb 1x185x6xf64,1177 -matmul_transb 1x185x7xf64,1361 -matmul_transb 1x185x8xf64,1565 -matmul_transb 1x185x9xf64,2296 -matmul_transb 1x190x1xf64,797 -matmul_transb 1x190x2xf64,813 -matmul_transb 1x190x3xf64,830 -matmul_transb 1x190x4xf64,821 -matmul_transb 1x190x5xf64,1010 -matmul_transb 1x190x6xf64,1207 -matmul_transb 1x190x7xf64,1396 -matmul_transb 1x190x8xf64,1605 -matmul_transb 1x190x9xf64,2356 -matmul_transb 1x195x1xf64,817 -matmul_transb 1x195x2xf64,833 -matmul_transb 1x195x3xf64,850 -matmul_transb 1x195x4xf64,841 -matmul_transb 1x195x5xf64,1035 -matmul_transb 1x195x6xf64,1237 -matmul_transb 1x195x7xf64,1431 -matmul_transb 1x195x8xf64,1645 -matmul_transb 1x195x9xf64,2416 -matmul_transb 1x200x1xf64,837 -matmul_transb 1x200x2xf64,853 -matmul_transb 1x200x3xf64,870 -matmul_transb 1x200x4xf64,861 -matmul_transb 1x200x5xf64,1060 -matmul_transb 1x200x6xf64,1267 -matmul_transb 1x200x7xf64,1466 -matmul_transb 1x200x8xf64,1685 -matmul_transb 1x200x9xf64,2476 -matmul_transb 1x30x1xf64,157 -matmul_transb 1x30x2xf64,175 -matmul_transb 1x30x3xf64,170 -matmul_transb 1x30x4xf64,192 -matmul_transb 1x30x5xf64,212 -matmul_transb 1x30x6xf64,246 -matmul_transb 1x30x7xf64,278 -matmul_transb 1x30x8xf64,314 -matmul_transb 1x30x9xf64,442 -matmul_transb 1x35x1xf64,177 -matmul_transb 1x35x2xf64,195 -matmul_transb 1x35x3xf64,190 -matmul_transb 1x35x4xf64,212 -matmul_transb 1x35x5xf64,237 -matmul_transb 1x35x6xf64,276 -matmul_transb 1x35x7xf64,312 -matmul_transb 1x35x8xf64,354 -matmul_transb 1x35x9xf64,502 -matmul_transb 1x40x1xf64,197 -matmul_transb 1x40x2xf64,215 -matmul_transb 1x40x3xf64,210 -matmul_transb 1x40x4xf64,232 -matmul_transb 1x40x5xf64,262 -matmul_transb 1x40x6xf64,317 -matmul_transb 1x40x7xf64,347 -matmul_transb 1x40x8xf64,394 -matmul_transb 1x40x9xf64,562 -matmul_transb 1x45x1xf64,217 -matmul_transb 1x45x2xf64,235 -matmul_transb 1x45x3xf64,230 -matmul_transb 1x45x4xf64,252 -matmul_transb 1x45x5xf64,295 -matmul_transb 1x45x6xf64,347 -matmul_transb 1x45x7xf64,382 -matmul_transb 1x45x8xf64,434 -matmul_transb 1x45x9xf64,622 -matmul_transb 1x50x1xf64,237 -matmul_transb 1x50x2xf64,255 -matmul_transb 1x50x3xf64,250 -matmul_transb 1x50x4xf64,272 -matmul_transb 1x50x5xf64,320 -matmul_transb 1x50x6xf64,377 -matmul_transb 1x50x7xf64,417 -matmul_transb 1x50x8xf64,474 -matmul_transb 1x50x9xf64,682 -matmul_transb 1x55x1xf64,257 -matmul_transb 1x55x2xf64,275 -matmul_transb 1x55x3xf64,270 -matmul_transb 1x55x4xf64,282 -matmul_transb 1x55x5xf64,345 -matmul_transb 1x55x6xf64,407 -matmul_transb 1x55x7xf64,452 -matmul_transb 1x55x8xf64,514 -matmul_transb 1x55x9xf64,741 -matmul_transb 1x60x1xf64,277 -matmul_transb 1x60x2xf64,295 -matmul_transb 1x60x3xf64,290 -matmul_transb 1x60x4xf64,302 -matmul_transb 1x60x5xf64,370 -matmul_transb 1x60x6xf64,437 -matmul_transb 1x60x7xf64,487 -matmul_transb 1x60x8xf64,565 -matmul_transb 1x60x9xf64,801 -matmul_transb 1x65x1xf64,297 -matmul_transb 1x65x2xf64,315 -matmul_transb 1x65x3xf64,310 -matmul_transb 1x65x4xf64,322 -matmul_transb 1x65x5xf64,381 -matmul_transb 1x65x6xf64,467 -matmul_transb 1x65x7xf64,521 -matmul_transb 1x65x8xf64,605 -matmul_transb 1x65x9xf64,861 -matmul_transb 1x70x1xf64,317 -matmul_transb 1x70x2xf64,335 -matmul_transb 1x70x3xf64,330 -matmul_transb 1x70x4xf64,342 -matmul_transb 1x70x5xf64,406 -matmul_transb 1x70x6xf64,497 -matmul_transb 1x70x7xf64,556 -matmul_transb 1x70x8xf64,645 -matmul_transb 1x70x9xf64,921 -matmul_transb 1x75x1xf64,337 -matmul_transb 1x75x2xf64,355 -matmul_transb 1x75x3xf64,350 -matmul_transb 1x75x4xf64,362 -matmul_transb 1x75x5xf64,431 -matmul_transb 1x75x6xf64,517 -matmul_transb 1x75x7xf64,591 -matmul_transb 1x75x8xf64,685 -matmul_transb 1x75x9xf64,981 -matmul_transb 1x80x1xf64,357 -matmul_transb 1x80x2xf64,375 -matmul_transb 1x80x3xf64,370 -matmul_transb 1x80x4xf64,382 -matmul_transb 1x80x5xf64,456 -matmul_transb 1x80x6xf64,547 -matmul_transb 1x80x7xf64,626 -matmul_transb 1x80x8xf64,725 -matmul_transb 1x80x9xf64,1041 -matmul_transb 1x85x1xf64,377 -matmul_transb 1x85x2xf64,395 -matmul_transb 1x85x3xf64,390 -matmul_transb 1x85x4xf64,402 -matmul_transb 1x85x5xf64,481 -matmul_transb 1x85x6xf64,577 -matmul_transb 1x85x7xf64,661 -matmul_transb 1x85x8xf64,765 -matmul_transb 1x85x9xf64,1101 -matmul_transb 1x90x1xf64,397 -matmul_transb 1x90x2xf64,414 -matmul_transb 1x90x3xf64,410 -matmul_transb 1x90x4xf64,422 -matmul_transb 1x90x5xf64,510 -matmul_transb 1x90x6xf64,607 -matmul_transb 1x90x7xf64,696 -matmul_transb 1x90x8xf64,805 -matmul_transb 1x90x9xf64,1161 -matmul_transb 1x95x1xf64,417 -matmul_transb 1x95x2xf64,434 -matmul_transb 1x95x3xf64,430 -matmul_transb 1x95x4xf64,442 -matmul_transb 1x95x5xf64,535 -matmul_transb 1x95x6xf64,637 -matmul_transb 1x95x7xf64,731 -matmul_transb 1x95x8xf64,845 -matmul_transb 1x95x9xf64,1221 diff --git a/myrtle/test_output.json b/myrtle/test_output.json deleted file mode 100644 index 8e309ba1..00000000 --- a/myrtle/test_output.json +++ /dev/null @@ -1 +0,0 @@ -{"main$async_dispatch_7_matmul_transpose_b_1x600x400_f64": {"loop-order": [[2, 0], [0, 0], [1, 0]], "tile-sizes": [[1], [40], [100]], "dual-buffer": true}} \ No newline at end of file From 46e0a02949e3a51199f8fb672f5904ab1ef8ff21 Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Wed, 23 Jul 2025 13:08:04 +0200 Subject: [PATCH 5/8] clean up spacing, comments, etc --- .../src/Quidditch/Target/ConfigureTiles.cpp | 14 ++----- .../compiler/src/Quidditch/Target/Passes.td | 2 +- .../src/Quidditch/Target/QuidditchTarget.cpp | 5 +-- myrtle/myrtle.egg-info/PKG-INFO | 41 ------------------- myrtle/myrtle.egg-info/SOURCES.txt | 19 --------- myrtle/myrtle.egg-info/dependency_links.txt | 1 - myrtle/myrtle.egg-info/requires.txt | 7 ---- myrtle/myrtle.egg-info/top_level.txt | 1 - 8 files changed, 7 insertions(+), 83 deletions(-) delete mode 100644 myrtle/myrtle.egg-info/PKG-INFO delete mode 100644 myrtle/myrtle.egg-info/SOURCES.txt delete mode 100644 myrtle/myrtle.egg-info/dependency_links.txt delete mode 100644 myrtle/myrtle.egg-info/requires.txt delete mode 100644 myrtle/myrtle.egg-info/top_level.txt diff --git a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp index 06662503..9e373dae 100644 --- a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp +++ b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp @@ -71,18 +71,18 @@ setRootConfig(FunctionOpInterface funcOp, Operation *rootOp, if (search == tbl->end()) { funcOp.emitWarning() << "\nConfigureTiles: Root operation of this dispatch " - "is a missing tiling scheme!"; + "is a missing tiling scheme"; return failure(); } quidditch::TilingScheme &ts = search->second; if (!ts.getTiles_flat(l1Tiles)) { funcOp.emitWarning() << "\nConfigureTiles: Found tiling scheme, but " - "couldn't get l1 tile list!"; + "couldn't get l1 tile list"; return failure(); } if (!ts.getOrder_flat(l1Interchange)) { funcOp.emitWarning() << "\nConfigureTiles: Found tiling scheme, but " - "couldn't get l1 interchange!"; + "couldn't get l1 interchange"; return failure(); } dualBuffer = ts.getDualBuffer(); @@ -97,19 +97,14 @@ setRootConfig(FunctionOpInterface funcOp, Operation *rootOp, void ConfigureTiles::runOnOperation() { FunctionOpInterface funcOp = getOperation(); - - // TODO: un-comment out check for translationInfo, instead of blindly - // overwriting it. if (getTranslationInfo(funcOp)) return; SmallVector computeOps = getComputeOps(funcOp); FailureOr rootOp = getRootOperation(computeOps); - if (failed(rootOp)) { return signalPassFailure(); } - Operation *rootOperation = rootOp.value(); if (!rootOperation) { return; @@ -122,10 +117,9 @@ void ConfigureTiles::runOnOperation() { return signalPassFailure(); } - // annotate linalg ops with tile sizes + // Annotate root linalg ops with tile sizes auto loweringConfig = getLoweringConfig(rootOperation); - // only add the lowering config if one does not exist already if (!loweringConfig) { if (failed(setRootConfig(funcOp, rootOperation, tbl))) { funcOp.emitWarning() diff --git a/codegen/compiler/src/Quidditch/Target/Passes.td b/codegen/compiler/src/Quidditch/Target/Passes.td index a784956a..532ddbbf 100644 --- a/codegen/compiler/src/Quidditch/Target/Passes.td +++ b/codegen/compiler/src/Quidditch/Target/Passes.td @@ -41,7 +41,7 @@ def ConfigureTiles : InterfacePass<"quidditch-configure-tiles", "mlir::FunctionO Option<"importTiles", "import-tiles", "std::string", /*default=*/"", "Name of a JSON file specifying loop bounds and order for each root linalg operation.">, Option<"tablePointer", "NeverPassAValueHere", "std::uintptr_t", /*default=*/"0", - "Hacky way to prevent opening input file multiple times. Never pass a value to this option.">, + "Avoids opening the input file multiple times. Never pass a value to this option via the command line.">, ]; } diff --git a/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp b/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp index 46a23a34..0d69fa8b 100644 --- a/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp +++ b/codegen/compiler/src/Quidditch/Target/QuidditchTarget.cpp @@ -84,7 +84,7 @@ struct QuidditchTargetOptions { std::string xDSLOptPath; std::string toolChainRoot; bool assertCompiled = false; - std::string importTiles = ""; // added for Configure Tiles Pass + std::string importTiles = ""; // added for Configure Tiles Pass quidditch::TileInfoTbl tileInfo = quidditch::TileInfoTbl(); // added for Configure Tiles Pass // TODO: This should actually be 112640 but DMA stack overflows. Ooopsie! @@ -190,10 +190,9 @@ class QuidditchTargetBackend final : public IREE::HAL::TargetBackend { std::string errs; quidditch::fillTileInfoTable(&targetOptions.tileInfo, targetOptions.importTiles, errs); - } - // automatically tile the rest of the dispatches + // automatically tile the dispatches funcPassManager.addPass([&] { auto thePass = quidditch::createConfigureTiles( {targetOptions.importTiles, (std::uintptr_t)&targetOptions.tileInfo}); diff --git a/myrtle/myrtle.egg-info/PKG-INFO b/myrtle/myrtle.egg-info/PKG-INFO deleted file mode 100644 index 76a87e79..00000000 --- a/myrtle/myrtle.egg-info/PKG-INFO +++ /dev/null @@ -1,41 +0,0 @@ -Metadata-Version: 2.4 -Name: myrtle -Version: 0.1.0 -Summary: Tiling Cost Model for Snitch -Requires-Python: ==3.11.* -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: numpy==2.3.1 -Requires-Dist: pandas==2.2.2 -Requires-Dist: scikit-learn==1.7.0 -Requires-Dist: matplotlib>=3.10.1 -Requires-Dist: pyqt6==6.9.1 -Requires-Dist: pyqt6-qt6==6.9.1 -Requires-Dist: pyqt6-sip==13.10.2 -Dynamic: license-file - -# myrtle -tiling cost model for the snitch cluster! - -## query Myrtle for a tile size - -``` -python3 myrtle.py -``` - -where - -- `` is the name of the iree dispatch to tile, for example, `"main$async_dispatch_9_matmul_transpose_b_1x161x600_f64"` -- `` is the tile size selection mode, either - - `"sflt"` - simple filtering tile selection - - `"scyc"` - simple cycle count predicted tile selection - - `"svrcyc"` - SVR (support vector machine) cycle count predicted tile selection - -- `` full path to where myrtle should store its output - -### Example runs - -``` -python3 myrtle.py "main\$async_dispatch_7_matmul_transpose_b_1x600x400_f64" svrcyc test_output.json -``` - diff --git a/myrtle/myrtle.egg-info/SOURCES.txt b/myrtle/myrtle.egg-info/SOURCES.txt deleted file mode 100644 index 3f3ebec3..00000000 --- a/myrtle/myrtle.egg-info/SOURCES.txt +++ /dev/null @@ -1,19 +0,0 @@ -LICENSE -README.md -pyproject.toml -myrtle/__init__.py -myrtle/myrtle.py -myrtle.egg-info/PKG-INFO -myrtle.egg-info/SOURCES.txt -myrtle.egg-info/dependency_links.txt -myrtle.egg-info/requires.txt -myrtle.egg-info/top_level.txt -myrtle/graphing/__init__.py -myrtle/graphing/graph_utils.py -myrtle/graphing/graphing-refactored.py -myrtle/tile_SA/__init__.py -myrtle/tile_SA/quidditch_load_counting.py -myrtle/tile_SA/utils.py -myrtle/tile_gen/__init__.py -myrtle/tile_gen/peek_at_snitch_assembly.py -myrtle/tile_gen/tile_size_generator.py \ No newline at end of file diff --git a/myrtle/myrtle.egg-info/dependency_links.txt b/myrtle/myrtle.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/myrtle/myrtle.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/myrtle/myrtle.egg-info/requires.txt b/myrtle/myrtle.egg-info/requires.txt deleted file mode 100644 index 13b56eef..00000000 --- a/myrtle/myrtle.egg-info/requires.txt +++ /dev/null @@ -1,7 +0,0 @@ -numpy==2.3.1 -pandas==2.2.2 -scikit-learn==1.7.0 -matplotlib>=3.10.1 -pyqt6==6.9.1 -pyqt6-qt6==6.9.1 -pyqt6-sip==13.10.2 diff --git a/myrtle/myrtle.egg-info/top_level.txt b/myrtle/myrtle.egg-info/top_level.txt deleted file mode 100644 index 73847a0a..00000000 --- a/myrtle/myrtle.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -myrtle From 133df80a809291e4724930e112113ef20da0d635 Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Wed, 23 Jul 2025 13:18:19 +0200 Subject: [PATCH 6/8] remove nsnet2 changes --- runtime/samples/nsnet2/CMakeLists.txt | 5 ++--- runtime/samples/nsnet2/NsNet2.py | 3 --- runtime/samples/nsnet2/nsnet2_util.c | 10 ---------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/runtime/samples/nsnet2/CMakeLists.txt b/runtime/samples/nsnet2/CMakeLists.txt index 1e205ee9..4a3300a5 100644 --- a/runtime/samples/nsnet2/CMakeLists.txt +++ b/runtime/samples/nsnet2/CMakeLists.txt @@ -1,7 +1,6 @@ -set( MYRTLE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../myrtle/myrtle/myrtle.py) -iree_turbine(SRC NsNet2.py DST ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DTYPE "f64" M 5 N 6 K 7) -quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DST nsnet2 FLAGS --mlir-disable-threading --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/manually-chosen-tiles.json) +iree_turbine(SRC NsNet2.py DST ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DTYPE "f64") +quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc DST nsnet2 FLAGS --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/manually-chosen-tiles.json) quidditch_module(SRC ${CMAKE_CURRENT_BINARY_DIR}/nsnet2.mlirbc LLVM DST nsnet2_llvm FLAGS --iree-quidditch-import-tiles=${CMAKE_CURRENT_LIST_DIR}/manually-chosen-tiles.json) add_library(nsnet2_util nsnet2_util.c) diff --git a/runtime/samples/nsnet2/NsNet2.py b/runtime/samples/nsnet2/NsNet2.py index 2b7bd698..1d529c0d 100644 --- a/runtime/samples/nsnet2/NsNet2.py +++ b/runtime/samples/nsnet2/NsNet2.py @@ -23,9 +23,6 @@ parser = argparse.ArgumentParser(prog='iree-turbine') parser.add_argument('output', nargs='?') parser.add_argument('--frames', dest='frames', metavar='N', type=int, default=1, nargs='?') -parser.add_argument('--m', dest='mDim', metavar='Mdim', type=int, default=1, nargs='?') -parser.add_argument('--n', dest='nDim', metavar='Ndim', type=int, default=1, nargs='?') -parser.add_argument('--k', dest='kDim', metavar='Kdim', type=int, default=1, nargs='?') parser.add_argument('--dtype', dest='dtype', metavar='F', choices=['f32', 'f64'], default='f32') parser.add_argument('-dump', dest='dump', action='store_true', default=False) args = parser.parse_args() diff --git a/runtime/samples/nsnet2/nsnet2_util.c b/runtime/samples/nsnet2/nsnet2_util.c index 30d2f55a..874f5fe6 100644 --- a/runtime/samples/nsnet2/nsnet2_util.c +++ b/runtime/samples/nsnet2/nsnet2_util.c @@ -10,13 +10,6 @@ iree_status_t compiled_ns_net2_create(iree_vm_instance_t *, iree_allocator_t, iree_vm_module_t **); -// copied from Quidditch/snitch_cluster/sw/snRuntime/src/riscv.h -inline uint32_t snrt_mcycle() { - uint32_t register r; - asm volatile("csrr %0, mcycle" : "=r"(r) : : "memory"); - return r; -} - int run_nsnet2_experiment( iree_hal_executable_library_query_fn_t implementation) { if (!snrt_is_dm_core()) return quidditch_dispatch_enter_worker_loop(); @@ -46,15 +39,12 @@ int run_nsnet2_experiment( .output_sizes = (const iree_host_size_t[]){IREE_ARRAYSIZE(*data)}, }; - unsigned long cycles = snrt_mcycle(); IREE_CHECK_OK(run_model(&config)); - unsigned long cycles_after = snrt_mcycle(); for (int i = 0; i < IREE_ARRAYSIZE(*data); i++) { double value = (*data)[i]; printf("%f\n", value); } - printf("\ncycles %lu\n", cycles_after - cycles); free(data); return 0; } From 0275795b1cd2085efc8595551ddac16e5d0b5fef Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Wed, 23 Jul 2025 13:25:08 +0200 Subject: [PATCH 7/8] fix newlines --- .gitignore | 15 --------------- .../compiler/src/Quidditch/Target/TilingScheme.h | 2 +- runtime/samples/.gitignore | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 runtime/samples/.gitignore diff --git a/.gitignore b/.gitignore index d5d8384a..5d267c9d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,18 +4,3 @@ /build .ccls* */compile_commands.json - -comparing-tile-sizes/1x1200x400wm-n-k/* -comparing-tile-sizes/1x600x600wm-n-k/* -comparing-tile-sizes/1x400x161wm-n-k/* -comparing-tile-sizes/1x600x400wm-n-k/* -fakeNN/linear-layer-search-space/out/* -IGNORE/* -.vscode/* -Testing/* -build-notes/* -build/* -toolchain/* -fakeNN/* - -comparing-tiling-schemes/* \ No newline at end of file diff --git a/codegen/compiler/src/Quidditch/Target/TilingScheme.h b/codegen/compiler/src/Quidditch/Target/TilingScheme.h index 3ffe3829..a81c810c 100644 --- a/codegen/compiler/src/Quidditch/Target/TilingScheme.h +++ b/codegen/compiler/src/Quidditch/Target/TilingScheme.h @@ -61,4 +61,4 @@ bool parseListOfInts(llvm::json::Object *obj, std::string listName, std::vector &out, std::string &errs); bool parseBool(llvm::json::Object *obj, std::string listName, bool &out, std::string &errs); -} // namespace quidditch \ No newline at end of file +} // namespace quidditch diff --git a/runtime/samples/.gitignore b/runtime/samples/.gitignore deleted file mode 100644 index 422da9c0..00000000 --- a/runtime/samples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.json-exported.json \ No newline at end of file From df16d45a0248927b294b2ef63c860140588e489a Mon Sep 17 00:00:00 2001 From: EmilySillars Date: Wed, 23 Jul 2025 13:30:26 +0200 Subject: [PATCH 8/8] remove old manual tiling pass --- .../Quidditch/Target/ConfigureForSnitch.cpp | 134 ------------------ .../src/Quidditch/Target/ConfigureTiles.cpp | 2 +- 2 files changed, 1 insertion(+), 135 deletions(-) delete mode 100644 codegen/compiler/src/Quidditch/Target/ConfigureForSnitch.cpp diff --git a/codegen/compiler/src/Quidditch/Target/ConfigureForSnitch.cpp b/codegen/compiler/src/Quidditch/Target/ConfigureForSnitch.cpp deleted file mode 100644 index 16399e1f..00000000 --- a/codegen/compiler/src/Quidditch/Target/ConfigureForSnitch.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "Passes.h" - -#include "Quidditch/Dialect/Snitch/IR/QuidditchSnitchAttrs.h" -#include "iree/compiler/Codegen/Dialect/Codegen/IR/IREECodegenAttrs.h" -#include "iree/compiler/Codegen/Utils/CPUUtils.h" -#include "iree/compiler/Codegen/Utils/Utils.h" -#include "iree/compiler/Dialect/HAL/IR/HALOps.h" -#include "mlir/Dialect/MemRef/Transforms/Transforms.h" -#include "mlir/Interfaces/FunctionInterfaces.h" -#include "mlir/Transforms/GreedyPatternRewriteDriver.h" - -namespace quidditch { -#define GEN_PASS_DEF_CONFIGUREFORSNITCHPASS -#include "Quidditch/Target/Passes.h.inc" -} // namespace quidditch - -using namespace mlir; -using namespace mlir::iree_compiler; - -namespace { -class ConfigureForSnitch - : public quidditch::impl::ConfigureForSnitchPassBase { -public: - using Base::Base; - -protected: - void runOnOperation() override; -}; -} // namespace - -static LogicalResult setTranslationInfo(FunctionOpInterface funcOp) { - return setTranslationInfo( - funcOp, - IREE::Codegen::TranslationInfoAttr::get( - funcOp.getContext(), - IREE::Codegen::DispatchLoweringPassPipeline::None, SymbolRefAttr())); -} - -static LogicalResult setRootConfig(FunctionOpInterface funcOp, - Operation *rootOp) { - return TypeSwitch(rootOp) - .Case([&](linalg::LinalgOp op) { - // [0]: Always one in our matvec case. - - // [1]: How many rows we are processing. Should fit in L1. - // Should be as high as possible for subgroup distribution. - // Should be a multiple of 8 to be further distributed to compute cores. - - // [2]: Reduction dimension. How many columns are we - // processing at once? Cannot be distributed but has a few effects: - // * It allows us to make [1] larger by fitting more rows into L1. - // This therefore also gives us more parallelism compute core wise. - // * It makes our workgroups larger, reducing dispatch overhead and - // memory bandwidth (by only needing to copy loop invariant memory - // once + needing to copy back the result fewer times). This could - // come at the cost of concurrency for distributing workgroups but is - // only applicable once on Occamy. - SmallVector workgroupTiles(3, 0); - SmallVector l1Tiles(3, 0); - SmallVector l1Interchange = {2, 0, 1}; - bool dualBuffer = true; - - if (funcOp.getName() == - "main$async_dispatch_9_matmul_transpose_b_1x161x600_f64") { - l1Tiles[0] = 0; - l1Tiles[1] = 56; - l1Tiles[2] = 100; - } - if (funcOp.getName() == - "main$async_dispatch_0_matmul_transpose_b_1x400x161_f64") { - l1Tiles[1] = 40; - // TODO: Switch to 82 and true once correctness bugs are fixed. - l1Tiles[2] = 0; - dualBuffer = false; - } - if (funcOp.getName() == - "main$async_dispatch_7_matmul_transpose_b_1x600x400_f64") { - l1Tiles[0] = 0; - l1Tiles[1] = 40; - l1Tiles[2] = 100; - } - if (funcOp.getName() == - "main$async_dispatch_8_matmul_transpose_b_1x600x600_f64") { - l1Tiles[0] = 0; - l1Tiles[1] = 40; - l1Tiles[2] = 100; - } - if (funcOp.getName() == - "main$async_dispatch_1_matmul_transpose_b_1x1200x400_f64") { - l1Tiles[0] = 0; - l1Tiles[1] = 40; - l1Tiles[2] = 100; - } - - setLoweringConfig(rootOp, quidditch::Snitch::LoweringConfigAttr::get( - rootOp->getContext(), workgroupTiles, - l1Tiles, l1Interchange, dualBuffer)); - return success(); - }) - .Default(success()); -} - -void ConfigureForSnitch::runOnOperation() { - FunctionOpInterface funcOp = getOperation(); - if (getTranslationInfo(funcOp)) - return; - - SmallVector computeOps = getComputeOps(funcOp); - FailureOr rootOp = getRootOperation(computeOps); - if (failed(rootOp)) - return signalPassFailure(); - Operation *rootOperation = rootOp.value(); - if (!rootOperation) - return; - - // Set the same translation info for all functions right now. - // This should move into 'setRootConfig' if we gain different pass pipelines - // for different kernels. - if (failed(setTranslationInfo(funcOp))) - return signalPassFailure(); - - auto loweringConfig = - getLoweringConfig(rootOperation); - if (!loweringConfig) - if (failed(setRootConfig(funcOp, rootOperation))) - return signalPassFailure(); - - // The root configuration setting introduces `tensor.dim` operations. - // Resolve those away. - RewritePatternSet patterns(funcOp.getContext()); - memref::populateResolveRankedShapedTypeResultDimsPatterns(patterns); - if (failed(applyPatternsAndFoldGreedily(funcOp, std::move(patterns)))) - signalPassFailure(); -} diff --git a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp index 9e373dae..01047006 100644 --- a/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp +++ b/codegen/compiler/src/Quidditch/Target/ConfigureTiles.cpp @@ -137,4 +137,4 @@ void ConfigureTiles::runOnOperation() { "fold greedily failed\n"; signalPassFailure(); } -} \ No newline at end of file +}