* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file test_cost_model.cpp
* \brief
*/
#include "gtest/gtest.h"
#include <dlfcn.h>
#include "cost_model/simulation/backend.h"
#include "operator/models/llama/llama_def.h"
#include "cost_model/simulation/common/CommonType.h"
#include "tilefwk/tilefwk.h"
#include "interface/inner/tilefwk.h"
#include "cost_model/simulation/pv/PvModelFactory.h"
#include "interface/configs/config_manager.h"
#include "cost_model/simulation/arch/PipeFactory.h"
#include "cost_model/simulation/arch/CacheMachineImpl.h"
#include "cost_model/simulation/machine/CoreMachine.h"
#include "cost_model/simulation/machine/Scheduler.h"
#include "cost_model/simulation/tools/ParseInput.h"
#include "cost_model/simulation/arch/PipeSimulatorFast.h"
#include "cost_model/simulation/tools/visualizer.h"
#include "cost_model/simulation/base/Reporter.h"
#include "cost_model/simulation/statistics/TraceLogger.h"
#include "cost_model/simulation/common/CommonTools.h"
using namespace npu::tile_fwk;
class CostModelTest : public testing::Test {
public:
static void SetUpTestCase() {}
static void TearDownTestCase() {}
void SetUp() override
{
config::SetPlatformConfig(KEY_ENABLE_COST_MODEL, true);
config::SetSimConfig(KEY_BUILD_TASK_BASED_TOPO, true);
config::SetHostOption(COMPILE_STAGE, CS_EXECUTE_GRAPH);
Program::GetInstance().Reset();
}
void TearDown() override {}
};
void RunLLamaLayerCostModel(const AttentionDims& dimsCfg, float threadhold = 0.001f)
{
(void)threadhold;
int b = dimsCfg.b;
int n = dimsCfg.n;
int s = dimsCfg.s;
int d = dimsCfg.d;
PROGRAM("LLAMALAYER")
{
Tensor H(DataType::DT_FP32, {b * s, n * d}, "H");
Tensor AW(DataType::DT_FP16, {n * d, n * d * 3}, "AW");
Tensor DW(DataType::DT_FP16, {n * d, n * d}, "DW");
Tensor FW(DataType::DT_FP16, {n * d, n * d * 3}, "FW");
Tensor Res(DT_FP32, {b * s, n * d}, "Res");
config::SetBuildStatic(true);
FUNCTION("LLAMA", {H, AW, DW, FW, Res})
{
Res = LlamaLayer(H, AW, DW, FW, dimsCfg, SMALL_DFS_VEC_CFG, DFS_CUBE_CFG);
}
config::SetPassStrategy("OOO");
}
}
void RunMatrixCostModel()
{
int bs = 1;
int m = 32;
int k = 32;
int n = 32;
std::vector<int64_t> shapeA = {bs, m, k};
std::vector<int64_t> shapeB = {bs, k, n};
std::vector<int64_t> shapeC = {bs, m, n};
config::Reset();
TileShape::Current().SetCubeTile({32, 32}, {32, 32}, {32, 32});
Tensor matA(DT_FP16, shapeA, "MatA", TileOpFormat::TILEOP_NZ);
Tensor matB(DT_FP16, shapeB, "MatB", TileOpFormat::TILEOP_ND);
Tensor matC(DT_FP32, shapeC, "MatC");
config::SetBuildStatic(true);
FUNCTION("BATCHMATMUL", {matA, matB, matC})
{
config::SetPassConfig("PVC2_OOO", "OoOSchedule", KEY_DISABLE_PASS, true);
matC = npu::tile_fwk::Matrix::BatchMatmul(DT_FP32, matA, matB, false, false);
}
}
void RunAttentionPostCostModel()
{
config::SetHostOption(COMPILE_STAGE, CS_EXECUTE_GRAPH);
int b = 1;
int n = 2;
int s = 128;
int d = 512;
int v_head = 128;
int h = 256;
std::vector<int64_t> inShape = {b, n, s, d};
Tensor attnPostIn(DT_FP32, inShape, "attnPostIn");
Tensor kvBProjWV(DT_FP32, {n, d, v_head}, "kvBProjWV");
Tensor oProjW(DT_FP32, {n * v_head, h}, "oProjW");
Tensor atten_output;
ConfigManager::Instance();
FUNCTION("AttentionPost")
{
int new_b = attnPostIn.GetShape()[0];
int new_n = attnPostIn.GetShape()[1];
int new_s = attnPostIn.GetShape()[2];
DataType dType = attnPostIn.GetStorage()->Datatype();
TileShape::Current().SetVecTile({1, 1, 32, d});
Tensor atten_res1 = Reshape(Transpose(attnPostIn, {1, 2}), {new_b * new_s, new_n, d});
TileShape::Current().SetVecTile({32, 1, d});
Tensor atten_res2 = Transpose(atten_res1, {0, 1});
TileShape::Current().SetVecTile(128, 128);
TileShape::Current().SetCubeTile({32, 32}, {128, 128}, {128, 128});
Tensor mm7_res = Matrix::BatchMatmul(dType, atten_res2, kvBProjWV);
TileShape::Current().SetVecTile({1, 128, 128});
Tensor mm7_res1 = Transpose(mm7_res, {0, 1});
Tensor mm7_res2 = Reshape(mm7_res1, {new_b, new_s, new_n * v_head});
Tensor attn_out_w = Unsqueeze(oProjW, 0);
atten_output = Matrix::BatchMatmul(dType, mm7_res2, attn_out_w);
}
}
TEST_F(CostModelTest, TestAttentionPostAccuracy1)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
RunAttentionPostCostModel();
}
TEST_F(CostModelTest, TestAttentionPostAccuracyFromJson)
{
config::SetPlatformConfig(KEY_ENABLE_COST_MODEL, false);
RunAttentionPostCostModel();
std::string jPath = config::LogTopFolder() + "/program.json";
config::SetPlatformConfig(KEY_ENABLE_COST_MODEL, true);
config::SetSimConfig(KEY_AGENT_JSON_PATH, jPath);
CostModelAgent costModelAgent;
costModelAgent.SubmitToCostModel(nullptr);
costModelAgent.RunCostModel();
costModelAgent.TerminateCostModel();
}
TEST_F(CostModelTest, TestGenCalendarSchedule)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
std::vector<std::string> arg = config::GetSimConfig(KEY_ARGS, std::vector<std::string>{});
arg.emplace_back("Model.genCalendarScheduleCpp=true");
config::SetSimConfig(KEY_ARGS, arg);
RunAttentionPostCostModel();
}
TEST_F(CostModelTest, TestAttentionPostCVMIXMode)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
std::vector<std::string> arg = config::GetSimConfig(KEY_ARGS, std::vector<std::string>{});
arg.emplace_back("Model.cubeVecMixMode=true");
config::SetSimConfig(KEY_ARGS, arg);
RunAttentionPostCostModel();
}
TEST_F(CostModelTest, TestAttentionPostSimulationSchedule)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
std::vector<std::string> arg;
arg.emplace_back("Model.statisticReportToFile=true");
arg.emplace_back("Model.deviceArch=A2A3");
arg.emplace_back("Model.useOOOPassSeq=false");
config::SetSimConfig(KEY_ARGS, arg);
RunAttentionPostCostModel();
}
TEST_F(CostModelTest, TestAttentionPostFunctional)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_SIM_MODE, int(CostModel::SimMode::EMULATOR));
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
RunAttentionPostCostModel();
}
TEST_F(CostModelTest, TestErrorInput)
{
std::string name = "TEST";
auto newFunc = std::make_shared<Function>(npu::tile_fwk::Program::GetInstance(), name, name, nullptr);
std::vector<int64_t> shape = {1, 1};
auto outcast = std::make_shared<LogicalTensor>(*newFunc, DT_FP32, shape);
newFunc->outCasts_.push_back(outcast);
newFunc->inCasts_.push_back(outcast);
CostModelAgent costModelAgent;
costModelAgent.SubmitToCostModel(newFunc.get());
}
TEST_F(CostModelTest, TestFixedLatencyTasks)
{
RunAttentionPostCostModel();
std::string jsonPath("./config/fixed_task_topo.json");
std::vector<std::string> arg = config::GetSimConfig(KEY_ARGS, std::vector<std::string>{});
arg.emplace_back("Model.simulationFixedLatencyTask=true");
arg.emplace_back("Model.fixedLatencyTaskInfoPath=" + jsonPath);
config::SetSimConfig(KEY_ARGS, arg);
std::string jPath = config::LogTopFolder() + "/program.json";
config::SetSimConfig(KEY_AGENT_JSON_PATH, jPath);
CostModelAgent costModelAgent;
costModelAgent.SubmitToCostModel(nullptr);
costModelAgent.RunCostModel();
costModelAgent.TerminateCostModel();
}
TEST_F(CostModelTest, TestAttentionPostL2Cache)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
std::vector<std::string> arg;
arg.emplace_back("Model.statisticReportToFile=false");
arg.emplace_back("Model.deviceArch=A2A3");
arg.emplace_back("Model.mteUseL2Cache=true");
config::SetSimConfig(KEY_ARGS, arg);
RunAttentionPostCostModel();
}
TEST_F(CostModelTest, TestBuildBasedOnConfigs)
{
std::string configPath("./config/test_config.conf");
std::vector<std::string> configs;
configs.push_back("--conf");
configs.push_back(configPath);
CostModelAgent costModelAgent;
costModelAgent.costModel = std::make_shared<CostModel::CostModelInterface>();
costModelAgent.costModel->BuildCostModel(configs);
configs.clear();
configs.push_back("-m");
configs.push_back("1");
configs.push_back("--conf");
configs.push_back(configPath);
costModelAgent.costModel = std::make_shared<CostModel::CostModelInterface>();
costModelAgent.costModel->BuildCostModel(configs);
}
TEST_F(CostModelTest, TestCoreMachineDeadlock)
{
int accuracylevel = 1;
config::SetSimConfig(KEY_ACCURACY_LEVEL, accuracylevel);
std::vector<std::string> arg = config::GetSimConfig(KEY_ARGS, std::vector<std::string>{});
arg.emplace_back("Model.testDeadLock=true");
arg.emplace_back("Core.bufferBackPressure=true");
arg.emplace_back("Pipe.ubSizeThreshold=256");
arg.emplace_back("Pipe.l1SizeThreshold=256");
arg.emplace_back("Pipe.l0aSizeThreshold=256");
arg.emplace_back("Pipe.l0bSizeThreshold=256");
arg.emplace_back("Pipe.l0cSizeThreshold=256");
config::SetSimConfig(KEY_ARGS, arg);
RunAttentionPostCostModel();
}
void RunCat()
{
TileShape::Current().SetVecTile(16, 6, 6, 16);
config::SetHostOption(COMPILE_STAGE, CS_EXECUTE_GRAPH);
std::vector<int64_t> shape1 = {10, 10, 10, 10};
std::vector<int64_t> shape2 = {20, 10, 10, 10};
int axis = 0;
Tensor params1(DT_FP32, shape1, "params1");
Tensor params2(DT_FP32, shape2, "params2");
Tensor res;
FUNCTION("A") { res = Cat(std::vector<Tensor>{params1, params2}, axis); }
}
TEST_F(CostModelTest, TestGlobalCalendar)
{
std::string jsonPath("./config/global.calendar.json");
std::string inputPath("./config/fixed_task_topo.json");
CostModel::CalendarMode calendarMode = CostModel::CalendarMode::GLOBAL_COUNTER;
std::vector<std::string> arg;
arg.emplace_back("Model.simulationFixedLatencyTask=true");
arg.emplace_back("Model.fixedLatencyTaskInfoPath=" + inputPath);
arg.emplace_back("Model.calendarFile=" + jsonPath);
arg.emplace_back("Model.calendarMode=" + std::to_string(static_cast<int>(calendarMode)));
config::SetSimConfig(KEY_ARGS, arg);
RunCat();
}
TEST_F(CostModelTest, TestLeafFunctionMode)
{
config::SetSimConfig(KEY_SIM_MODE, int(CostModel::SimMode::LEAF_FUNCTION));
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
std::vector<std::string> arg;
arg.emplace_back("Model.deviceArch=A2A3");
arg.emplace_back("Model.statisticReportToFile=false");
config::SetSimConfig(KEY_ARGS, arg);
RunAttentionPostCostModel();
}
class CostModelDynTest : public testing::Test {
public:
static void SetUpTestCase() {}
static void TearDownTestCase() {}
void SetUp() override
{
cacheEnable = config::GetPassGlobalConfig(KEY_ENABLE_BINARY_CACHE, false);
config::SetPassGlobalConfig(KEY_ENABLE_BINARY_CACHE, false);
oriEnableAihacBackend = config::GetPlatformConfig(KEY_ENABLE_AIHAC_BACKEND, oriEnableAihacBackend);
config::SetPlatformConfig(KEY_ENABLE_AIHAC_BACKEND, true);
Program::GetInstance().Reset();
}
void TearDown() override
{
config::SetPassGlobalConfig(KEY_ENABLE_BINARY_CACHE, cacheEnable);
config::SetPlatformConfig(KEY_ENABLE_AIHAC_BACKEND, oriEnableAihacBackend);
}
protected:
bool oriEnableAihacBackend = false;
bool cacheEnable = false;
};
void CostModelTestLoopViewAssemble(const Tensor& t0, const Tensor& t1, const Tensor& blockTable, Tensor& out, int s)
{
FUNCTION("main", {t0, t1, blockTable}, {out})
{
LOOP("L0", FunctionType::DYNAMIC_LOOP, i, LoopRange(GetInputShape(t0, 0) / s))
{
SymbolicScalar idx = GetTensorData(blockTable, {i, 0});
Tensor t0s = View(t0, {s, s}, {idx * s, 0});
Tensor qi(DT_FP32, {s, 2 * s}, "qi");
Assemble(t1, {0, 0}, qi);
Assemble(t0s, {0, s}, qi);
Tensor ki(DT_FP32, {s, 2 * s}, "ki");
Assemble(t0s, {0, 0}, ki);
Assemble(t1, {0, s}, ki);
Tensor t2 = Matrix::Matmul(DataType::DT_FP32, qi, ki, false, true);
Assemble(t2, {idx * s, 0}, out);
}
}
}
TEST_F(CostModelDynTest, TestDD)
{
constexpr int tilingX = 32;
constexpr int tilingY = 32;
TileShape::Current().SetVecTile(tilingX, tilingY);
constexpr int tilingM = 32;
constexpr int tilingN = 32;
constexpr int tilingK = 32;
TileShape::Current().SetCubeTile({tilingM, tilingM}, {tilingN, tilingN}, {tilingK, tilingK});
std::vector<uint8_t> devProgBinary;
int s = 32;
int n = 8;
Tensor t0(DT_FP32, {n * s, s}, "t0");
Tensor t1(DT_FP32, {s, s}, "t1");
Tensor blockTable{DT_INT32, {n, 1}, "blockTable"};
Tensor out(DT_FP32, {n * s, s}, "out");
CostModelTestLoopViewAssemble(t0, t1, blockTable, out, s);
auto func = Program::GetInstance().GetLastFunction();
auto pv = CostModel::PvModelFactory::CreateDyn();
pv->Codegen(func);
}
TEST_F(CostModelTest, TestUnknownArchType)
{
EXPECT_THROW(CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_MTE_IN, "A0"), std::invalid_argument);
}
TEST_F(CostModelTest, TestCreateA5Cache)
{
std::unique_ptr<CostModel::CacheMachineImpl> cacheImpl =
CostModel::PipeFactory::CreateCache(CostModel::CacheType::L2CACHE, "A5");
CostModel::CachePacket packet;
cacheImpl->Simulate(packet);
}
TEST_F(CostModelTest, TestA5ArchType)
{
auto simulator = CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_MTE_IN, "A5");
EXPECT_TRUE(simulator != nullptr);
}
TEST_F(CostModelTest, TestPipeFactoryCreateA2A3)
{
auto sim = CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_MTE_IN, "A2A3");
EXPECT_TRUE(sim != nullptr);
auto sim2 = CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_VECTOR_ALU, "A2A3");
EXPECT_TRUE(sim2 != nullptr);
auto sim3 = CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_CUBE, "A2A3");
EXPECT_TRUE(sim3 != nullptr);
auto sim4 = CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_TILE_ALLOC, "A2A3");
EXPECT_TRUE(sim4 != nullptr);
auto sim5 = CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_CALL, "A2A3");
EXPECT_TRUE(sim5 != nullptr);
}
TEST_F(CostModelTest, TestPipeFactoryCreateUnknownPipeType)
{
EXPECT_THROW(CostModel::PipeFactory::Create(CostModel::CorePipeType::PIPE_UNKNOW, "A2A3"), std::invalid_argument);
}
TEST_F(CostModelTest, TestPipeFactoryCreateCacheA2A3)
{
auto cacheImpl = CostModel::PipeFactory::CreateCache(CostModel::CacheType::L2CACHE, "A2A3");
EXPECT_TRUE(cacheImpl != nullptr);
}
TEST_F(CostModelTest, TestPipeFactoryCreateCacheUnknownArch)
{
EXPECT_THROW(CostModel::PipeFactory::CreateCache(CostModel::CacheType::L2CACHE, "A0"), std::invalid_argument);
}
TEST_F(CostModelTest, TestPipeFactoryCreateCacheUnknownType)
{
EXPECT_THROW(CostModel::PipeFactory::CreateCache(CostModel::CacheType::FUNCTION_CACHE, "A2A3"), std::invalid_argument);
}
TEST_F(CostModelTest, TestCoreMachineDeadlock2)
{
CostModel::CoreMachine* coreMachine = new CostModel::CoreMachine(CostModel::MachineType::AIC);
std::set<int> unissuedTileMagics;
coreMachine->sim = std::make_shared<CostModel::SimSys>();
unissuedTileMagics.insert(1);
unissuedTileMagics.insert(2);
coreMachine->tileOps[1] = std::make_shared<CostModel::TileOp>();
coreMachine->tileOps[2] = std::make_shared<CostModel::TileOp>();
coreMachine->tileOps[1]->magic = 1;
coreMachine->tileOps[2]->magic = 2;
coreMachine->tileOps[1]->opcode = "";
coreMachine->tileOps[2]->opcode = "";
coreMachine->tiles[1] = std::make_shared<CostModel::Tile>();
coreMachine->tiles[2] = std::make_shared<CostModel::Tile>();
coreMachine->tiles[1]->magic = 1;
coreMachine->tiles[2]->magic = 2;
coreMachine->aliveBuffer[CostModel::CorePipeType::PIPE_CUBE_BMU_L1].insert(1);
coreMachine->aliveBuffer[CostModel::CorePipeType::PIPE_CUBE_BMU_L0A].insert(2);
CostModel::ReadyQueue readyQueue1(CostModel::CorePipeType::PIPE_CUBE_BMU_L1, 0);
readyQueue1.Insert(1);
coreMachine->readyQueues.push_back(readyQueue1);
CostModel::ReadyQueue readyQueue2(CostModel::CorePipeType::PIPE_CUBE_BMU_L0A, 1);
readyQueue2.Insert(2);
coreMachine->readyQueues.push_back(readyQueue2);
coreMachine->executingTaskId = 123;
coreMachine->executingFunctionHash = 456;
try {
coreMachine->AnalysisDeadlock(unissuedTileMagics);
} catch (const std::exception& e) {
EXPECT_TRUE(true);
}
try {
coreMachine->CheckDeadlock();
} catch (const std::exception& e) {
EXPECT_TRUE(true);
}
coreMachine->sim->ReportDeadlock(1);
delete coreMachine;
}
TEST_F(CostModelTest, TestScheduler)
{
using namespace CostModel;
CostModel::Scheduler scheduler;
scheduler.sim = std::make_shared<CostModel::SimSys>();
std::unordered_map<int, CostModel::TilePtr> tiles;
std::unordered_map<int, CostModel::TileOpPtr> tileOps;
std::vector<std::vector<int>> tileAllocSequence(static_cast<int>(CorePipeType::TOTAL_CORE_PIPE_TYPE));
auto t10 = std::make_shared<CostModel::Tile>();
t10->magic = 10;
t10->exeInfo.domCount = 5;
t10->pipeType = CostModel::CorePipeType::PIPE_MTE1;
auto t11 = std::make_shared<CostModel::Tile>();
t11->magic = 11;
t11->exeInfo.domCount = 1;
t11->pipeType = CostModel::CorePipeType::PIPE_MTE1;
auto op100 = std::make_shared<CostModel::TileOp>();
op100->magic = 100;
op100->pipeType = CorePipeType::PIPE_VECTOR_BMU;
auto t30 = std::make_shared<CostModel::Tile>();
t30->magic = 30;
t30->exeInfo.isOutcast = true;
t30->pipeType = CostModel::CorePipeType::PIPE_MTE1;
auto t40 = std::make_shared<CostModel::Tile>();
t40->magic = 40;
t40->pipeType = CostModel::CorePipeType::PIPE_MTE1;
op100->iOperand = {t10, t11};
op100->oOperand = {t30, t40};
t10->consumers = {op100};
t11->consumers = {op100};
t30->producers = {op100};
t40->producers = {op100};
tiles[10] = t10;
tiles[11] = t11;
tiles[30] = t30;
tiles[40] = t40;
tileOps[100] = op100;
scheduler.SortTile(tiles, tileOps, tileAllocSequence);
EXPECT_GT(op100->exeInfo.sequenceToIssue, -1);
EXPECT_EQ(t10->exeInfo.copyOutIdx, t11->exeInfo.copyOutIdx);
}
TEST_F(CostModelTest, TestScheduler_EmptyInput)
{
std::unordered_map<int, CostModel::TilePtr> tiles;
std::unordered_map<int, CostModel::TileOpPtr> tileOps;
std::vector<std::vector<int>> seq;
CostModel::Scheduler scheduler;
scheduler.sim = std::make_shared<CostModel::SimSys>();
scheduler.SortTile(tiles, tileOps, seq);
}
TEST_F(CostModelTest, TestRemoveBarrierCounter_LogCoverage)
{
using namespace CostModel;
GenCalendar calendar;
std::vector<uint64_t> srcIds;
for (uint64_t i = 1; i <= 11; ++i) {
srcIds.push_back(i);
calendar.taskTopoInfo[i] = CalendarEntry{};
}
for (uint64_t j = 100; j < 110; ++j) {
CalendarEntry info;
info.waitSrcTaskIds = srcIds;
calendar.taskTopoInfo[j] = info;
}
calendar.RemoveBarrierCounter();
uint64_t firstTargetId = 100;
EXPECT_TRUE(calendar.taskTopoInfo[firstTargetId].waitSrcTaskIds.empty());
EXPECT_FALSE(calendar.taskTopoInfo[firstTargetId].waitBarrierCounterIds.empty());
EXPECT_EQ(calendar.taskTopoInfo[firstTargetId].waitBarrierCounterIds[0].first, 100);
}
TEST_F(CostModelTest, TestGetPipeType_AssertOnMissingOpcode)
{
using namespace CostModel;
TileOp op;
op.opcode = "UNKNOWN_OP";
try {
op.GetPipeType();
} catch (const std::exception& e) {
}
op.pipeType = CorePipeType::PIPE_UNKNOW;
try {
op.GetAddress();
} catch (const std::exception& e) {
}
try {
op.GetSize();
} catch (const std::exception& e) {
}
}
TEST_F(CostModelTest, TestCheckTileOp)
{
using namespace CostModel;
ParseInput parser;
auto func = std::make_shared<CostModel::Function>();
func->funcName = "TestFunc";
auto op = std::make_shared<TileOp>();
op->opcode = "ADD";
op->iOperand = {};
op->oOperand = {};
func->tileOps.push_back(op);
EXPECT_NO_THROW(parser.CheckTileOp(func));
}
TEST_F(CostModelTest, TestCheckTile)
{
using namespace CostModel;
ParseInput parser;
auto func = std::make_shared<CostModel::Function>();
auto tile1 = std::make_shared<Tile>();
tile1->magic = 101;
tile1->producers = {};
auto tile2 = std::make_shared<Tile>();
tile2->magic = 202;
tile2->consumers = {};
func->tiles.push_back(tile1);
func->tiles.push_back(tile2);
parser.CheckTile(func);
}
TEST_F(CostModelTest, TestParseInputFile)
{
using namespace CostModel;
std::vector<std::string> cfg;
const std::string path = "1";
std::deque<TaskMap> deque;
std::unordered_map<long unsigned int, std::deque<CostModel::ReplayTaskEntry>> map;
ParseInput parser;
parser.ParseJsonConfig(path, cfg);
parser.ParseConfig(path, cfg);
parser.ParseCalendarJson(nullptr, path);
parser.ParseFixedLatencyTask(nullptr, path);
parser.ParseTopoJson(path, deque);
parser.ParseReplayInfoJson(path, map);
parser.ParseJson(nullptr, path);
}
TEST_F(CostModelTest, TestParseJsonWithValidFile)
{
std::string jsonPath = "/tmp/parse_json_test_" + std::to_string(getpid()) + ".json";
std::ofstream ofs(jsonPath);
ofs << R"({"functions":[]})";
ofs.close();
auto sim = std::make_shared<CostModel::SimSys>();
CostModel::ParseInput parser;
parser.ParseJson(sim, jsonPath);
unlink(jsonPath.c_str());
}
TEST_F(CostModelTest, TestParseSingleFunction)
{
auto sim = std::make_shared<CostModel::SimSys>();
std::string name = "TestParseSingleFunc";
auto func = std::make_shared<Function>(npu::tile_fwk::Program::GetInstance(), name, name, nullptr);
std::vector<int64_t> shape = {1, 1};
auto incast = std::make_shared<LogicalTensor>(*func, DT_FP32, shape);
auto outcast = std::make_shared<LogicalTensor>(*func, DT_FP32, shape);
func->inCasts_.push_back(incast);
func->outCasts_.push_back(outcast);
CostModel::ParseInput parser;
parser.ParseSingleFunction(sim, func.get());
EXPECT_NE(sim, nullptr);
}
TEST_F(CostModelTest, TestJsonFErrororFormat)
{
using namespace CostModel;
const std::string path = "./config/test_config.conf";
CostModelAgent agent;
try {
agent.GetFunctionFromJson(path);
} catch (const std::exception& e) {
}
}
TEST_F(CostModelTest, TestGetCyclesForPassA2A3)
{
const std::string opCode = "ADD";
std::vector<std::vector<int>> shape = {{1, 1, 1, 1}};
DataType dtype = DataType::DT_INT4;
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
int64_t cycle = CostModel::GetCyclesForPass(opCode, shape, dtype);
EXPECT_GT(cycle, 0);
}
TEST_F(CostModelTest, TestGetCyclesForPassA5)
{
const std::string opCode = "CAST";
std::vector<std::vector<int>> shape = {{1, 1, 1, 1}};
DataType dtype = DataType::DT_INT4;
config::SetPlatformConfig("device_platform", "ASCEND_950PR_9579");
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
int64_t cycle = CostModel::GetCyclesForPass(opCode, shape, dtype);
EXPECT_GT(cycle, 0);
}
TEST_F(CostModelTest, TestGetCyclesForPassCopyIn)
{
const std::string opCode = "COPY_IN";
std::vector<std::vector<int>> shape = {{1, 1, 1, 1}};
DataType dtype = DataType::DT_INT4;
config::SetPlatformConfig("device_platform", "ASCEND_950PR_9579");
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
int64_t cycle = CostModel::GetCyclesForPass(opCode, shape, dtype);
EXPECT_GT(cycle, 0);
}
TEST_F(CostModelTest, TestGetCyclesForPassCopyOut)
{
const std::string opCode = "COPY_OUT";
std::vector<std::vector<int>> shape = {{1, 1, 1, 1}};
DataType dtype = DataType::DT_INT4;
config::SetPlatformConfig("device_platform", "ASCEND_950PR_9579");
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
int64_t cycle = CostModel::GetCyclesForPass(opCode, shape, dtype);
EXPECT_GT(cycle, 0);
}
TEST_F(CostModelTest, TestGetCyclesForPassSimulate)
{
const std::string opCode = "WHERE_TT";
std::vector<std::vector<int>> shape = {{1, 1, 1, 1}};
DataType dtype = DataType::DT_INT4;
config::SetPlatformConfig("device_platform", "ASCEND_950PR_9579");
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
int64_t cycle = CostModel::GetCyclesForPass(opCode, shape, dtype);
EXPECT_GT(cycle, 0);
}
TEST_F(CostModelTest, TestGetCyclesForPassSo)
{
typedef int64_t (*GetCyclesForPassFunc)(
const std::string& op, const std::vector<std::vector<int>>& shape, DataType dtype);
const std::string opCode = "L1_TO_L0A";
std::vector<std::vector<int>> shape = {{1, 1, 1, 1}};
DataType dtype = DataType::DT_INT4;
config::SetPlatformConfig("device_platform", "ASCEND_950PR_9579");
config::SetSimConfig(KEY_ACCURACY_LEVEL, 1);
std::string soPath = "libtile_fwk_simulation.so";
void* handle = dlopen(soPath.c_str(), RTLD_LAZY);
EXPECT_NO_THROW(if (!handle) { throw std::runtime_error("can not load library: " + std::string(dlerror())); });
GetCyclesForPassFunc get_cycles_func = (GetCyclesForPassFunc)dlsym(handle, "GetCyclesForPass");
EXPECT_NO_THROW(if (!get_cycles_func) {
throw std::runtime_error("Failed to find symbol GetCyclesForPass: " + std::string(dlerror()));
});
int64_t cycle = get_cycles_func(opCode, shape, dtype);
EXPECT_GT(cycle, 0);
}
TEST_F(CostModelTest, TestMachineName)
{
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::DEVICE), "DEVICE");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::CPU), "AICPU");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::AIC), "AIC");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::AIV), "AIV");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::MIXAICORE), "MIXAICORE");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::PIPE), "PIPE");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::CACHE), "CACHE");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::HUB), "HUB");
EXPECT_EQ(CostModel::MachineName(CostModel::MachineType::UNKNOWN), "ILLEGAL");
}
TEST_F(CostModelTest, TestCacheName)
{
EXPECT_EQ(CostModel::CacheName(CostModel::CacheType::FUNCTION_CACHE), "FunctionCache");
EXPECT_EQ(CostModel::CacheName(CostModel::CacheType::L2CACHE), "L2CACHE");
EXPECT_EQ(CostModel::CacheName(CostModel::CacheType::TOTAL_CACHE_TYPE), "ILLEGAL");
}
TEST_F(CostModelTest, TestCacheRequestName)
{
EXPECT_EQ(CostModel::CacheRequestName(CostModel::CacheRequestType::FUNCTION_REQ), "Function_Read");
EXPECT_EQ(CostModel::CacheRequestName(CostModel::CacheRequestType::DATA_READ_REQ), "Data_Read");
EXPECT_EQ(CostModel::CacheRequestName(CostModel::CacheRequestType::DATA_WRITE_REQ), "Data_Write");
}
TEST_F(CostModelTest, TestTileStringConstructor)
{
std::string jsonStr = R"({"magic":42,"shape":[1,2,3],"offset":[0,1,2],"memorytype":{"tobe":"MEM_UB"},"nodetype":"LOCAL","rawtensor":{"symbol":"x","datatype":"DT_FP16","rawmagic":99,"rawshape":[4,5,6]}})";
CostModel::Tile tile(jsonStr);
EXPECT_EQ(tile.magic, 42);
EXPECT_EQ(tile.shape, std::vector<int>({1, 2, 3}));
EXPECT_EQ(tile.offset, std::vector<int>({0, 1, 2}));
EXPECT_EQ(tile.bufType, CostModel::OperandType::BUF_UB);
}
TEST_F(CostModelTest, TestGetOpSequeceAfterOOO)
{
CostModel::Function func;
func.opSequenceAfterOOO_[10] = 100;
uint64_t index = 0;
func.GetOpSequeceAfterOOO(10, index);
EXPECT_EQ(index, 100);
uint64_t index2 = 5;
func.GetOpSequeceAfterOOO(20, index2);
EXPECT_EQ(index2, 5);
}
TEST_F(CostModelTest, TestGetOpRelativeReadyCycle)
{
CostModel::Function func;
func.startCycles = 10;
auto tileOp = std::make_shared<CostModel::TileOp>();
tileOp->exeInfo.cycleInfo.executeStartCycle = 50;
tileOp->pipeType = CostModel::CorePipeType::PIPE_VECTOR_BMU;
func.pipeLastEndCycle[CostModel::CorePipeType::PIPE_VECTOR_BMU] = 60;
uint64_t result = func.GetOpRelativeReadyCycle(tileOp, 20);
EXPECT_GE(result, 60);
}
TEST_F(CostModelTest, TestCalculateRelativeCycle)
{
CostModel::Function func;
func.startCycles = 10;
auto tileOp = std::make_shared<CostModel::TileOp>();
tileOp->exeInfo.cycleInfo.executeStartCycle = 50;
tileOp->exeInfo.cycleInfo.executeEndCycle = 100;
tileOp->pipeType = CostModel::CorePipeType::PIPE_VECTOR_ALU;
func.opMagicSequence.push_back(1);
func.tileOpMap[1] = tileOp;
func.CalculateRelativeCycle(20, 1.0);
EXPECT_EQ(tileOp->exeInfo.cycleInfo.relativeStartCycle, 60);
EXPECT_EQ(tileOp->exeInfo.cycleInfo.relativeEndCycle, 110);
}
TEST_F(CostModelTest, TestParseJsonWithValidData)
{
std::string jsonPath = "/tmp/parse_json_data_test_" + std::to_string(getpid()) + ".json";
std::ofstream ofs(jsonPath);
ofs << R"({
"functions": [{
"hash": "12345",
"magic": 1,
"magicname": "test_func_START",
"operations": [{
"opcode": "ADD",
"opmagic": 10,
"ioperands": [{"magic":100,"shape":[1,1],"offset":[0,0],"memorytype":{"tobe":"MEM_UB"},"nodetype":"LOCAL","rawtensor":{"symbol":"x","datatype":"DT_FP16","rawmagic":200,"rawshape":[1,1]}}],
"ooperands": [{"magic":101,"shape":[1,1],"offset":[0,0],"memorytype":{"tobe":"MEM_UB"},"nodetype":"OUTCAST","rawtensor":{"symbol":"y","datatype":"DT_FP16","rawmagic":201,"rawshape":[1,1]}}]
}]
}]
})";
ofs.close();
auto sim = std::make_shared<CostModel::SimSys>();
sim->config.startFunctionLabel = "START";
CostModel::ParseInput parser;
parser.ParseJson(sim, jsonPath);
EXPECT_EQ(sim->startFuncName, "test_func_START");
EXPECT_EQ(sim->startFuncHash, 12345);
unlink(jsonPath.c_str());
}
TEST_F(CostModelTest, TestParseTopoJsonWithValidData)
{
std::string topoPath = "/tmp/parse_topo_test_" + std::to_string(getpid()) + ".json";
std::ofstream ofs(topoPath);
ofs << R"([{"seqNo":0,"taskId":1,"leafIndex":0,"opmagic":10,"psgId":-1,"rootIndex":0,"uniqueKey":1,"funcHash":123,"coreType":"AIV","successors":[]}])";
ofs.close();
std::deque<CostModel::TaskMap> deque;
CostModel::ParseInput parser;
parser.ParseTopoJson(topoPath, deque);
EXPECT_EQ(deque.size(), 1);
unlink(topoPath.c_str());
}
TEST_F(CostModelTest, TestParseReplayInfoJsonWithValidData)
{
std::string replayPath = "/tmp/parse_replay_test_" + std::to_string(getpid()) + ".json";
std::ofstream ofs(replayPath);
ofs << R"([{"blockIdx":0,"coreType":"AIV","tasks":[{"seqNo":1,"taskId":100,"execStart":10,"execEnd":20}]}])";
ofs.close();
std::unordered_map<uint64_t, std::deque<CostModel::ReplayTaskEntry>> map;
CostModel::ParseInput parser;
parser.ParseReplayInfoJson(replayPath, map);
EXPECT_GT(map.size(), 0);
unlink(replayPath.c_str());
}
TEST_F(CostModelTest, TestDebugFunction)
{
auto func = std::make_shared<CostModel::Function>();
func->funcName = "test_debug";
std::unordered_map<int, CostModel::TilePtr> tiles;
std::unordered_map<int, CostModel::TileOpPtr> tileOps;
CostModel::ModelVisualizer visualizer;
std::string outdir = "/tmp/debug_func_test_" + std::to_string(getpid());
mkdir(outdir.c_str(), 0755);
visualizer.DebugFunction(func, tiles, tileOps, outdir);
std::ifstream ifs(outdir + "/test_debug.deadlock_debug_graph.dot");
EXPECT_TRUE(ifs.is_open());
unlink((outdir + "/test_debug.deadlock_debug_graph.dot").c_str());
}
TEST_F(CostModelTest, TestSimQueueBuild)
{
CostModel::SimQueue<int> queue;
queue.Enqueue(10);
queue.Build();
EXPECT_EQ(queue.Size(), 0);
}
TEST_F(CostModelTest, TestSimQueueXferAndGetSim)
{
CostModel::SimQueue<int> queue;
queue.Xfer();
EXPECT_EQ(queue.GetSim(), nullptr);
}
TEST_F(CostModelTest, TestSimQueueFrontAndPopFront)
{
CostModel::SimQueue<int> queue;
int val = 0;
EXPECT_FALSE(queue.Front(val));
EXPECT_FALSE(queue.PopFront());
queue.Enqueue(42);
queue.UpdateIntervalCycles(1);
queue.Step();
EXPECT_TRUE(queue.Front(val));
EXPECT_EQ(val, 42);
EXPECT_TRUE(queue.PopFront());
EXPECT_EQ(queue.Size(), 0);
}
TEST_F(CostModelTest, TestSimQueueDequeueEmpty)
{
CostModel::SimQueue<int> queue;
int val = 0;
EXPECT_FALSE(queue.Dequeue(val));
}
TEST_F(CostModelTest, TestSimQueueEmptyStates)
{
CostModel::SimQueue<int> queue;
EXPECT_TRUE(queue.Empty());
queue.Enqueue(1);
EXPECT_TRUE(queue.Empty());
queue.UpdateIntervalCycles(1);
queue.Step();
EXPECT_FALSE(queue.Empty());
}
TEST_F(CostModelTest, TestSimQueueWriteQueueSize)
{
CostModel::SimQueue<int> queue;
EXPECT_EQ(queue.WriteQueueSize(), 0);
queue.Enqueue(1);
queue.Enqueue(2);
EXPECT_EQ(queue.WriteQueueSize(), 2);
}
TEST_F(CostModelTest, TestSimQueueFullAndSetMaxSize)
{
CostModel::SimQueue<int> queue;
queue.SetMaxSize(2);
EXPECT_FALSE(queue.Full());
queue.Enqueue(1);
queue.Enqueue(2);
EXPECT_TRUE(queue.Full());
}
TEST_F(CostModelTest, TestSimQueueCalendarPopFront)
{
CostModel::SimQueue<int> queue;
EXPECT_EQ(queue.CalendarPopFront(), 0);
queue.Enqueue(7);
queue.UpdateIntervalCycles(1);
queue.Step();
EXPECT_EQ(queue.CalendarPopFront(), 7);
}
TEST_F(CostModelTest, TestSimQueueIsTerminate)
{
CostModel::SimQueue<int> queue;
EXPECT_TRUE(queue.IsTerminate());
queue.Enqueue(1);
EXPECT_FALSE(queue.IsTerminate());
}
TEST_F(CostModelTest, TestSimQueueGetMinWaitCyclesZero)
{
CostModel::SimQueue<int> queue;
queue.SetWriteDelay(0);
queue.SetReadDelay(0);
queue.Enqueue(1);
queue.UpdateIntervalCycles(1);
queue.Step();
queue.Step();
uint64_t wait = queue.GetMinWaitCycles();
EXPECT_TRUE(wait >= 1);
}
TEST_F(CostModelTest, TestSimQueueWithDelay)
{
CostModel::SimQueue<int> queue;
queue.SetWriteDelay(3);
queue.SetReadDelay(2);
queue.Enqueue(10);
queue.UpdateIntervalCycles(1);
queue.Step();
queue.Step();
queue.Step();
queue.Step();
int val = 0;
EXPECT_TRUE(queue.Dequeue(val));
EXPECT_EQ(val, 10);
}
TEST_F(CostModelTest, TestSimQueueReset)
{
CostModel::SimQueue<int> queue;
queue.Enqueue(1);
queue.Enqueue(2);
queue.Reset();
EXPECT_EQ(queue.Size(), 0);
EXPECT_EQ(queue.WriteQueueSize(), 0);
}
TEST_F(CostModelTest, TestSimQueueSetCounterInfo)
{
CostModel::SimQueue<int> queue;
auto logger = std::make_shared<CostModel::TraceLogger>();
queue.SetCounterInfo(logger, 1, 2);
}
TEST_F(CostModelTest, TestParseDynTopo)
{
std::string topoPath = "/tmp/parse_dyn_topo_test_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(topoPath);
ofs << "1,2,3,4,5,6,7,8,9,10,11,12\n";
ofs.close();
npu::tile_fwk::CostModelAgent agent;
Json res = agent.ParseDynTopo(topoPath);
EXPECT_GT(res.size(), 0);
unlink(topoPath.c_str());
}
TEST_F(CostModelTest, TestSubmitTopo)
{
std::string topoPath = "/tmp/submit_topo_test_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(topoPath);
ofs << "1,2,3,4,5,6,7,8,9,10,11,12\n";
ofs.close();
npu::tile_fwk::CostModelAgent agent;
agent.SubmitTopo(topoPath);
EXPECT_FALSE(agent.topoJsonPath.empty());
unlink(topoPath.c_str());
}
TEST_F(CostModelTest, TestGetLeafFunctionTimeCost)
{
npu::tile_fwk::CostModelAgent agent;
EXPECT_EQ(agent.GetLeafFunctionTimeCost(0), 0);
}
TEST_F(CostModelTest, TestSubmitSingleFuncToCostModel)
{
npu::tile_fwk::CostModelAgent agent;
std::string name = "TestSingleFunc";
auto newFunc = std::make_shared<npu::tile_fwk::Function>(npu::tile_fwk::Program::GetInstance(), name, name, nullptr);
agent.SubmitSingleFuncToCostModel(newFunc.get());
}
TEST_F(CostModelTest, TestDebugSingleFunc)
{
npu::tile_fwk::CostModelAgent agent;
auto newFunc = std::make_shared<npu::tile_fwk::Function>(npu::tile_fwk::Program::GetInstance(), "root", "root", nullptr);
config::SetSimConfig(KEY_DEBUG_SINGLE_FUNCNAME, "");
agent.DebugSingleFunc(newFunc.get());
}
TEST_F(CostModelTest, TestPipeMachineImplSimulate)
{
class TestPipeImpl : public CostModel::PipeMachineImpl {
uint64_t Simulate(const CostModel::TileOpPtr&) override { return 42; }
uint64_t PostSimulate(const CostModel::TileOpPtr&) override { return 10; }
};
TestPipeImpl impl;
auto tileOp = std::make_shared<CostModel::TileOp>();
EXPECT_EQ(impl.Simulate(tileOp), 42);
EXPECT_EQ(impl.PostSimulate(tileOp), 10);
}
TEST_F(CostModelTest, TestPipeMachineImplSimulateForPass)
{
class TestPipeImpl : public CostModel::PipeMachineImpl {
uint64_t Simulate(const CostModel::TileOpPtr&) override { return 0; }
uint64_t PostSimulate(const CostModel::TileOpPtr&) override { return 0; }
};
TestPipeImpl impl;
EXPECT_EQ(impl.SimulateForPass("ADD", {{1, 1}}, npu::tile_fwk::DataType::DT_FP32), 0);
EXPECT_EQ(impl.PostSimulateForPass("ADD", {{1, 1}}, npu::tile_fwk::DataType::DT_FP32), 0);
}
class MockCacheMachineImpl : public CostModel::CacheMachineImpl {
public:
uint64_t SimulateLatency = 10;
uint64_t Simulate(const CostModel::CachePacket& packet) override
{
(void)packet;
return SimulateLatency;
}
};
class CacheMachineTest : public testing::Test {
public:
static void SetUpTestCase() {}
static void TearDownTestCase() {}
void SetUp() override
{
config::SetPlatformConfig(KEY_ENABLE_COST_MODEL, true);
sim = std::make_shared<CostModel::SimSys>();
cacheMachine = new CostModel::CacheMachine(CostModel::CacheType::L2CACHE, "A2A3");
cacheMachine->sim = sim;
cacheMachine->Build();
}
void TearDown() override
{
delete cacheMachine;
}
protected:
std::shared_ptr<CostModel::SimSys> sim;
CostModel::CacheMachine* cacheMachine = nullptr;
};
TEST_F(CacheMachineTest, TestConstructor)
{
EXPECT_NE(cacheMachine->cacheImpl, nullptr);
EXPECT_EQ(cacheMachine->cacheType, CostModel::CacheType::L2CACHE);
}
TEST_F(CacheMachineTest, TestBuild)
{
EXPECT_NE(cacheMachine->stats, nullptr);
}
TEST_F(CacheMachineTest, TestInitQueueDelay)
{
cacheMachine->InitQueueDelay();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestStepQueue)
{
sim->globalCycles = 100;
cacheMachine->lastCycles = 50;
cacheMachine->StepQueue();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestGetQueueNextCycles)
{
sim->globalCycles = 100;
uint64_t nextCycles = cacheMachine->GetQueueNextCycles();
EXPECT_GE(nextCycles, 100);
}
TEST_F(CacheMachineTest, TestIsTerminateNoTask)
{
cacheMachine->executingTask = false;
bool terminate = cacheMachine->IsTerminate();
EXPECT_TRUE(terminate);
}
TEST_F(CacheMachineTest, TestIsTerminateWithExecutingTask)
{
cacheMachine->executingTask = true;
bool terminate = cacheMachine->IsTerminate();
EXPECT_FALSE(terminate);
}
TEST_F(CacheMachineTest, TestIsTerminateWithDataInQueue)
{
cacheMachine->executingTask = false;
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x1000;
cacheMachine->dataRequestQueue.Enqueue(pkt, 0);
bool terminate = cacheMachine->IsTerminate();
EXPECT_FALSE(terminate);
}
TEST_F(CacheMachineTest, TestRequestData)
{
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x1000;
pkt.requestType = CostModel::CacheRequestType::DATA_READ_REQ;
cacheMachine->lastCycles = sim->globalCycles;
cacheMachine->RequestData(pkt, 0);
EXPECT_TRUE(cacheMachine->dataRequestQueue.Empty());
}
TEST_F(CacheMachineTest, TestStepWithEmptyQueue)
{
sim->globalCycles = 100;
cacheMachine->executingTask = false;
cacheMachine->Step();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestStepWithPacketCacheHit)
{
sim->globalCycles = 100;
cacheMachine->executingTask = false;
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x1000;
pkt.requestType = CostModel::CacheRequestType::DATA_READ_REQ;
cacheMachine->dataRequestQueue.Enqueue(pkt, 0);
auto mockImpl = new MockCacheMachineImpl();
mockImpl->SimulateLatency = 10;
cacheMachine->cacheImpl.reset(mockImpl);
cacheMachine->AccessCache(0x1000, 100);
cacheMachine->Step();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestStepWithPacketCacheMiss)
{
sim->globalCycles = 100;
cacheMachine->executingTask = false;
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x2000;
pkt.requestType = CostModel::CacheRequestType::DATA_READ_REQ;
cacheMachine->dataRequestQueue.Enqueue(pkt, 0);
cacheMachine->Step();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestXfer)
{
sim->globalCycles = 100;
cacheMachine->lastCycles = 50;
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x3000;
pkt.requestType = CostModel::CacheRequestType::DATA_READ_REQ;
cacheMachine->dataRequestQueue.Enqueue(pkt, 0);
cacheMachine->Xfer();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestProcessMSHR)
{
sim->globalCycles = 100;
cacheMachine->config.l2MissExtraLatency = 150;
cacheMachine->config.l2HitLatency = 50;
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x4000;
pkt.requestType = CostModel::CacheRequestType::DATA_READ_REQ;
cacheMachine->AddMSHR(pkt, 100);
sim->globalCycles = 300;
cacheMachine->ProcessMSHR();
EXPECT_TRUE(cacheMachine->responseQueue.empty() == false ||
cacheMachine->responseQueue.size() == 0);
}
TEST_F(CacheMachineTest, TestProcessRespWithMachine)
{
auto coreMachine = new CostModel::CoreMachine(CostModel::MachineType::AIC);
coreMachine->sim = sim;
sim->pidToMachineMp[1] = std::shared_ptr<CostModel::Machine>(coreMachine);
CostModel::CachePacket pkt;
pkt.pid = 1;
pkt.addr = 0x5000;
pkt.requestType = CostModel::CacheRequestType::DATA_READ_REQ;
pkt.cycleInfo.cacheRecvCycle = 100;
cacheMachine->responseQueue.emplace_back(pkt, sim->globalCycles);
cacheMachine->ProcessResp();
EXPECT_TRUE(true);
}
TEST_F(CacheMachineTest, TestAllocateCacheAndEvict)
{
cacheMachine->config.l2Size = 1024;
cacheMachine->config.l2LineSize = 64;
for (int i = 0; i < 20; ++i) {
cacheMachine->AllocateCache(0x1000 + i * 0x100, 100 + i);
}
EXPECT_TRUE(cacheMachine->cache.size() <= 16);
}
TEST_F(CacheMachineTest, TestAccessCacheHit)
{
cacheMachine->AllocateCache(0x6000, 100);
bool hit = cacheMachine->AccessCache(0x6000, 200);
EXPECT_TRUE(hit);
}
TEST_F(CacheMachineTest, TestAccessCacheMiss)
{
bool hit = cacheMachine->AccessCache(0x7000, 100);
EXPECT_FALSE(hit);
}
TEST_F(CacheMachineTest, TestEvictLRU)
{
cacheMachine->AllocateCache(0x8000, 100);
size_t sizeBefore = cacheMachine->cache.size();
cacheMachine->EvictLRU();
EXPECT_TRUE(cacheMachine->cache.size() < sizeBefore || sizeBefore == 1);
}
TEST_F(CacheMachineTest, TestReport)
{
EXPECT_NO_THROW(cacheMachine->Report());
}
TEST_F(CacheMachineTest, TestGetSim)
{
EXPECT_EQ(cacheMachine->GetSim(), sim);
}
TEST_F(CacheMachineTest, TestReset)
{
EXPECT_NO_THROW(cacheMachine->Reset());
}
class ReporterTest : public testing::Test {
public:
static void SetUpTestCase() {}
static void TearDownTestCase() {}
void SetUp() override {}
void TearDown() override {}
};
TEST_F(ReporterTest, TestReporterReportTitle)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportTitle("TestTitle");
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("TestTitle"), std::string::npos);
EXPECT_EQ(output.size(), CostModel::TOTAL_WIDTH + 1);
size_t leftCount = output.find("TestTitle");
size_t rightCount = output.size() - leftCount - std::string("TestTitle").size() - 1;
EXPECT_GE(leftCount, 0);
EXPECT_GE(rightCount, 0);
}
TEST_F(ReporterTest, TestReporterReportMap)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
std::map<uint64_t, uint64_t> emptyMap;
CostModel::Reporter::ReportMap("EmptyMap", emptyMap);
std::string output1 = oss.str();
std::map<uint64_t, uint64_t> fewMap = {{1, 10}, {2, 20}, {3, 30}};
CostModel::Reporter::ReportMap("FewMap", fewMap);
std::string output2 = oss.str();
std::map<uint64_t, uint64_t> manyMap;
for (int i = 0; i < 9; ++i) {
manyMap[i] = i * 100;
}
CostModel::Reporter::ReportMap("ManyMap", manyMap);
std::string output3 = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output1.find("EmptyMap"), std::string::npos);
EXPECT_NE(output2.find("FewMap"), std::string::npos);
EXPECT_NE(output2.find("1"), std::string::npos);
EXPECT_NE(output2.find("10"), std::string::npos);
EXPECT_NE(output3.find("ManyMap"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportMapAndPct)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
std::map<int, uint64_t> vals = {{0, 50}, {1, 30}};
uint64_t baseVal = 100;
CostModel::Reporter::ReportMapAndPct("TestPct", vals, baseVal);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("TestPct"), std::string::npos);
EXPECT_NE(output.find("50"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportMapsAndPct)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
std::map<int, uint64_t> vals = {{0, 50}, {1, 30}};
std::map<int, uint64_t> baseValsHasKey = {{0, 100}, {1, 200}};
CostModel::Reporter::ReportMapsAndPct("HasBase", vals, baseValsHasKey);
std::map<int, uint64_t> baseValsNoKey = {{2, 100}};
CostModel::Reporter::ReportMapsAndPct("NoBaseKey", vals, baseValsNoKey);
std::map<int, uint64_t> baseValsZero = {{0, 0}};
std::map<int, uint64_t> valsZero = {{0, 50}};
CostModel::Reporter::ReportMapsAndPct("ZeroBase", valsZero, baseValsZero);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("HasBase"), std::string::npos);
EXPECT_NE(output.find("NoBaseKey"), std::string::npos);
EXPECT_NE(output.find("ZeroBase"), std::string::npos);
EXPECT_NE(output.find("nan%"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportVal)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportVal("Uint64Val", static_cast<uint64_t>(12345));
CostModel::Reporter::ReportVal("FloatVal", 3.14f);
CostModel::Reporter::ReportVal("DoubleVal", 2.718);
CostModel::Reporter::ReportValWithLvl("Uint64Lvl", static_cast<uint64_t>(999), 1);
CostModel::Reporter::ReportValWithLvl("FloatLvl", 1.5f, 2);
CostModel::Reporter::ReportValWithLvl("DoubleLvl", 0.01, 3);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("Uint64Val"), std::string::npos);
EXPECT_NE(output.find("12345"), std::string::npos);
EXPECT_NE(output.find("FloatVal"), std::string::npos);
EXPECT_NE(output.find("DoubleVal"), std::string::npos);
EXPECT_NE(output.find("|--Uint64Lvl"), std::string::npos);
EXPECT_NE(output.find("|--FloatLvl"), std::string::npos);
EXPECT_NE(output.find("|--DoubleLvl"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportAvg)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportAvg("AvgUint64", static_cast<uint64_t>(100), static_cast<uint64_t>(10));
CostModel::Reporter::ReportAvg("AvgFloatNormal", 100.0f, 10.0f);
CostModel::Reporter::ReportAvg("AvgFloatNegDenom", 100.0f, -1.0f);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("AvgUint64"), std::string::npos);
EXPECT_NE(output.find("AvgFloatNormal"), std::string::npos);
EXPECT_NE(output.find("AvgFloatNegDenom"), std::string::npos);
EXPECT_NE(output.find("nan"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportPct)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportPct("PctUint64", static_cast<uint64_t>(50), static_cast<uint64_t>(100));
CostModel::Reporter::ReportPct("PctFloatNormal", 50.0f, 100.0f);
CostModel::Reporter::ReportPct("PctFloatNegDenom", 50.0f, -1.0f);
CostModel::Reporter::ReportPct("PctFloatRate", 0.5f);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("PctUint64"), std::string::npos);
EXPECT_NE(output.find("PctFloatNormal"), std::string::npos);
EXPECT_NE(output.find("PctFloatNegDenom"), std::string::npos);
EXPECT_NE(output.find("nan%"), std::string::npos);
EXPECT_NE(output.find("PctFloatRate"), std::string::npos);
EXPECT_NE(output.find("50.00%"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportValAndPct)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportValAndPct("VPUint64Normal", static_cast<uint64_t>(50), static_cast<uint64_t>(100));
CostModel::Reporter::ReportValAndPct("VPUint64ZeroDenom", static_cast<uint64_t>(50), static_cast<uint64_t>(0));
CostModel::Reporter::ReportValAndPct("VPFloatUint64", 50.0f, static_cast<uint64_t>(100));
CostModel::Reporter::ReportValAndPctFl("VPFlNormal", 50.0, 100.0);
CostModel::Reporter::ReportValAndPctFl("VPFlNegDenom", 50.0, -1.0);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("VPUint64Normal"), std::string::npos);
EXPECT_NE(output.find("VPUint64ZeroDenom"), std::string::npos);
EXPECT_NE(output.find("nan%"), std::string::npos);
EXPECT_NE(output.find("VPFloatUint64"), std::string::npos);
EXPECT_NE(output.find("VPFlNormal"), std::string::npos);
EXPECT_NE(output.find("VPFlNegDenom"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportHexCounter)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportHexCounter("HexName", 0xff, 42);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("HexName"), std::string::npos);
EXPECT_NE(output.find("42"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterReportStallLoc)
{
std::ostringstream oss;
std::streambuf* oldBuf = std::cout.rdbuf(oss.rdbuf());
CostModel::Reporter::ReportStallLoc("Stall", 0xa, 0xb, 0xc, 100.0);
std::string output = oss.str();
std::cout.rdbuf(oldBuf);
EXPECT_NE(output.find("Stall"), std::string::npos);
EXPECT_NE(output.find("local_0x"), std::string::npos);
EXPECT_NE(output.find("peer_0x"), std::string::npos);
}
TEST_F(ReporterTest, TestReporterOutputStream)
{
CostModel::Reporter reporter;
std::string tmpFile = "/tmp/reporter_test_" + std::to_string(getpid()) + ".txt";
std::streambuf* oldBuf = reporter.ReportSetOutStreamFile(tmpFile);
EXPECT_NE(oldBuf, nullptr);
std::cout << "LineToRedirect" << std::endl;
reporter.ReportResetOutStreamCout(oldBuf);
std::ifstream ifs(tmpFile);
EXPECT_TRUE(ifs.is_open());
std::string content;
std::getline(ifs, content);
EXPECT_NE(content.find("LineToRedirect"), std::string::npos);
ifs.close();
unlink(tmpFile.c_str());
}
TEST_F(ReporterTest, TestReporterOutputStreamAppend)
{
CostModel::Reporter reporter;
std::string tmpFile = "/tmp/reporter_append_test_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(tmpFile);
ofs << "ExistingLine" << std::endl;
ofs.close();
std::streambuf* oldBuf = reporter.ReportSetOutStreamFile(tmpFile, true);
EXPECT_NE(oldBuf, nullptr);
std::cout << "AppendedLine" << std::endl;
reporter.ReportResetOutStreamCout(oldBuf);
std::ifstream ifs(tmpFile);
std::string line1, line2;
std::getline(ifs, line1);
std::getline(ifs, line2);
EXPECT_NE(line1.find("ExistingLine"), std::string::npos);
EXPECT_NE(line2.find("AppendedLine"), std::string::npos);
ifs.close();
reporter.ReportResetOutStreamCout(nullptr);
unlink(tmpFile.c_str());
}
class TraceLoggerTest : public testing::Test {
public:
static void SetUpTestCase() {}
static void TearDownTestCase() {}
void SetUp() override
{
logger = std::make_shared<CostModel::TraceLogger>();
sim = std::make_shared<CostModel::SimSys>();
logger->sim = sim;
}
void TearDown() override {}
protected:
std::shared_ptr<CostModel::TraceLogger> logger;
std::shared_ptr<CostModel::SimSys> sim;
};
TEST_F(TraceLoggerTest, TestEventToJson)
{
CostModel::Event evBasic;
evBasic.name = "TestEvent";
evBasic.id = -1;
evBasic.catagory = "";
evBasic.phase = "B";
evBasic.bp = "";
evBasic.timestamp = 1000;
evBasic.pid = 1;
evBasic.tid = 2;
evBasic.hint = "";
CostModel::Json j1 = evBasic.ToJson();
EXPECT_EQ(j1["name"], "TestEvent");
EXPECT_EQ(j1["ph"], "B");
EXPECT_EQ(j1["ts"], 1000);
EXPECT_EQ(j1["pid"], 1);
EXPECT_EQ(j1["tid"], 2);
EXPECT_TRUE(j1.find("cat") == j1.end());
EXPECT_TRUE(j1.find("bp") == j1.end());
EXPECT_TRUE(j1.find("id") == j1.end());
EXPECT_TRUE(j1.find("args") == j1.end());
CostModel::Event evFull;
evFull.name = "FullEvent";
evFull.id = 5;
evFull.catagory = "event";
evFull.phase = "X";
evFull.bp = "e";
evFull.timestamp = 2000;
evFull.pid = 3;
evFull.tid = 4;
evFull.hint = "some hint";
CostModel::Json j2 = evFull.ToJson();
EXPECT_EQ(j2["cat"], "event");
EXPECT_EQ(j2["bp"], "e");
EXPECT_EQ(j2["id"], 5);
EXPECT_TRUE(j2.find("args") != j2.end());
EXPECT_EQ(j2["args"]["event-hint"], "some hint");
CostModel::Event evHintWithColor;
evHintWithColor.name = "Op(red)";
evHintWithColor.id = 1;
evHintWithColor.catagory = "event";
evHintWithColor.phase = "B";
evHintWithColor.bp = "";
evHintWithColor.timestamp = 3000;
evHintWithColor.pid = 1;
evHintWithColor.tid = 1;
evHintWithColor.hint = "hint text";
CostModel::Json j3 = evHintWithColor.ToJson();
EXPECT_EQ(j3["args"]["color"], "red");
}
TEST_F(TraceLoggerTest, TestEventFlowJson)
{
CostModel::Event ev;
ev.name = "FlowEvent";
ev.id = 10;
ev.catagory = "event";
ev.phase = "B";
ev.bp = "e";
ev.timestamp = 5000;
ev.pid = 2;
ev.tid = 3;
CostModel::Json jStart = ev.ToFlowStartJson(42);
EXPECT_EQ(jStart["id"], 42);
EXPECT_EQ(jStart["ph"], "s");
EXPECT_EQ(jStart["name"], "machine-view-last-dep");
EXPECT_EQ(jStart["ts"], 4999);
EXPECT_EQ(jStart["pid"], 2);
EXPECT_EQ(jStart["tid"], 3);
CostModel::Json jEnd = ev.ToFlowEndJson(42);
EXPECT_EQ(jEnd["id"], 42);
EXPECT_EQ(jEnd["ph"], "f");
EXPECT_EQ(jEnd["bp"], "e");
EXPECT_EQ(jEnd["ts"], 5000);
}
TEST_F(TraceLoggerTest, TestEventGetColor)
{
CostModel::Event ev1;
ev1.name = "Op(red)";
EXPECT_EQ(ev1.GetColor(), "red");
CostModel::Event ev2;
ev2.name = "NoParentheses";
EXPECT_EQ(ev2.GetColor(), "");
CostModel::Event ev3;
ev3.name = "Op(left(";
EXPECT_EQ(ev3.GetColor(), "");
CostModel::Event ev4;
ev4.name = "Op()";
EXPECT_EQ(ev4.GetColor(), "");
CostModel::Event ev5;
ev5.name = "Op(blue)extra";
EXPECT_EQ(ev5.GetColor(), "blue");
}
TEST_F(TraceLoggerTest, TestEventExtraHintInfo)
{
CostModel::Event ev;
ev.hint = "TaskId:42 pSgId:5";
std::string taskKey = "TaskId:";
int taskId = ev.ExtraHintInfo(taskKey);
EXPECT_EQ(taskId, 42);
std::string sgKey = "pSgId:";
int sgId = ev.ExtraHintInfo(sgKey);
EXPECT_EQ(sgId, 5);
}
TEST_F(TraceLoggerTest, TestCounterEventToJson)
{
CostModel::CounterEvent ce;
ce.id = 1;
ce.catagory = "count";
ce.phase = "C";
ce.type = CostModel::CounterType::COUNT_SIZE;
ce.size = 42;
ce.timestamp = 1000;
ce.pid = 1;
ce.tid = 2;
CostModel::Json j = ce.ToJson();
EXPECT_EQ(j["args"]["size"], 42);
EXPECT_EQ(j["name"], "count");
EXPECT_EQ(j["pid"], 1);
EXPECT_EQ(j["tid"], 2);
EXPECT_EQ(j["ph"], "C");
EXPECT_EQ(j["ts"], 1000);
}
TEST_F(TraceLoggerTest, TestThreadProcessToJson)
{
CostModel::Thread th;
th.name = "WorkerThread";
th.pid = 1;
th.tid = 2;
CostModel::Json jTh = th.ToJson();
EXPECT_EQ(jTh["args"]["name"], "WorkerThread");
EXPECT_EQ(jTh["cat"], "__metadata");
EXPECT_EQ(jTh["name"], "thread_name");
EXPECT_EQ(jTh["ph"], "M");
EXPECT_EQ(jTh["pid"], 1);
EXPECT_EQ(jTh["tid"], 2);
CostModel::Process proc;
proc.name = "AICore";
proc.pid = 3;
proc.coreIdx = 5;
CostModel::Json jProc = proc.ToJson();
EXPECT_EQ(jProc["args"]["name"], "AICore");
EXPECT_EQ(jProc["cat"], "__metadata");
EXPECT_EQ(jProc["name"], "process_name");
EXPECT_EQ(jProc["ph"], "M");
EXPECT_EQ(jProc["pid"], 3);
CostModel::Json jSort = proc.ToSortIndexJson(7);
EXPECT_EQ(jSort["args"]["sort_index"], 7);
EXPECT_EQ(jSort["name"], "process_sort_index");
EXPECT_EQ(jSort["pid"], 3);
}
TEST_F(TraceLoggerTest, TestDurationToJson)
{
CostModel::Duration dur1;
dur1.start.name = "EmptyCatHint";
dur1.start.id = 1;
dur1.start.catagory = "";
dur1.start.hint = "";
dur1.start.timestamp = 100;
dur1.start.pid = 1;
dur1.start.tid = 2;
dur1.end.timestamp = 200;
CostModel::Json j1 = dur1.ToJson();
EXPECT_EQ(j1["ph"], "X");
EXPECT_EQ(j1["name"], "EmptyCatHint");
EXPECT_EQ(j1["ts"], 100);
EXPECT_EQ(j1["dur"], 100);
EXPECT_TRUE(j1.find("cat") == j1.end());
EXPECT_TRUE(j1.find("args") == j1.end());
CostModel::Duration dur2;
dur2.start.name = "Op(blue)";
dur2.start.id = 2;
dur2.start.catagory = "event";
dur2.start.hint = "hint text";
dur2.start.timestamp = 300;
dur2.start.pid = 3;
dur2.start.tid = 4;
dur2.end.timestamp = 500;
CostModel::Json j2 = dur2.ToJson();
EXPECT_EQ(j2["cat"], "event");
EXPECT_TRUE(j2.find("args") != j2.end());
EXPECT_EQ(j2["args"]["event-hint"], "hint text");
EXPECT_EQ(j2["args"]["color"], "blue");
}
TEST_F(TraceLoggerTest, TestDurationOutputTraces)
{
std::map<CostModel::Pid, CostModel::Process> mProcesses;
std::map<CostModel::PTid, CostModel::Thread> mThreads;
mProcesses[1] = CostModel::Process{.name = "AIC", .pid = 1, .coreIdx = 3};
mProcesses[1000] = CostModel::Process{.name = "MachineView", .pid = 1000, .coreIdx = 0};
mThreads[CostModel::PTid{1, 1}] = CostModel::Thread{.name = "Pipe1", .pid = 1, .tid = 1};
mThreads[CostModel::PTid{1000, 1}] = CostModel::Thread{.name = "MVThread", .pid = 1000, .tid = 1};
CostModel::Duration dur;
dur.start.name = "TileOp";
dur.start.id = 1;
dur.start.catagory = "event";
dur.start.hint = "hint";
dur.start.timestamp = 1000;
dur.start.pid = 1;
dur.start.tid = 1;
dur.end.timestamp = 2000;
dur.end.pid = 1;
dur.end.tid = 1;
std::string ctxPath = "/tmp/dur_ctx_trace_" + std::to_string(getpid()) + ".txt";
std::ofstream ofsCtx(ctxPath);
CostModel::Duration durCtx;
durCtx.start.name = "MVOp";
durCtx.start.timestamp = 5000;
durCtx.start.pid = 1000;
durCtx.start.tid = 1;
durCtx.end.timestamp = 6000;
durCtx.end.pid = 1000;
durCtx.end.tid = 1;
durCtx.OutputContextSwitchTrace(ofsCtx, mProcesses, mThreads, 1800000);
ofsCtx.close();
std::ifstream ifsCtx(ctxPath);
std::string ctxContent;
std::getline(ifsCtx, ctxContent);
EXPECT_NE(ctxContent.find("sched_wakeup"), std::string::npos);
ifsCtx.close();
unlink(ctxPath.c_str());
std::string beginEndPath = "/tmp/dur_beginend_trace_" + std::to_string(getpid()) + ".txt";
std::ofstream ofsBE(beginEndPath);
dur.OutputBeginEndTrace(ofsBE, mProcesses, mThreads, 1800000);
ofsBE.close();
std::ifstream ifsBE(beginEndPath);
std::string beContent;
std::getline(ifsBE, beContent);
EXPECT_NE(beContent.find("tracing_mark_write"), std::string::npos);
ifsBE.close();
unlink(beginEndPath.c_str());
}
TEST_F(TraceLoggerTest, TestTraceLoggerSetNames)
{
logger->SetProcessName("AICore", 1, 5);
EXPECT_TRUE(logger->mProcesses.find(1) != logger->mProcesses.end());
EXPECT_EQ(logger->mProcesses[1].name, "AICore");
EXPECT_EQ(logger->mProcesses[1].coreIdx, 5);
EXPECT_TRUE(logger->mMachineTileOpMap.find(1) != logger->mMachineTileOpMap.end());
logger->SetThreadName("Worker", 1, 2);
CostModel::PTid ptid{1, 2};
EXPECT_TRUE(logger->mThreads.find(ptid) != logger->mThreads.end());
EXPECT_EQ(logger->mThreads[ptid].name, "Worker");
}
TEST_F(TraceLoggerTest, TestTraceLoggerAddEventBeginEnd)
{
logger->SetProcessName("AICore", CostModel::GetProcessID(CostModel::MachineType::AIC, 0), 0);
CostModel::Pid corePid = CostModel::GetProcessID(CostModel::MachineType::AIC, 0);
CostModel::Tid coreTid = 1;
logger->SetThreadName("AICThread", corePid, coreTid);
CostModel::Event beginEv = logger->AddEventBegin("TileStart", corePid, coreTid, 100, "TaskId:10 pSgId:5");
EXPECT_EQ(beginEv.name, "TileStart");
EXPECT_GT(beginEv.id, 0);
EXPECT_EQ(beginEv.phase, "B");
EXPECT_EQ(beginEv.timestamp, 100);
EXPECT_FALSE((logger->m_eventStacks[CostModel::PTid{corePid, coreTid}].empty()));
CostModel::Event endEv = logger->AddEventEnd(corePid, coreTid, 200);
EXPECT_EQ(endEv.name, "TileStart");
EXPECT_EQ(endEv.phase, "E");
EXPECT_EQ(endEv.timestamp, 200);
EXPECT_TRUE((logger->m_eventStacks[CostModel::PTid{corePid, coreTid}].empty()));
EXPECT_TRUE(logger->mDurations.find(beginEv.id) != logger->mDurations.end());
logger->SetProcessName("MachineView", logger->topMachineViewPid, 0);
CostModel::Tid mvTid = CostModel::GetProcessID(CostModel::MachineType::AIC, 1);
logger->SetThreadName("MVThread", logger->topMachineViewPid, mvTid);
CostModel::Event mvBegin = logger->AddEventBegin("MVOp", logger->topMachineViewPid, mvTid, 300, "TaskId:20 SeqNo:1 pSgId:3");
EXPECT_EQ(mvBegin.hint, "TaskId:20 SeqNo:1 pSgId:3");
CostModel::Event mvEnd = logger->AddEventEnd(logger->topMachineViewPid, mvTid, 400);
EXPECT_TRUE(logger->mTaskIDToDurationIndex.find(20) != logger->mTaskIDToDurationIndex.end());
}
TEST_F(TraceLoggerTest, TestTraceLoggerAddDuration)
{
logger->SetProcessName("AICore", 1, 0);
CostModel::LogData data1;
data1.isLogTileOp = false;
data1.name = "NormalOp";
data1.pid = 1;
data1.tid = 1;
data1.sTime = 100;
data1.eTime = 200;
data1.hint = "hint";
logger->AddDuration(data1);
EXPECT_EQ(logger->mEvents.size(), 2);
EXPECT_FALSE(logger->mDurations.empty());
CostModel::LogData data2;
data2.isLogTileOp = true;
data2.name = "100 TileOp";
data2.pid = 1;
data2.tid = 1;
data2.sTime = 300;
data2.eTime = 400;
data2.hint = "";
logger->AddDuration(data2);
EXPECT_TRUE(logger->mMachineTileOpMap[1].find(100) != logger->mMachineTileOpMap[1].end());
}
TEST_F(TraceLoggerTest, TestTraceLoggerAddFlow)
{
logger->AddFlow("testFlow", CostModel::EventId{CostModel::PTid{1, 2}, 3}, CostModel::EventId{CostModel::PTid{4, 5}, 6});
EXPECT_EQ(logger->mFlows.size(), 1);
EXPECT_EQ(logger->mFlows[0].name, "testFlow");
logger->SetProcessName("AICore", 1, 0);
CostModel::LogData data;
data.isLogTileOp = true;
data.name = "100 SrcOp";
data.pid = 1;
data.tid = 1;
data.sTime = 100;
data.eTime = 200;
logger->AddDuration(data);
data.isLogTileOp = true;
data.name = "200 DstOp";
data.pid = 1;
data.tid = 1;
data.sTime = 300;
data.eTime = 400;
logger->AddDuration(data);
logger->AddTileOpFlow(1, 100, 200);
EXPECT_EQ(logger->mFlows.size(), 2);
logger->AddTileOpFlow(2, 100, 200);
EXPECT_EQ(logger->mFlows.size(), 2);
logger->AddTileOpFlow(1, 999, 200);
EXPECT_EQ(logger->mFlows.size(), 2);
}
TEST_F(TraceLoggerTest, TestTraceLoggerAddCounterEvent)
{
sim->globalCycles = 500;
logger->AddCounterEvent(1, 2, CostModel::CounterType::QUEUE_PUSH);
EXPECT_FALSE(logger->mCounters.empty());
EXPECT_EQ(logger->mCounters.back().type, CostModel::CounterType::QUEUE_PUSH);
EXPECT_EQ(logger->mCounters.back().pid, 1);
EXPECT_EQ(logger->mCounters.back().tid, 2);
CostModel::PTid ptid{1, 2};
EXPECT_TRUE(logger->mCounts.find(ptid) != logger->mCounts.end());
EXPECT_FALSE(logger->mCounts[ptid].empty());
}
TEST_F(TraceLoggerTest, TestTraceLoggerEraseLogInfo)
{
logger->SetProcessName("AICore", 1, 0);
logger->SetThreadName("Thread1", 1, 1);
CostModel::Event begin1 = logger->AddEventBegin("Op1", 1, 1, 100);
logger->AddEventEnd(1, 1, 200);
CostModel::Event begin2 = logger->AddEventBegin("Op2", 1, 1, 300);
logger->AddEventEnd(1, 1, 400);
sim->globalCycles = 500;
logger->AddCounterEvent(1, 2, CostModel::CounterType::QUEUE_PUSH);
sim->globalCycles = 600;
logger->AddCounterEvent(1, 2, CostModel::CounterType::QUEUE_POP);
size_t eventsBefore = logger->mEvents.size();
size_t durationsBefore = logger->mDurations.size();
size_t countersBefore = logger->mCounters.size();
logger->EraseLogInfo(250);
EXPECT_LT(logger->mEvents.size(), eventsBefore);
EXPECT_LT(logger->mDurations.size(), durationsBefore);
EXPECT_LT(logger->mCounters.size(), countersBefore);
EXPECT_TRUE(logger->mTaskIDToDurationIndex.empty());
}
TEST_F(TraceLoggerTest, TestTraceLoggerQSizeToJson)
{
std::vector<CostModel::CounterEvent> emptyVec;
CostModel::Json j1 = logger->QSizeToJson(emptyVec);
EXPECT_TRUE(j1.is_array());
EXPECT_EQ(j1.size(), 0);
std::vector<CostModel::CounterEvent> counterVec;
CostModel::CounterEvent ce1;
ce1.id = 1;
ce1.catagory = "count";
ce1.phase = "C";
ce1.type = CostModel::CounterType::COUNT_SIZE;
ce1.size = 10;
ce1.timestamp = 100;
ce1.pid = 1;
ce1.tid = 2;
counterVec.push_back(ce1);
CostModel::CounterEvent ce2;
ce2.id = 2;
ce2.catagory = "count";
ce2.phase = "C";
ce2.type = CostModel::CounterType::COUNT_SIZE;
ce2.size = 20;
ce2.timestamp = 200;
ce2.pid = 1;
ce2.tid = 2;
counterVec.push_back(ce2);
CostModel::Json j2 = logger->QSizeToJson(counterVec);
EXPECT_EQ(j2.size(), 2);
EXPECT_EQ(j2[0]["args"]["size"], 10);
EXPECT_EQ(j2[1]["args"]["size"], 20);
}
TEST_F(TraceLoggerTest, TestTraceLoggerToJson)
{
logger->SetProcessName("AICore", 1, 0);
logger->SetThreadName("Thread1", 1, 1);
logger->processDeviceReadyQueue = true;
CostModel::Event beginEv = logger->AddEventBegin("TestOp", 1, 1, 100, "hint");
logger->AddEventEnd(1, 1, 200);
logger->AddFlow("flow1", CostModel::EventId{CostModel::PTid{1, 1}, beginEv.id},
CostModel::EventId{CostModel::PTid{1, 1}, beginEv.id});
CostModel::Json j = logger->ToJson();
EXPECT_TRUE(j.find("traceEvents") != j.end());
EXPECT_TRUE(j["traceEvents"].is_array());
EXPECT_GT(j["traceEvents"].size(), 0);
bool hasProcess = false;
bool hasThread = false;
bool hasDuration = false;
for (auto& item : j["traceEvents"]) {
if (item["name"] == "process_name") hasProcess = true;
if (item["name"] == "thread_name") hasThread = true;
if (item["ph"] == "X") hasDuration = true;
}
EXPECT_TRUE(hasProcess);
EXPECT_TRUE(hasThread);
EXPECT_TRUE(hasDuration);
}
TEST_F(TraceLoggerTest, TestTraceLoggerToTrace)
{
logger->SetProcessName("AICore", 1, 0);
logger->SetThreadName("Thread1", 1, 1);
CostModel::Event beginEv = logger->AddEventBegin("TestOp", 1, 1, 100, "hint");
logger->AddEventEnd(1, 1, 200);
std::string tracePath = "/tmp/trace_logger_trace_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(tracePath);
logger->ToTrace(ofs);
ofs.close();
std::ifstream ifs(tracePath);
std::string content;
std::getline(ifs, content);
EXPECT_NE(content.find("tracing_mark_write"), std::string::npos);
ifs.close();
unlink(tracePath.c_str());
}
TEST_F(TraceLoggerTest, TestTraceLoggerLogCoreInfo)
{
logger->SetProcessName("AICore", 1, 0);
logger->SetThreadName("Thread1", 1, 1);
sim->taskCompleteSeq[10] = 1;
CostModel::Event startEv;
startEv.name = "InitTask";
startEv.pid = 1;
startEv.tid = 1;
startEv.timestamp = 100;
startEv.hint = "TaskId:10 pSgId:5";
CostModel::Event endEv;
endEv.pid = 1;
endEv.tid = 1;
endEv.timestamp = 200;
CostModel::Duration dur;
dur.start = startEv;
dur.end = endEv;
EXPECT_NO_THROW(logger->LogCoreInfo(dur));
CostModel::Event startEvNoInit;
startEvNoInit.name = "NormalTask";
startEvNoInit.pid = 1;
startEvNoInit.tid = 1;
startEvNoInit.timestamp = 300;
startEvNoInit.hint = "TaskId:10 pSgId:5";
CostModel::Duration durNoInit;
durNoInit.start = startEvNoInit;
durNoInit.end = endEv;
EXPECT_NO_THROW(logger->LogCoreInfo(durNoInit));
}
TEST_F(TraceLoggerTest, TestTraceLoggerToPipeTrace)
{
logger->mCoreInfoLogs[0] = CostModel::CoreInfoLog(0, "AIC");
logger->mCoreInfoLogs[0].pipeLogs["Pipe1"].push_back(CostModel::Json::object({{"tileOp", "Op1"}, {"execStart", 100}, {"execEnd", 200}}));
std::string pipePath = "/tmp/pipe_trace_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(pipePath);
logger->ToPipeTrace(ofs);
ofs.close();
std::ifstream ifs(pipePath);
CostModel::Json j;
ifs >> j;
EXPECT_TRUE(j.is_array());
EXPECT_GT(j.size(), 0);
EXPECT_EQ(j[0]["blockIdx"], 0);
EXPECT_EQ(j[0]["coreType"], "AIC");
ifs.close();
unlink(pipePath.c_str());
}
TEST_F(TraceLoggerTest, TestTraceLoggerToFilterTrace)
{
logger->SetProcessName("AICore", CostModel::GetProcessID(CostModel::MachineType::AIC, 0), 0);
logger->SetThreadName("Thread1", CostModel::GetProcessID(CostModel::MachineType::AIC, 0), 1);
sim->taskCompleteSeq[10] = 1;
std::map<int, std::pair<std::string, std::vector<CostModel::Json>>> coreTasks;
std::string filterPath = "/tmp/filter_trace_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(filterPath);
logger->ToFilterTrace(ofs, coreTasks);
ofs.close();
EXPECT_TRUE(coreTasks.find(0) != coreTasks.end());
unlink(filterPath.c_str());
}
TEST_F(TraceLoggerTest, TestTraceLoggerAddEventBeginEndNoHint)
{
logger->SetProcessName("AICore", 1, 0);
logger->SetThreadName("Thread2", 1, 2);
CostModel::Event beginEv = logger->AddEventBegin("NoHintOp", 1, 2, 500);
EXPECT_EQ(beginEv.hint, "");
EXPECT_EQ(beginEv.phase, "B");
CostModel::Event endEv = logger->AddEventEnd(1, 2, 600);
EXPECT_EQ(endEv.phase, "E");
EXPECT_EQ(endEv.hint, "");
EXPECT_EQ(endEv.timestamp, 600);
}
TEST_F(TraceLoggerTest, TestTraceLoggerToCalendarGlobalJson)
{
std::map<int, std::pair<std::string, std::vector<CostModel::Json>>> coreTasks;
coreTasks[0] = {"AIC", {}};
coreTasks[1] = {"AIV", {}};
std::string calPath = "/tmp/calendar_global_" + std::to_string(getpid()) + ".txt";
std::ofstream ofs(calPath);
logger->ToCalendarGlobalJson(ofs, coreTasks);
ofs.close();
std::ifstream ifs(calPath);
CostModel::Json j;
ifs >> j;
EXPECT_EQ(j["numSupportedCounters"], 1);
EXPECT_TRUE(j["cores"].is_array());
ifs.close();
unlink(calPath.c_str());
}
TEST_F(TraceLoggerTest, TestTraceLoggerEraseLogInfoAllAfter)
{
logger->SetProcessName("AICore", 1, 0);
logger->SetThreadName("Thread1", 1, 1);
logger->AddEventBegin("Op1", 1, 1, 100);
logger->AddEventEnd(1, 1, 200);
logger->AddEventBegin("Op2", 1, 1, 300);
logger->AddEventEnd(1, 1, 400);
size_t durationsBefore = logger->mDurations.size();
logger->EraseLogInfo(50);
EXPECT_LT(logger->mDurations.size(), durationsBefore);
EXPECT_TRUE(logger->mTaskIDToDurationIndex.empty());
}