已合并
fix: 支持Nddma Brc缓存建模和多Group无依赖场景Group间建模 #1960
zhang_shengjie创建于 9月3日
fix: 支持Nddma Brc缓存建模和多Group无依赖场景Group间建模 #1960
已合并
共 12 个文件变更+1211-11
| @@ -0,0 +1,27 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#ifndef ATT_GROUP_PERF_CONFIG_H_ | ||
| 12 | +#define ATT_GROUP_PERF_CONFIG_H_ | ||
| 13 | + | ||
| 14 | +namespace att { | ||
| 15 | + | ||
| 16 | +// Values are from two cold 2048x1024, block_dim=52, g0 -> SyncAll -> g1 samples: | ||
| 17 | +// edge median 3208.7/8510.2 cycles and p95 5358.1/9845.5 cycles. They are deliberately | ||
| 18 | +// conservative and must be revalidated with the profile matrix before changing. | ||
| 19 | +struct SerializedEdgePenaltyConfig { | ||
| 20 | + static constexpr double kPenaltyRatio = 0.10; | ||
| 21 | + static constexpr double kPenaltyCapCycles = 8000.0; | ||
| 22 | + static constexpr const char *kModelVersion = "serialized_edge_v1"; | ||
| 23 | +}; | ||
| 24 | + | ||
| 25 | +} // namespace att | ||
| 26 | + | ||
| 27 | +#endif // ATT_GROUP_PERF_CONFIG_H_ | ||
| @@ -0,0 +1,207 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | +#include "cache_guard_count.h" | ||
| 11 | + | ||
| 12 | +#include <algorithm> | ||
| 13 | +#include <cmath> | ||
| 14 | +#include <limits> | ||
| 15 | + | ||
| 16 | +namespace att { | ||
| 17 | +namespace { | ||
| 18 | +bool IsPositiveConstantOrSymbol(const Expr &expr, uint64_t &value) { | ||
| 19 | + if (!expr.IsConstExpr()) { | ||
| 20 | + return expr.IsValid(); | ||
| 21 | + } | ||
| 22 | + if (expr.GetExprType() == af::ExprType::kExprConstantInteger) { | ||
| 23 | + int64_t signed_value = 0; | ||
| 24 | + if (!expr.GetConstValue(signed_value) || signed_value <= 0) { | ||
| 25 | + return false; | ||
| 26 | + } | ||
| 27 | + value = static_cast<uint64_t>(signed_value); | ||
| 28 | + return true; | ||
| 29 | + } | ||
| 30 | + if (expr.GetExprType() == af::ExprType::kExprConstantRealDouble) { | ||
| 31 | + double real_value = 0.0; | ||
| 32 | + if (!expr.GetConstValue(real_value) || !std::isfinite(real_value) || real_value <= 0.0 || | ||
| 33 | + real_value >= std::ldexp(1.0, std::numeric_limits<uint64_t>::digits)) { | ||
| 34 | + return false; | ||
| 35 | + } | ||
| 36 | + double integral = 0.0; | ||
| 37 | + if (std::fabs(std::modf(real_value, &integral)) > std::numeric_limits<double>::epsilon()) { | ||
| 38 | + return false; | ||
| 39 | + } | ||
| 40 | + value = static_cast<uint64_t>(real_value); | ||
| 41 | + return value > 0U; | ||
| 42 | + } | ||
| 43 | + return false; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +bool GetIntegerConstant(const Expr &expr, uint64_t &value) { | ||
| 47 | + return expr.IsConstExpr() && expr.GetExprType() == af::ExprType::kExprConstantInteger && expr.GetConstValue(value); | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +bool HasSafeBlockRange(uint64_t block, uint64_t inner, uint64_t extent) { | ||
| 51 | + return block <= std::numeric_limits<uint64_t>::max() / inner && | ||
| 52 | + block * inner <= std::numeric_limits<uint64_t>::max() - extent; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +bool EquivalentExpr(const Expr &lhs, const Expr &rhs) { | ||
| 56 | + if (!lhs.IsValid() || !rhs.IsValid()) { | ||
| 57 | + return false; | ||
| 58 | + } | ||
| 59 | + return lhs.Simplify() == rhs.Simplify(); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +// A block split generated by the scheduler has | ||
| 63 | +// block_dim = ceil(period / block_inner_extent) | ||
| 64 | +// and the guarded loop uses the same block-inner extent (the final block is | ||
| 65 | +// clipped by the codegen tail calculation). This is the only symbolic | ||
| 66 | +// uniformity proof currently accepted. It avoids enumerating a symbolic | ||
| 67 | +// block_dim while retaining the parser/scheduler positivity contract for | ||
| 68 | +// axis repeats; arbitrary symbolic expressions still take the legacy path. | ||
| 69 | +bool IsCanonicalPartition(const CacheGuardInfo &info, const Expr &block_dim) { | ||
| 70 | + if (info.kind != CacheGuardKind::kFusedBroadcast || !info.period.IsValid() || !info.block_inner_extent.IsValid() || | ||
| 71 | + !info.loop_extent.IsValid()) { | ||
| 72 | + return false; | ||
| 73 | + } | ||
| 74 | + if (!EquivalentExpr(info.loop_extent, info.block_inner_extent)) { | ||
| 75 | + return false; | ||
| 76 | + } | ||
| 77 | + const Expr expected = af::sym::Ceiling(af::sym::Div(info.period, info.block_inner_extent)); | ||
| 78 | + return EquivalentExpr(block_dim, expected); | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +uint64_t CountConstantHits(const CacheGuardInfo &info, uint64_t block) { | ||
| 82 | + uint64_t period = 0U; | ||
| 83 | + uint64_t inner = 0U; | ||
| 84 | + uint64_t extent = 0U; | ||
| 85 | + if (!GetIntegerConstant(info.loop_extent, extent) || extent == 0U) { | ||
| 86 | + return 0U; | ||
| 87 | + } | ||
| 88 | + if (info.kind == CacheGuardKind::kOriginBroadcast) { | ||
| 89 | + return 1U; | ||
| 90 | + } | ||
| 91 | + (void)GetIntegerConstant(info.period, period); | ||
| 92 | + (void)GetIntegerConstant(info.block_inner_extent, inner); | ||
| 93 | + if (!HasSafeBlockRange(block, inner, extent)) { | ||
| 94 | + return 0U; | ||
| 95 | + } | ||
| 96 | + const uint64_t base = block * inner; | ||
| 97 | + if (base >= period) { | ||
| 98 | + return 0U; | ||
| 99 | + } | ||
| 100 | + const uint64_t effective_extent = std::min(extent, period - base); | ||
| 101 | + const uint64_t last = (base + effective_extent - 1U) / period; | ||
| 102 | + const uint64_t before = (base == 0U) ? 0U : (base - 1U) / period; | ||
| 103 | + const uint64_t multiples = last - before + ((base == 0U) ? 1U : 0U); | ||
| 104 | + return multiples + ((base % period == 0U) ? 0U : 1U); | ||
| 105 | +} | ||
| 106 | +} // namespace | ||
| 107 | + | ||
| 108 | +bool ValidateCacheGuardInfo(const CacheGuardInfo &info) { | ||
| 109 | + if (!info.address_invariant || !info.guard_axis.IsValid() || !info.loop_extent.IsValid()) { | ||
| 110 | + return false; | ||
| 111 | + } | ||
| 112 | + if (info.kind == CacheGuardKind::kNone) { | ||
| 113 | + return true; | ||
| 114 | + } | ||
| 115 | + uint64_t unused = 0U; | ||
| 116 | + if (!IsPositiveConstantOrSymbol(info.loop_extent, unused) || | ||
| 117 | + (!info.loop_extent.IsConstExpr() && !info.positive_range_proven)) { | ||
| 118 | + return false; | ||
| 119 | + } | ||
| 120 | + if (info.kind == CacheGuardKind::kOriginBroadcast) { | ||
| 121 | + return true; | ||
| 122 | + } | ||
| 123 | + if (info.kind != CacheGuardKind::kFusedBroadcast || !info.period.IsValid() || !info.block_inner_extent.IsValid() || | ||
| 124 | + !IsPositiveConstantOrSymbol(info.period, unused) || | ||
| 125 | + !IsPositiveConstantOrSymbol(info.block_inner_extent, unused) || | ||
| 126 | + ((!info.period.IsConstExpr() || !info.block_inner_extent.IsConstExpr()) && !info.positive_range_proven)) { | ||
| 127 | + return false; | ||
| 128 | + } | ||
| 129 | + uint64_t period = 0U; | ||
| 130 | + uint64_t inner = 0U; | ||
| 131 | + const bool period_safe = !info.period.IsConstExpr() || GetIntegerConstant(info.period, period); | ||
| 132 | + const bool inner_safe = !info.block_inner_extent.IsConstExpr() || GetIntegerConstant(info.block_inner_extent, inner); | ||
| 133 | + return period_safe && inner_safe && | ||
| 134 | + (!info.period.IsConstExpr() || !info.block_inner_extent.IsConstExpr() || | ||
| 135 | + inner <= std::numeric_limits<uint64_t>::max() / period); | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +Expr CountGuardHits(const CacheGuardInfo &info, const Expr &block_idx) { | ||
| 139 | + uint64_t extent = 0U; | ||
| 140 | + if (GetIntegerConstant(info.loop_extent, extent) && extent == 0U) { | ||
| 141 | + return CreateExpr(0U); | ||
| 142 | + } | ||
| 143 | + if (!ValidateCacheGuardInfo(info) || !block_idx.IsValid()) { | ||
| 144 | + return CreateExpr(0U); | ||
| 145 | + } | ||
| 146 | + if (info.kind == CacheGuardKind::kNone) { | ||
| 147 | + return CreateExpr(0U); | ||
| 148 | + } | ||
| 149 | + uint64_t block = 0U; | ||
| 150 | + uint64_t period = 0U; | ||
| 151 | + uint64_t inner = 0U; | ||
| 152 | + if (GetIntegerConstant(block_idx, block) && GetIntegerConstant(info.period, period) && | ||
| 153 | + GetIntegerConstant(info.block_inner_extent, inner) && GetIntegerConstant(info.loop_extent, extent)) { | ||
| 154 | + if (!HasSafeBlockRange(block, inner, extent)) { | ||
| 155 | + return CreateExpr(0U); | ||
| 156 | + } | ||
| 157 | + return CreateExpr(CountConstantHits(info, block)); | ||
| 158 | + } | ||
| 159 | + if (info.kind == CacheGuardKind::kOriginBroadcast) { | ||
| 160 | + return CreateExpr(1U); | ||
| 161 | + } | ||
| 162 | + Expr base = af::sym::Mul(block_idx, info.block_inner_extent); | ||
| 163 | + Expr effective_extent = af::sym::Min(info.loop_extent, af::sym::Max(CreateExpr(0U), af::sym::Sub(info.period, base))); | ||
| 164 | + Expr modulo_hits = af::sym::Sub(af::sym::Ceiling(af::sym::Div(af::sym::Add(base, effective_extent), info.period)), | ||
| 165 | + af::sym::Ceiling(af::sym::Div(base, info.period))); | ||
| 166 | + Expr first_offset = af::sym::Mod(base, info.period); | ||
| 167 | + Expr has_work = af::sym::Ceiling(af::sym::Div(effective_extent, af::sym::Add(effective_extent, CreateExpr(1U)))); | ||
| 168 | + return af::sym::Add( | ||
| 169 | + modulo_hits, | ||
| 170 | + af::sym::Mul(has_work, af::sym::Floor(af::sym::Div( | ||
| 171 | + af::sym::Add(first_offset, af::sym::Sub(info.period, CreateExpr(1U))), info.period)))); | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +bool IsBlockCountUniform(const CacheGuardInfo &info, const Expr &block_dim) { | ||
| 175 | + if (!ValidateCacheGuardInfo(info) || !block_dim.IsValid()) { | ||
| 176 | + return false; | ||
| 177 | + } | ||
| 178 | + if (info.kind == CacheGuardKind::kNone || info.kind == CacheGuardKind::kOriginBroadcast) { | ||
| 179 | + return true; | ||
| 180 | + } | ||
| 181 | + if (IsCanonicalPartition(info, block_dim)) { | ||
| 182 | + return true; | ||
| 183 | + } | ||
| 184 | + uint64_t blocks = 0U; | ||
| 185 | + uint64_t period = 0U; | ||
| 186 | + uint64_t inner = 0U; | ||
| 187 | + uint64_t extent = 0U; | ||
| 188 | + if (!GetIntegerConstant(block_dim, blocks) || blocks > 1000000U || !GetIntegerConstant(info.period, period) || | ||
| 189 | + !GetIntegerConstant(info.block_inner_extent, inner) || !GetIntegerConstant(info.loop_extent, extent) || | ||
| 190 | + blocks == 0U) { | ||
| 191 | + return false; | ||
| 192 | + } | ||
| 193 | + if (!HasSafeBlockRange(blocks - 1U, inner, extent)) { | ||
| 194 | + return false; | ||
| 195 | + } | ||
| 196 | + const uint64_t first = CountConstantHits(info, 0U); | ||
| 197 | + for (uint64_t block = 1U; block < blocks; ++block) { | ||
| 198 | + if (!HasSafeBlockRange(block, inner, extent)) { | ||
| 199 | + return false; | ||
| 200 | + } | ||
| 201 | + if (CountConstantHits(info, block) != first) { | ||
| 202 | + return false; | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + return true; | ||
| 206 | +} | ||
| 207 | +} // namespace att | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | +#ifndef EXPR_GEN_CACHE_GUARD_COUNT_H_ | ||
| 11 | +#define EXPR_GEN_CACHE_GUARD_COUNT_H_ | ||
| 12 | + | ||
| 13 | +#include "base/base_types.h" | ||
| 14 | + | ||
| 15 | +namespace att { | ||
| 16 | +enum class CacheGuardKind { kNone, kOriginBroadcast, kFusedBroadcast }; | ||
| 17 | + | ||
| 18 | +struct CacheGuardInfo { | ||
| 19 | + CacheGuardKind kind{CacheGuardKind::kNone}; | ||
| 20 | + Expr guard_axis; | ||
| 21 | + Expr period; | ||
| 22 | + Expr block_inner_extent; | ||
| 23 | + Expr loop_extent; | ||
| 24 | + bool address_invariant{false}; | ||
| 25 | + // Set only when metadata came from parser/scheduler axis repeats whose | ||
| 26 | + // positive range is guaranteed by the scheduling contract. Generic | ||
| 27 | + // symbolic expressions must leave this false and take the legacy path. | ||
| 28 | + bool positive_range_proven{false}; | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +Expr CountGuardHits(const CacheGuardInfo &info, const Expr &block_idx); | ||
| 32 | +bool IsBlockCountUniform(const CacheGuardInfo &info, const Expr &block_dim); | ||
| 33 | +bool ValidateCacheGuardInfo(const CacheGuardInfo &info); | ||
| 34 | +} // namespace att | ||
| 35 | + | ||
| 36 | +#endif // EXPR_GEN_CACHE_GUARD_COUNT_H_ | ||
| @@ -42,7 +42,103 @@ void InsertAxis(const SubAxis *cur_dim, const NodeInfo &node_info, | |||
| 42 | } | 42 | } |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
| 45 | + | ||
| 46 | +bool AxisStableLess(const SubAxis *lhs, const SubAxis *rhs) { | ||
| 47 | + if (lhs->id != rhs->id) { | ||
| 48 | + return lhs->id < rhs->id; | ||
| 49 | + } | ||
| 50 | + return lhs->name < rhs->name; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +bool IsBindMultiCoreInner(const SubAxis *axis) { | ||
| 54 | + return axis != nullptr && axis->is_bind_multi_core && axis->axis_type == AxisPosition::INNER; | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +bool SharesParent(const SubAxis *lhs, const SubAxis *rhs) { | ||
| 58 | + for (const auto *parent : lhs->parent_axis) { | ||
| 59 | + if (parent == nullptr) { | ||
| 60 | + continue; | ||
| 61 | + } | ||
| 62 | + for (const auto *other_parent : rhs->parent_axis) { | ||
| 63 | + if (other_parent != nullptr && | ||
| 64 | + (other_parent == parent || (!parent->name.empty() && parent->name == other_parent->name))) { | ||
| 65 | + return true; | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + return false; | ||
| 70 | +} | ||
| 45 | } // namespace | 71 | } // namespace |
| 72 | + | ||
| 73 | +bool ExeTimePassManager::TryBuildCacheGuardInfo(const NodeInfo &node, CacheGuardInfo &info) const { | ||
| 74 | + info = CacheGuardInfo{}; | ||
| 75 | + if (node.is_cv_ub_fusion || (node.exec_condition != af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis && | ||
| 76 | + node.exec_condition != af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis)) { | ||
| 77 | + return false; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + std::vector<const SubAxis *> block_inner_axes; | ||
| 81 | + for (const auto *axis : node.loop_axes) { | ||
| 82 | + if (IsBindMultiCoreInner(axis)) { | ||
| 83 | + block_inner_axes.emplace_back(axis); | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + std::sort(block_inner_axes.begin(), block_inner_axes.end(), AxisStableLess); | ||
| 87 | + // NDDMA is issued once per block-inner iteration; trailing vector axes are | ||
| 88 | + // consumed inside that operation and must not disable its cache guard. | ||
| 89 | + const bool nddma_with_trailing_vector_axis = node.node_type == "Nddma"; | ||
| 90 | + if (block_inner_axes.size() != 1U || node.loop_axes.empty() || | ||
| 91 | + (!nddma_with_trailing_vector_axis && node.loop_axes.back() != block_inner_axes.front())) { | ||
| 92 | + return false; | ||
| 93 | + } | ||
| 94 | + const auto *block_inner = block_inner_axes.front(); | ||
| 95 | + if (!block_inner->repeat.IsValid()) { | ||
| 96 | + return false; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + info.guard_axis = af::Symbol(block_inner->name.c_str()); | ||
| 100 | + info.loop_extent = block_inner->repeat; | ||
| 101 | + info.address_invariant = true; | ||
| 102 | + // Parser axis repeats are constrained to positive tile sizes by the | ||
| 103 | + // scheduler. Record that provenance so symbolic modulo/division is only | ||
| 104 | + // emitted for metadata produced by this adapter, never for arbitrary | ||
| 105 | + // caller-provided expressions. | ||
| 106 | + info.positive_range_proven = true; | ||
| 107 | + if (node.exec_condition == af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis) { | ||
| 108 | + info.kind = CacheGuardKind::kOriginBroadcast; | ||
| 109 | + return ValidateCacheGuardInfo(info); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + std::vector<const SubAxis *> outer_parents; | ||
| 113 | + for (const auto *parent : block_inner->parent_axis) { | ||
| 114 | + if (parent != nullptr && parent->axis_type == AxisPosition::OUTER && !parent->is_bind_multi_core) { | ||
| 115 | + outer_parents.emplace_back(parent); | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + if (tuning_space_ != nullptr) { | ||
| 119 | + for (const auto &axis_holder : tuning_space_->sub_axes) { | ||
| 120 | + const auto *axis = axis_holder.get(); | ||
| 121 | + if (axis == nullptr || axis->axis_type != AxisPosition::OUTER || axis->is_bind_multi_core) { | ||
| 122 | + continue; | ||
| 123 | + } | ||
| 124 | + const bool direct_parent = | ||
| 125 | + std::find(axis->parent_axis.begin(), axis->parent_axis.end(), block_inner) != axis->parent_axis.end(); | ||
| 126 | + if ((direct_parent || SharesParent(axis, block_inner)) && | ||
| 127 | + std::find(outer_parents.begin(), outer_parents.end(), axis) == outer_parents.end()) { | ||
| 128 | + outer_parents.emplace_back(axis); | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + std::sort(outer_parents.begin(), outer_parents.end(), AxisStableLess); | ||
| 133 | + if (outer_parents.size() != 1U || !outer_parents.front()->repeat.IsValid()) { | ||
| 134 | + return false; | ||
| 135 | + } | ||
| 136 | + info.kind = CacheGuardKind::kFusedBroadcast; | ||
| 137 | + info.period = outer_parents.front()->repeat; | ||
| 138 | + info.block_inner_extent = block_inner->repeat; | ||
| 139 | + return ValidateCacheGuardInfo(info); | ||
| 140 | +} | ||
| 141 | + | ||
| 46 | void ExeTimePassManager::AddBAxis(const std::string &dim_name, const Expr &repeat, const Expr &stride, | 142 | void ExeTimePassManager::AddBAxis(const std::string &dim_name, const Expr &repeat, const Expr &stride, |
| 47 | const NodeInfo &node_info) { | 143 | const NodeInfo &node_info) { |
| 48 | TensorPtr output_tensor = node_info.outputs[0]; | 144 | TensorPtr output_tensor = node_info.outputs[0]; |
| @@ -77,7 +173,8 @@ void ExeTimePassManager::AddRAxis(const std::string &dim_name, const Expr &repea | |||
| 77 | 173 | ||
| 78 | void ExeTimePassManager::CheckBroadcast(const NodeInfo &node) { | 174 | void ExeTimePassManager::CheckBroadcast(const NodeInfo &node) { |
| 79 | const std::string kNodeBroadcast = "Broadcast"; | 175 | const std::string kNodeBroadcast = "Broadcast"; |
| 80 | - if (node.node_type == kNodeBroadcast || ascgen_utils::IsGeneralizeBrcInlineScene(node.node_ptr)) { | 176 | + if (node.node_type == kNodeBroadcast || |
| 177 | + (node.node_ptr != nullptr && ascgen_utils::IsGeneralizeBrcInlineScene(node.node_ptr))) { | ||
| 81 | auto &tensor = node.inputs[0]; | 178 | auto &tensor = node.inputs[0]; |
| 82 | for (size_t i = 0; i < tensor->dim_info.size(); i++) { | 179 | for (size_t i = 0; i < tensor->dim_info.size(); i++) { |
| 83 | AddBAxis(tensor->dim_info[i]->name, tensor->repeat[i], tensor->stride[i], node); | 180 | AddBAxis(tensor->dim_info[i]->name, tensor->repeat[i], tensor->stride[i], node); |
| @@ -189,6 +286,30 @@ bool ExeTimePassManager::GetRLoop(const NodeInfo &node, Expr &r_loop) const { | |||
| 189 | return false; | 286 | return false; |
| 190 | } | 287 | } |
| 191 | 288 | ||
| 289 | +Expr ExeTimePassManager::GetBlockDimExpr() const { | ||
| 290 | + if (tuning_space_ == nullptr || tuning_space_->block_dims.empty()) { | ||
| 291 | + return Expr(); | ||
| 292 | + } | ||
| 293 | + Expr block_dim; | ||
| 294 | + bool has_block_dim = false; | ||
| 295 | + for (const auto &block_axes : tuning_space_->block_dims) { | ||
| 296 | + Expr group_dim = CreateExpr(1U); | ||
| 297 | + for (const auto *axis : block_axes) { | ||
| 298 | + if (axis == nullptr) { | ||
| 299 | + return Expr(); | ||
| 300 | + } | ||
| 301 | + Expr axis_dim = axis->repeat; | ||
| 302 | + if (!axis_dim.IsValid()) { | ||
| 303 | + return Expr(); | ||
| 304 | + } | ||
| 305 | + group_dim = af::sym::Mul(group_dim, axis_dim); | ||
| 306 | + } | ||
| 307 | + block_dim = has_block_dim ? af::sym::Max(block_dim, group_dim) : group_dim; | ||
| 308 | + has_block_dim = true; | ||
| 309 | + } | ||
| 310 | + return block_dim.Simplify(); | ||
| 311 | +} | ||
| 312 | + | ||
| 192 | /* | 313 | /* |
| 193 | brc缓存执行逻辑: | 314 | brc缓存执行逻辑: |
| 194 | 1.递归计算节点的输入信息,将每一个节点与一个或多个Data节点绑定 | 315 | 1.递归计算节点的输入信息,将每一个节点与一个或多个Data节点绑定 |
| @@ -243,6 +364,20 @@ TernaryOp ExeTimePassManager::HandleReduceOrNormalSplit(const NodeInfo &node, co | |||
| 243 | } | 364 | } |
| 244 | 365 | ||
| 245 | TernaryOp ExeTimePassManager::UpdateNodeExeTime(const NodeInfo &node, const Expr &exe_time) const { | 366 | TernaryOp ExeTimePassManager::UpdateNodeExeTime(const NodeInfo &node, const Expr &exe_time) const { |
| 367 | + const bool is_cache_guard = node.exec_condition == af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis || | ||
| 368 | + node.exec_condition == af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis; | ||
| 369 | + CacheGuardInfo guard_info; | ||
| 370 | + if (TryBuildCacheGuardInfo(node, guard_info) && guard_info.address_invariant) { | ||
| 371 | + const auto block_dim = GetBlockDimExpr(); | ||
| 372 | + if (IsBlockCountUniform(guard_info, block_dim)) { | ||
| 373 | + return TernaryOp(CountGuardHits(guard_info, CreateExpr(0U))); | ||
| 374 | + } | ||
| 375 | + if (is_cache_guard) { | ||
| 376 | + GELOGD("Node[%s] cache guard exe-time fallback: block hit count is not uniform.", node.name.c_str()); | ||
| 377 | + } | ||
| 378 | + } else if (is_cache_guard) { | ||
| 379 | + GELOGD("Node[%s] cache guard exe-time fallback: metadata unavailable or address is variant.", node.name.c_str()); | ||
| 380 | + } | ||
| 246 | if (brc_buf_node_.find(node.name) == brc_buf_node_.end()) { | 381 | if (brc_buf_node_.find(node.name) == brc_buf_node_.end()) { |
| 247 | return TernaryOp(exe_time); | 382 | return TernaryOp(exe_time); |
| 248 | } | 383 | } |
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <set> | 14 | #include <set> |
| 15 | #include "util/ternary_op.h" | 15 | #include "util/ternary_op.h" |
| 16 | #include "parser/tuning_space.h" | 16 | #include "parser/tuning_space.h" |
| 17 | +#include "expr_gen/cache_guard_count.h" | ||
| 17 | 18 | ||
| 18 | namespace att { | 19 | namespace att { |
| 19 | class ExeTimePassManager { | 20 | class ExeTimePassManager { |
| @@ -39,6 +40,9 @@ class ExeTimePassManager { | |||
| 39 | // 获取需要处理的节点 | 40 | // 获取需要处理的节点 |
| 40 | TernaryOp UpdateNodeExeTime(const NodeInfo &node, const Expr &exe_time) const; | 41 | TernaryOp UpdateNodeExeTime(const NodeInfo &node, const Expr &exe_time) const; |
| 41 | 42 | ||
| 43 | + // 从 parser 已有的轴关系中重建 cache guard 元数据;无法唯一确定时返回 false。 | ||
| 44 | + bool TryBuildCacheGuardInfo(const NodeInfo &node, CacheGuardInfo &info) const; | ||
| 45 | + | ||
| 42 | private: | 46 | private: |
| 43 | void CheckReduce(const NodeInfo &node); | 47 | void CheckReduce(const NodeInfo &node); |
| 44 | void CheckBroadcast(const NodeInfo &node); | 48 | void CheckBroadcast(const NodeInfo &node); |
| @@ -47,6 +51,7 @@ class ExeTimePassManager { | |||
| 47 | void UpdateBufNode(const std::vector<NodeInfo> &nodes); | 51 | void UpdateBufNode(const std::vector<NodeInfo> &nodes); |
| 48 | void GenLog(const std::string &type_name, const std::map<std::string, std::set<std::string>> &axis_list) const; | 52 | void GenLog(const std::string &type_name, const std::map<std::string, std::set<std::string>> &axis_list) const; |
| 49 | bool GetRLoop(const NodeInfo &node, Expr &r_loop) const; | 53 | bool GetRLoop(const NodeInfo &node, Expr &r_loop) const; |
| 54 | + Expr GetBlockDimExpr() const; | ||
| 50 | bool CheckAxisSplit(const NodeInfo &node, const SubAxis *axis, Expr *fused_axis) const; | 55 | bool CheckAxisSplit(const NodeInfo &node, const SubAxis *axis, Expr *fused_axis) const; |
| 51 | TernaryOp HandleBroadcastSplit(const NodeInfo &node, const Expr &exe_time, const Expr &fused_exe_time) const; | 56 | TernaryOp HandleBroadcastSplit(const NodeInfo &node, const Expr &exe_time, const Expr &fused_exe_time) const; |
| 52 | TernaryOp HandleReduceOrNormalSplit(const NodeInfo &node, const Expr &exe_time, const SubAxis *axis, bool r_split, | 57 | TernaryOp HandleReduceOrNormalSplit(const NodeInfo &node, const Expr &exe_time, const SubAxis *axis, bool r_split, |
| @@ -40,6 +40,21 @@ bool HasComputeType(const ascir::ImplGraph &impl_graph, const af::ComputeType co | |||
| 40 | return false; | 40 | return false; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | +bool ShouldPreserveCacheCondition(const af::AscNodePtr &node, const bool graph_has_reduce) { | ||
| 44 | + if (!graph_has_reduce) { | ||
| 45 | + return true; | ||
| 46 | + } | ||
| 47 | + if (node == nullptr) { | ||
| 48 | + return false; | ||
| 49 | + } | ||
| 50 | + // Codegen evaluates cache eligibility per node in a Reduce graph. Keep the | ||
| 51 | + // condition for data movement/broadcast nodes so ATT models the same Nddma | ||
| 52 | + // (Load converted to Nddma) execution count; reduction nodes retain the | ||
| 53 | + // historical conservative behavior. | ||
| 54 | + const auto &type = node->GetType(); | ||
| 55 | + return type == kLoad || type == "Nddma" || type == kBroadcast; | ||
| 56 | +} | ||
| 57 | + | ||
| 43 | // 通过输出获取当前节点单次执行涉及到的轴的范围 | 58 | // 通过输出获取当前节点单次执行涉及到的轴的范围 |
| 44 | std::vector<int64_t> GetNodeVectorizedAxis(const af::AscNodePtr &ge_node, int64_t loop_axis_id) { | 59 | std::vector<int64_t> GetNodeVectorizedAxis(const af::AscNodePtr &ge_node, int64_t loop_axis_id) { |
| 45 | std::vector<int64_t> total_vectorized_axis; | 60 | std::vector<int64_t> total_vectorized_axis; |
| @@ -783,7 +798,9 @@ af::Status AscendGraphParser::ConvertNodeInfos(const af::AscNodePtr &ge_node, co | |||
| 783 | NodeInfo node_info; | 798 | NodeInfo node_info; |
| 784 | node_info.name = ge_node->GetName(); | 799 | node_info.name = ge_node->GetName(); |
| 785 | node_info.node_type = ge_node->GetType(); | 800 | node_info.node_type = ge_node->GetType(); |
| 786 | - node_info.exec_condition = use_cache_flag ? attrs.exec_condition : af::ExecuteCondition::kNoCache; | 801 | + const bool preserve_cache_condition = |
| 802 | + use_cache_flag || ShouldPreserveCacheCondition(ge_node, /*graph_has_reduce=*/!use_cache_flag); | ||
| 803 | + node_info.exec_condition = preserve_cache_condition ? attrs.exec_condition : af::ExecuteCondition::kNoCache; | ||
| 787 | if (kUnitMap.find(ge_node->attr.api.unit) == kUnitMap.end()) { | 804 | if (kUnitMap.find(ge_node->attr.api.unit) == kUnitMap.end()) { |
| 788 | node_info.node_unit = "UnitInvalid"; | 805 | node_info.node_unit = "UnitInvalid"; |
| 789 | } else { | 806 | } else { |
| @@ -21,6 +21,7 @@ | |||
| 21 | #include "mmpa/mmpa_api.h" | 21 | #include "mmpa/mmpa_api.h" |
| 22 | #include "base_types_printer.h" | 22 | #include "base_types_printer.h" |
| 23 | #include "base/att_const_values.h" | 23 | #include "base/att_const_values.h" |
| 24 | +#include "base/att_group_perf_config.h" | ||
| 24 | #include "common_utils.h" | 25 | #include "common_utils.h" |
| 25 | #include "generator_utils/tilingdata_gen_utils.h" | 26 | #include "generator_utils/tilingdata_gen_utils.h" |
| 26 | #include "tiling_data_gen/tiling_data_generator.h" | 27 | #include "tiling_data_gen/tiling_data_generator.h" |
| @@ -3591,12 +3592,102 @@ std::string TilingCodeGenImpl::GenPerfUpdateCode(const std::vector<std::string> | |||
| 3591 | return update_code; | 3592 | return update_code; |
| 3592 | } | 3593 | } |
| 3593 | 3594 | ||
| 3595 | +std::vector<std::pair<size_t, size_t>> TilingCodeGenImpl::GetSerializedGroupEdges( | ||
| 3596 | + const std::map<size_t, std::pair<std::string, std::string>> &graph_info, | ||
| 3597 | + const std::map<size_t, std::map<size_t, std::map<std::string, af::Expression>>> &var_relation, | ||
| 3598 | + const bool enable_group_parallel) const { | ||
| 3599 | + if (enable_group_parallel) { | ||
| 3600 | + return {}; | ||
| 3601 | + } | ||
| 3602 | + | ||
| 3603 | + std::set<std::pair<size_t, size_t>> edge_set; | ||
| 3604 | + for (const auto &dst_relation : var_relation) { | ||
| 3605 | + const size_t dst_id = dst_relation.first; | ||
| 3606 | + if (graph_info.find(dst_id) == graph_info.end()) { | ||
| 3607 | + continue; | ||
| 3608 | + } | ||
| 3609 | + for (const auto &src_relation : dst_relation.second) { | ||
| 3610 | + const size_t src_id = src_relation.first; | ||
| 3611 | + if (!src_relation.second.empty() && src_id != dst_id && graph_info.find(src_id) != graph_info.end()) { | ||
| 3612 | + edge_set.emplace(src_id, dst_id); | ||
| 3613 | + } | ||
| 3614 | + } | ||
| 3615 | + } | ||
| 3616 | + return {edge_set.begin(), edge_set.end()}; | ||
| 3617 | +} | ||
| 3618 | + | ||
| 3619 | +std::vector<std::pair<size_t, size_t>> TilingCodeGenImpl::GetWorkspaceSharedGroupEdges( | ||
| 3620 | + const size_t asc_graph_id, const size_t impl_graph_id, | ||
| 3621 | + const std::map<size_t, std::pair<std::string, std::string>> &graph_info) const { | ||
| 3622 | + std::map<size_t, std::set<int64_t>> group_workspace_ids; | ||
| 3623 | + for (const auto &model_info : tiling_model_info_) { | ||
| 3624 | + const auto &ident = model_info.schedule_group_ident; | ||
| 3625 | + if (ident.asc_graph_id != asc_graph_id || ident.impl_graph_id != impl_graph_id || | ||
| 3626 | + graph_info.find(ident.group_id) == graph_info.end()) { | ||
| 3627 | + continue; | ||
| 3628 | + } | ||
| 3629 | + auto &tensor_ids = group_workspace_ids[ident.group_id]; | ||
| 3630 | + for (const auto &workspace : model_info.workspace_size_map) { | ||
| 3631 | + tensor_ids.insert(workspace.first); | ||
| 3632 | + } | ||
| 3633 | + } | ||
| 3634 | + std::vector<std::pair<size_t, size_t>> edges; | ||
| 3635 | + for (auto lhs = group_workspace_ids.begin(); lhs != group_workspace_ids.end(); ++lhs) { | ||
| 3636 | + auto rhs = lhs; | ||
| 3637 | + for (++rhs; rhs != group_workspace_ids.end(); ++rhs) { | ||
| 3638 | + const auto &smaller = (lhs->second.size() <= rhs->second.size()) ? lhs->second : rhs->second; | ||
| 3639 | + const auto &larger = (lhs->second.size() <= rhs->second.size()) ? rhs->second : lhs->second; | ||
| 3640 | + const bool shared = std::any_of(smaller.cbegin(), smaller.cend(), | ||
| 3641 | + [&larger](const int64_t tensor_id) { return larger.count(tensor_id) != 0U; }); | ||
| 3642 | + if (shared) { | ||
| 3643 | + edges.emplace_back(lhs->first, rhs->first); | ||
| 3644 | + } | ||
| 3645 | + } | ||
| 3646 | + } | ||
| 3647 | + return edges; | ||
| 3648 | +} | ||
| 3649 | + | ||
| 3650 | +std::string TilingCodeGenImpl::GenSerializedEdgePenalty(const std::vector<std::string> &groups_perf, | ||
| 3651 | + const std::vector<std::pair<size_t, size_t>> &serialized_edges, | ||
| 3652 | + const std::string &indent) const { | ||
| 3653 | + std::vector<std::pair<size_t, size_t>> valid_edges; | ||
| 3654 | + valid_edges.reserve(serialized_edges.size()); | ||
| 3655 | + for (const auto &edge : serialized_edges) { | ||
| 3656 | + if (edge.first >= groups_perf.size() || edge.second >= groups_perf.size()) { | ||
| 3657 | + return {}; | ||
| 3658 | + } | ||
| 3659 | + valid_edges.emplace_back(edge); | ||
| 3660 | + } | ||
| 3661 | + if (valid_edges.empty()) { | ||
| 3662 | + return {}; | ||
| 3663 | + } | ||
| 3664 | + | ||
| 3665 | + const std::string ratio = std::to_string(SerializedEdgePenaltyConfig::kPenaltyRatio); | ||
| 3666 | + const std::string cap = std::to_string(SerializedEdgePenaltyConfig::kPenaltyCapCycles); | ||
| 3667 | + std::string code = indent + "double serialized_edge_base = 0.0;\n"; | ||
| 3668 | + for (const auto &edge : valid_edges) { | ||
| 3669 | + code += indent + "serialized_edge_base += std::max(" + groups_perf[edge.first] + ", " + groups_perf[edge.second] + | ||
| 3670 | + ");\n"; | ||
| 3671 | + } | ||
| 3672 | + code += indent + "double serialized_edge_penalty = std::min(" + cap + ", " + ratio + " * serialized_edge_base);\n"; | ||
| 3673 | + code += indent + "cur_perf += serialized_edge_penalty;\n"; | ||
| 3674 | + code += indent + | ||
| 3675 | + "OP_LOGD(OP_NAME, \"serialized_edge_model=" + std::string(SerializedEdgePenaltyConfig::kModelVersion) + | ||
| 3676 | + " serialized_edge_penalty=min(" + cap + ", " + ratio + | ||
| 3677 | + " * serialized_edge_base)=%lf (base=%lf)\", serialized_edge_penalty, serialized_edge_base);\n"; | ||
| 3678 | + return code; | ||
| 3679 | +} | ||
| 3680 | + | ||
| 3594 | void TilingCodeGenImpl::GenBestPerfUpdateCode(const size_t asc_graph_id, const size_t impl_graph_id, | 3681 | void TilingCodeGenImpl::GenBestPerfUpdateCode(const size_t asc_graph_id, const size_t impl_graph_id, |
| 3595 | const std::vector<std::string> &assign_max_block_num, | 3682 | const std::vector<std::string> &assign_max_block_num, |
| 3596 | - const std::string &indent) { | 3683 | + const std::string &indent, const std::string &objective_log) { |
| 3597 | std::string tiling_key_prefix = "graph" + std::to_string(asc_graph_id) + "_"; | 3684 | std::string tiling_key_prefix = "graph" + std::to_string(asc_graph_id) + "_"; |
| 3598 | - tiling_func_.AddLine(indent + "OP_LOGI(OP_NAME, \"The value of graph" + std::to_string(asc_graph_id) + "_result" + | 3685 | + if (objective_log.empty()) { |
| 3599 | - std::to_string(impl_graph_id) + " is %lf\", cur_perf);"); | 3686 | + tiling_func_.AddLine(indent + "OP_LOGI(OP_NAME, \"The value of graph" + std::to_string(asc_graph_id) + "_result" + |
| 3687 | + std::to_string(impl_graph_id) + " is %lf\", cur_perf);"); | ||
| 3688 | + } else { | ||
| 3689 | + tiling_func_.AddLine(objective_log); | ||
| 3690 | + } | ||
| 3600 | tiling_func_.AddLine(indent + "if (IsEqual(best_perf, -1) || cur_perf < best_perf) {"); | 3691 | tiling_func_.AddLine(indent + "if (IsEqual(best_perf, -1) || cur_perf < best_perf) {"); |
| 3601 | tiling_func_.AddLine(indent + " best_perf = cur_perf;"); | 3692 | tiling_func_.AddLine(indent + " best_perf = cur_perf;"); |
| 3602 | for (const auto &code : assign_max_block_num) { | 3693 | for (const auto &code : assign_max_block_num) { |
| @@ -3617,14 +3708,58 @@ void TilingCodeGenImpl::GenBestPerfUpdateCode(const size_t asc_graph_id, const s | |||
| 3617 | 3708 | ||
| 3618 | af::Status TilingCodeGenImpl::GenUpdatePerf(const size_t asc_graph_id, const size_t impl_graph_id, | 3709 | af::Status TilingCodeGenImpl::GenUpdatePerf(const size_t asc_graph_id, const size_t impl_graph_id, |
| 3619 | const std::vector<std::string> &groups_perf, | 3710 | const std::vector<std::string> &groups_perf, |
| 3711 | + const std::vector<std::pair<size_t, size_t>> &serialized_edges, | ||
| 3620 | const std::vector<std::string> &groups_block_num, | 3712 | const std::vector<std::string> &groups_block_num, |
| 3621 | const std::vector<std::string> &assign_max_block_num) { | 3713 | const std::vector<std::string> &assign_max_block_num) { |
| 3714 | + std::string objective_log; | ||
| 3622 | if (!IsScheduleResultEnableParallel(asc_graph_id, impl_graph_id)) { | 3715 | if (!IsScheduleResultEnableParallel(asc_graph_id, impl_graph_id)) { |
| 3623 | tiling_func_.AddLine(GenSumAllGroupsPerf(groups_perf)); | 3716 | tiling_func_.AddLine(GenSumAllGroupsPerf(groups_perf)); |
| 3717 | + tiling_func_.AddLine(" double group_sum = cur_perf;"); | ||
| 3718 | + bool edge_model_enabled = false; | ||
| 3719 | + if (!serialized_edges.empty()) { | ||
| 3720 | + const auto penalty_code = GenSerializedEdgePenalty(groups_perf, serialized_edges, " "); | ||
| 3721 | + if (!penalty_code.empty()) { | ||
| 3722 | + edge_model_enabled = true; | ||
| 3723 | + tiling_func_.AddLine(penalty_code); | ||
| 3724 | + tiling_func_.AddLine( | ||
| 3725 | + " OP_LOGD(OP_NAME, \"objective=group_sum(%lf)+edge_penalty(%lf)=%lf\", group_sum, " | ||
| 3726 | + "serialized_edge_penalty, cur_perf);"); | ||
| 3727 | + } | ||
| 3728 | + } | ||
| 3729 | + | ||
| 3730 | + std::string group_formula; | ||
| 3731 | + for (size_t i = 0U; i < groups_perf.size(); ++i) { | ||
| 3732 | + if (i != 0U) { | ||
| 3733 | + group_formula += " + "; | ||
| 3734 | + } | ||
| 3735 | + group_formula += "group" + std::to_string(i); | ||
| 3736 | + } | ||
| 3737 | + std::string edge_base_formula = "0"; | ||
| 3738 | + if (edge_model_enabled) { | ||
| 3739 | + edge_base_formula.clear(); | ||
| 3740 | + for (size_t i = 0U; i < serialized_edges.size(); ++i) { | ||
| 3741 | + if (i != 0U) { | ||
| 3742 | + edge_base_formula += " + "; | ||
| 3743 | + } | ||
| 3744 | + edge_base_formula += "max(group" + std::to_string(serialized_edges[i].first) + ", group" + | ||
| 3745 | + std::to_string(serialized_edges[i].second) + ")"; | ||
| 3746 | + } | ||
| 3747 | + } | ||
| 3748 | + objective_log = " OP_LOGI(OP_NAME, \"The value of graph" + std::to_string(asc_graph_id) + "_result" + | ||
| 3749 | + std::to_string(impl_graph_id) + " is %lf objective(group_sum(" + group_formula + | ||
| 3750 | + ")=%lf + edge_model(" + std::string(SerializedEdgePenaltyConfig::kModelVersion) + | ||
| 3751 | + ", base=" + edge_base_formula + "=%lf, penalty=min(" + | ||
| 3752 | + std::to_string(SerializedEdgePenaltyConfig::kPenaltyCapCycles) + ", " + | ||
| 3753 | + std::to_string(SerializedEdgePenaltyConfig::kPenaltyRatio) + "*base)=%lf)=%lf\", cur_perf, "; | ||
| 3754 | + if (edge_model_enabled) { | ||
| 3755 | + objective_log += "group_sum, serialized_edge_base, serialized_edge_penalty, cur_perf);"; | ||
| 3756 | + } else { | ||
| 3757 | + objective_log += "group_sum, 0.0, 0.0, cur_perf);"; | ||
| 3758 | + } | ||
| 3624 | } else { | 3759 | } else { |
| 3625 | tiling_func_.AddLine(GenPerfUpdateCode(groups_perf, groups_block_num, " ")); | 3760 | tiling_func_.AddLine(GenPerfUpdateCode(groups_perf, groups_block_num, " ")); |
| 3626 | } | 3761 | } |
| 3627 | - GenBestPerfUpdateCode(asc_graph_id, impl_graph_id, assign_max_block_num, " "); | 3762 | + GenBestPerfUpdateCode(asc_graph_id, impl_graph_id, assign_max_block_num, " ", objective_log); |
| 3628 | tiling_func_.AddLine(" }"); // 闭合 GetScheduleResult 函数 | 3763 | tiling_func_.AddLine(" }"); // 闭合 GetScheduleResult 函数 |
| 3629 | return af::SUCCESS; | 3764 | return af::SUCCESS; |
| 3630 | } | 3765 | } |
| @@ -3811,11 +3946,12 @@ af::Status TilingCodeGenImpl::GenSingleGroupScheduleResult( | |||
| 3811 | const size_t asc_graph_id, const size_t impl_graph_id, | 3946 | const size_t asc_graph_id, const size_t impl_graph_id, |
| 3812 | const std::map<size_t, std::pair<std::string, std::string>> &graph_info, | 3947 | const std::map<size_t, std::pair<std::string, std::string>> &graph_info, |
| 3813 | const std::map<std::string, std::set<std::string>> &hardware_map) { | 3948 | const std::map<std::string, std::set<std::string>> &hardware_map) { |
| 3814 | - const auto var_relation = var_relations_[asc_graph_id][impl_graph_id]; | 3949 | + const auto &var_relation = var_relations_[asc_graph_id][impl_graph_id]; |
| 3815 | std::string check_cond; | 3950 | std::string check_cond; |
| 3816 | std::vector<std::string> assign_max_block_num; | 3951 | std::vector<std::string> assign_max_block_num; |
| 3817 | std::vector<std::string> groups_perf; | 3952 | std::vector<std::string> groups_perf; |
| 3818 | std::vector<std::string> groups_block_num; | 3953 | std::vector<std::string> groups_block_num; |
| 3954 | + std::map<size_t, size_t> group_perf_positions; | ||
| 3819 | for (const auto &group_info : graph_info) { | 3955 | for (const auto &group_info : graph_info) { |
| 3820 | auto [input_vars_set_code, need_update_second_group_input_vars] = | 3956 | auto [input_vars_set_code, need_update_second_group_input_vars] = |
| 3821 | ProcessVarRelations(graph_info, var_relation, group_info.first); | 3957 | ProcessVarRelations(graph_info, var_relation, group_info.first); |
| @@ -3834,6 +3970,7 @@ af::Status TilingCodeGenImpl::GenSingleGroupScheduleResult( | |||
| 3834 | "Gen schedule group do tiling failed, graph id[%zu], impl id[%zu]", asc_graph_id, | 3970 | "Gen schedule group do tiling failed, graph id[%zu], impl id[%zu]", asc_graph_id, |
| 3835 | impl_graph_id); | 3971 | impl_graph_id); |
| 3836 | groups_perf.emplace_back(GenGetScheduleGroupPerf(group_info.second.first, group_info.second.second)); | 3972 | groups_perf.emplace_back(GenGetScheduleGroupPerf(group_info.second.first, group_info.second.second)); |
| 3973 | + group_perf_positions[group_info.first] = groups_perf.size() - 1U; | ||
| 3837 | assign_max_block_num.emplace_back(GenCurMaxBlockDim(group_info.second.second, groups_block_num, cur_block)); | 3974 | assign_max_block_num.emplace_back(GenCurMaxBlockDim(group_info.second.second, groups_block_num, cur_block)); |
| 3838 | groups_block_num.emplace_back(GenGetCurBlockDim(group_info.second.second)); | 3975 | groups_block_num.emplace_back(GenGetCurBlockDim(group_info.second.second)); |
| 3839 | } | 3976 | } |
| @@ -3842,8 +3979,27 @@ af::Status TilingCodeGenImpl::GenSingleGroupScheduleResult( | |||
| 3842 | impl_graph_id); | 3979 | impl_graph_id); |
| 3843 | GE_ASSERT_EQ(groups_block_num.size(), groups_perf.size()); | 3980 | GE_ASSERT_EQ(groups_block_num.size(), groups_perf.size()); |
| 3844 | tiling_func_.AddLine(" if (" + (check_cond.empty() ? "true" : check_cond) + ") {"); | 3981 | tiling_func_.AddLine(" if (" + (check_cond.empty() ? "true" : check_cond) + ") {"); |
| 3845 | - GE_ASSERT_SUCCESS(GenUpdatePerf(asc_graph_id, impl_graph_id, groups_perf, groups_block_num, assign_max_block_num), | 3982 | + const bool enable_group_parallel = IsScheduleResultEnableParallel(asc_graph_id, impl_graph_id); |
| 3846 | - "Gen update perf failed, asc_graph_id %zu impl_graph_id %zu", asc_graph_id, impl_graph_id); | 3983 | + std::vector<std::pair<size_t, size_t>> group_edges = |
| 3984 | + GetSerializedGroupEdges(graph_info, var_relation, enable_group_parallel); | ||
| 3985 | + if (!enable_group_parallel) { | ||
| 3986 | + const auto workspace_edges = GetWorkspaceSharedGroupEdges(asc_graph_id, impl_graph_id, graph_info); | ||
| 3987 | + std::set<std::pair<size_t, size_t>> merged_edges(group_edges.cbegin(), group_edges.cend()); | ||
| 3988 | + merged_edges.insert(workspace_edges.cbegin(), workspace_edges.cend()); | ||
| 3989 | + group_edges.assign(merged_edges.cbegin(), merged_edges.cend()); | ||
| 3990 | + } | ||
| 3991 | + std::vector<std::pair<size_t, size_t>> serialized_edges; | ||
| 3992 | + serialized_edges.reserve(group_edges.size()); | ||
| 3993 | + for (const auto &edge : group_edges) { | ||
| 3994 | + const auto src_pos = group_perf_positions.find(edge.first); | ||
| 3995 | + const auto dst_pos = group_perf_positions.find(edge.second); | ||
| 3996 | + if (src_pos != group_perf_positions.end() && dst_pos != group_perf_positions.end()) { | ||
| 3997 | + serialized_edges.emplace_back(src_pos->second, dst_pos->second); | ||
| 3998 | + } | ||
| 3999 | + } | ||
| 4000 | + GE_ASSERT_SUCCESS( | ||
| 4001 | + GenUpdatePerf(asc_graph_id, impl_graph_id, groups_perf, serialized_edges, groups_block_num, assign_max_block_num), | ||
| 4002 | + "Gen update perf failed, asc_graph_id %zu impl_graph_id %zu", asc_graph_id, impl_graph_id); | ||
| 3847 | GenGetScheduleResultTail(graph_info); | 4003 | GenGetScheduleResultTail(graph_info); |
| 3848 | return af::SUCCESS; | 4004 | return af::SUCCESS; |
| 3849 | } | 4005 | } |
| @@ -141,6 +141,7 @@ class TilingCodeGenImpl { | |||
| 141 | const std::map<std::string, std::set<std::string>> &hardware_map); | 141 | const std::map<std::string, std::set<std::string>> &hardware_map); |
| 142 | af::Status GenUpdatePerf(const size_t asc_graph_id, const size_t impl_graph_id, | 142 | af::Status GenUpdatePerf(const size_t asc_graph_id, const size_t impl_graph_id, |
| 143 | const std::vector<std::string> &groups_perf, | 143 | const std::vector<std::string> &groups_perf, |
| 144 | + const std::vector<std::pair<size_t, size_t>> &serialized_edges, | ||
| 144 | const std::vector<std::string> &groups_block_num, | 145 | const std::vector<std::string> &groups_block_num, |
| 145 | const std::vector<std::string> &assign_max_block_num); | 146 | const std::vector<std::string> &assign_max_block_num); |
| 146 | void GenGetMaxScoreIndex(const AscGraphNamepspaceMap &namespace_map); | 147 | void GenGetMaxScoreIndex(const AscGraphNamepspaceMap &namespace_map); |
| @@ -320,9 +321,20 @@ class TilingCodeGenImpl { | |||
| 320 | // 辅助函数:生成perf更新代码(消除GenUpdatePerf和GenGetScheduleResultPerfAndTail的重复) | 321 | // 辅助函数:生成perf更新代码(消除GenUpdatePerf和GenGetScheduleResultPerfAndTail的重复) |
| 321 | static std::string GenPerfUpdateCode(const std::vector<std::string> &groups_perf, | 322 | static std::string GenPerfUpdateCode(const std::vector<std::string> &groups_perf, |
| 322 | const std::vector<std::string> &groups_block_num, const std::string &indent); | 323 | const std::vector<std::string> &groups_block_num, const std::string &indent); |
| 324 | + std::vector<std::pair<size_t, size_t>> GetSerializedGroupEdges( | ||
| 325 | + const std::map<size_t, std::pair<std::string, std::string>> &graph_info, | ||
| 326 | + const std::map<size_t, std::map<size_t, std::map<std::string, af::Expression>>> &var_relation, | ||
| 327 | + bool enable_group_parallel) const; | ||
| 328 | + std::vector<std::pair<size_t, size_t>> GetWorkspaceSharedGroupEdges( | ||
| 329 | + const size_t asc_graph_id, const size_t impl_graph_id, | ||
| 330 | + const std::map<size_t, std::pair<std::string, std::string>> &graph_info) const; | ||
| 331 | + std::string GenSerializedEdgePenalty(const std::vector<std::string> &groups_perf, | ||
| 332 | + const std::vector<std::pair<size_t, size_t>> &serialized_edges, | ||
| 333 | + const std::string &indent) const; | ||
| 323 | // 辅助函数:生成best perf更新代码 | 334 | // 辅助函数:生成best perf更新代码 |
| 324 | void GenBestPerfUpdateCode(const size_t asc_graph_id, const size_t impl_graph_id, | 335 | void GenBestPerfUpdateCode(const size_t asc_graph_id, const size_t impl_graph_id, |
| 325 | - const std::vector<std::string> &assign_max_block_num, const std::string &indent); | 336 | + const std::vector<std::string> &assign_max_block_num, const std::string &indent, |
| 337 | + const std::string &objective_log = {}); | ||
| 326 | 338 | ||
| 327 | // 为所有group生成cache line冲突检测helper函数 | 339 | // 为所有group生成cache line冲突检测helper函数 |
| 328 | af::Status GenConflictGroupHelpers(const size_t asc_graph_id, const size_t impl_graph_id, | 340 | af::Status GenConflictGroupHelpers(const size_t asc_graph_id, const size_t impl_graph_id, |
| @@ -8,14 +8,289 @@ | |||
| 8 | * See LICENSE in the root of the software repository for the full text of the License. | 8 | * See LICENSE in the root of the software repository for the full text of the License. |
| 9 | */ | 9 | */ |
| 10 | #include <iostream> | 10 | #include <iostream> |
| 11 | +#include <limits> | ||
| 11 | #include "gtest/gtest.h" | 12 | #include "gtest/gtest.h" |
| 12 | #include "test_brc_buf_graph.h" | 13 | #include "test_brc_buf_graph.h" |
| 13 | #include "parser/ascend_graph_parser.h" | 14 | #include "parser/ascend_graph_parser.h" |
| 14 | #include "expr_gen/arg_list_manager.h" | 15 | #include "expr_gen/arg_list_manager.h" |
| 15 | #include "expr_gen/exe_time_pass.h" | 16 | #include "expr_gen/exe_time_pass.h" |
| 17 | +#include "expr_gen/cache_guard_count.h" | ||
| 16 | #include "graph_construct_utils.h" | 18 | #include "graph_construct_utils.h" |
| 17 | 19 | ||
| 18 | namespace att { | 20 | namespace att { |
| 21 | +namespace { | ||
| 22 | +Expr Symbol(const char *name) { | ||
| 23 | + return af::Expression::Parse(name); | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +CacheGuardInfo MakeOriginGuard(const Expr &guard_axis, const Expr &loop_extent) { | ||
| 27 | + return {CacheGuardKind::kOriginBroadcast, guard_axis, Expr(), Expr(), loop_extent, true, true}; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +CacheGuardInfo MakeFusedGuard(const Expr &guard_axis, const Expr &period, const Expr &block_inner_extent, | ||
| 31 | + const Expr &loop_extent) { | ||
| 32 | + return {CacheGuardKind::kFusedBroadcast, guard_axis, period, block_inner_extent, loop_extent, true, true}; | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +uint64_t EvaluateConst(const Expr &expr) { | ||
| 36 | + uint64_t value = 0; | ||
| 37 | + EXPECT_TRUE(expr.GetConstValue(value)); | ||
| 38 | + return value; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +TuningSpacePtr MakeGuardTuningSpace(const Expr &period, const Expr &inner_extent, const Expr &block_dim) { | ||
| 42 | + auto tuning_space = std::make_shared<TuningSpace>(); | ||
| 43 | + auto origin = std::make_unique<SubAxis>(); | ||
| 44 | + origin->id = 1; | ||
| 45 | + origin->name = "guarded"; | ||
| 46 | + origin->repeat = inner_extent; | ||
| 47 | + auto outer = std::make_unique<SubAxis>(); | ||
| 48 | + outer->id = 2; | ||
| 49 | + outer->name = "outer"; | ||
| 50 | + outer->axis_type = AxisPosition::OUTER; | ||
| 51 | + outer->repeat = period; | ||
| 52 | + auto inner = std::make_unique<SubAxis>(); | ||
| 53 | + inner->id = 3; | ||
| 54 | + inner->name = "block_inner"; | ||
| 55 | + inner->axis_type = AxisPosition::INNER; | ||
| 56 | + inner->is_bind_multi_core = true; | ||
| 57 | + inner->repeat = inner_extent; | ||
| 58 | + inner->orig_axis = {origin.get()}; | ||
| 59 | + // Parser split outputs reference their common source axis through parent_axis. | ||
| 60 | + inner->parent_axis = {origin.get()}; | ||
| 61 | + outer->parent_axis = {origin.get()}; | ||
| 62 | + auto block = std::make_unique<SubAxis>(); | ||
| 63 | + block->id = 4; | ||
| 64 | + block->name = "block"; | ||
| 65 | + block->axis_type = AxisPosition::OUTER; | ||
| 66 | + block->repeat = block_dim; | ||
| 67 | + tuning_space->sub_axes.emplace_back(std::move(origin)); | ||
| 68 | + tuning_space->sub_axes.emplace_back(std::move(outer)); | ||
| 69 | + tuning_space->sub_axes.emplace_back(std::move(inner)); | ||
| 70 | + tuning_space->sub_axes.emplace_back(std::move(block)); | ||
| 71 | + tuning_space->block_dims = {{tuning_space->sub_axes[3].get()}}; | ||
| 72 | + NodeInfo node; | ||
| 73 | + node.name = "cast0"; | ||
| 74 | + node.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis; | ||
| 75 | + node.loop_axes = {tuning_space->sub_axes[2].get()}; | ||
| 76 | + tuning_space->node_infos.emplace_back(node); | ||
| 77 | + return tuning_space; | ||
| 78 | +} | ||
| 79 | +} // namespace | ||
| 80 | + | ||
| 81 | +TEST(CacheGuardInfoTest, OriginGuardCountsOneHit) { | ||
| 82 | + CacheGuardInfo info = MakeOriginGuard(Symbol("i"), Symbol("19")); | ||
| 83 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, Symbol("0"))), 1); | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +TEST(CacheGuardInfoTest, FusedGuardCountsPeriodHitsPerBlock) { | ||
| 87 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("4"), Symbol("2"), Symbol("6")); | ||
| 88 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, Symbol("0"))), 1); | ||
| 89 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, Symbol("1"))), 1); | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +TEST(CacheGuardInfoTest, FusedGuardReturnsZeroBeyondPeriod) { | ||
| 93 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("4"), Symbol("2"), Symbol("2")); | ||
| 94 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, Symbol("3"))), 0); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +TEST(CacheGuardInfoTest, InvalidPeriodDoesNotBuildModulo) { | ||
| 98 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("0"), Symbol("2"), Symbol("6")); | ||
| 99 | + EXPECT_FALSE(ValidateCacheGuardInfo(info)); | ||
| 100 | +} | ||
| 101 | + | ||
| 102 | +TEST(CacheGuardInfoTest, BlockDependentCountFallsBackUnlessUniform) { | ||
| 103 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("5"), Symbol("2"), Symbol("6")); | ||
| 104 | + EXPECT_FALSE(IsBlockCountUniform(info, Symbol("54"))); | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +TEST(CacheGuardInfoTest, SymbolicCanonicalBlockSplitProvesUniformity) { | ||
| 108 | + const auto period = Symbol("period"); | ||
| 109 | + const auto inner = Symbol("inner"); | ||
| 110 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), period, inner, inner); | ||
| 111 | + const auto block_dim = af::sym::Ceiling(af::sym::Div(period, inner)); | ||
| 112 | + EXPECT_TRUE(IsBlockCountUniform(info, block_dim)); | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +TEST(CacheGuardInfoTest, ArbitrarySymbolicBlockDimFallsBack) { | ||
| 116 | + CacheGuardInfo info = MakeFusedGuard(Symbol("period"), Symbol("inner"), Symbol("inner"), Symbol("inner")); | ||
| 117 | + EXPECT_FALSE(IsBlockCountUniform(info, Symbol("block_dim"))); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +TEST(CacheGuardInfoTest, UnitExtentTailMakesBlockCountNonUniform) { | ||
| 121 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("5"), Symbol("2"), Symbol("1")); | ||
| 122 | + EXPECT_FALSE(IsBlockCountUniform(info, Symbol("4"))); | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +TEST(CacheGuardInfoTest, UnitPeriodTailMakesBlockCountNonUniform) { | ||
| 126 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("1"), Symbol("2"), Symbol("2")); | ||
| 127 | + EXPECT_FALSE(IsBlockCountUniform(info, Symbol("2"))); | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +TEST(CacheGuardInfoTest, UnitPeriodUnitExtentCountsSingleHit) { | ||
| 131 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("1"), Symbol("2"), Symbol("1")); | ||
| 132 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, Symbol("0"))), 1); | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +TEST(CacheGuardInfoTest, RejectsNonPositiveLoopExtent) { | ||
| 136 | + EXPECT_FALSE(ValidateCacheGuardInfo(MakeOriginGuard(Symbol("i"), Symbol("0")))); | ||
| 137 | + EXPECT_FALSE(ValidateCacheGuardInfo(MakeOriginGuard(Symbol("i"), Symbol("-1")))); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +TEST(CacheGuardInfoTest, AllowsMixedSymbolicAndConstantMetadata) { | ||
| 141 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("period"), Symbol("2"), Symbol("extent")); | ||
| 142 | + EXPECT_TRUE(ValidateCacheGuardInfo(info)); | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +TEST(CacheGuardInfoTest, RejectsUnprovenSymbolicMetadata) { | ||
| 146 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("period"), Symbol("2"), Symbol("extent")); | ||
| 147 | + info.positive_range_proven = false; | ||
| 148 | + EXPECT_FALSE(ValidateCacheGuardInfo(info)); | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +TEST(CacheGuardInfoTest, RejectsNonIntegerConstantMetadata) { | ||
| 152 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), Symbol("2.5"), Symbol("2"), Symbol("6")); | ||
| 153 | + EXPECT_FALSE(ValidateCacheGuardInfo(info)); | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +TEST(CacheGuardInfoTest, NoneGuardSafelyReturnsZero) { | ||
| 157 | + CacheGuardInfo info; | ||
| 158 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, Symbol("0"))), 0); | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +TEST(CacheGuardInfoTest, OverflowingBlockRangeIsNotUniform) { | ||
| 162 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), CreateExpr(1U), | ||
| 163 | + CreateExpr(std::numeric_limits<uint64_t>::max() / 2U), CreateExpr(2U)); | ||
| 164 | + EXPECT_FALSE(IsBlockCountUniform(info, CreateExpr(3U))); | ||
| 165 | +} | ||
| 166 | + | ||
| 167 | +TEST(CacheGuardInfoTest, LargeLoopExtentUsesConstantTimeCounting) { | ||
| 168 | + CacheGuardInfo info = MakeFusedGuard(Symbol("i"), CreateExpr(4U), CreateExpr(2U), CreateExpr(1000000000000UL)); | ||
| 169 | + EXPECT_EQ(EvaluateConst(CountGuardHits(info, CreateExpr(0U))), 1); | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +TEST(CacheGuardInfoTest, RejectsRealExtentBeyondUint64) { | ||
| 173 | + EXPECT_FALSE(ValidateCacheGuardInfo(MakeOriginGuard(Symbol("i"), Symbol("18446744073709551616.0")))); | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +TEST(CacheGuardInfoTest, ReconstructsFusedGuardMetadataFromBlockAxes) { | ||
| 177 | + auto tuning_space = std::make_shared<TuningSpace>(); | ||
| 178 | + auto origin = std::make_unique<SubAxis>(); | ||
| 179 | + origin->id = 1; | ||
| 180 | + origin->name = "guarded"; | ||
| 181 | + origin->repeat = Symbol("19"); | ||
| 182 | + auto outer = std::make_unique<SubAxis>(); | ||
| 183 | + outer->id = 2; | ||
| 184 | + outer->name = "outer"; | ||
| 185 | + outer->axis_type = AxisPosition::OUTER; | ||
| 186 | + outer->repeat = Symbol("19"); | ||
| 187 | + auto inner = std::make_unique<SubAxis>(); | ||
| 188 | + inner->id = 3; | ||
| 189 | + inner->name = "block_inner"; | ||
| 190 | + inner->axis_type = AxisPosition::INNER; | ||
| 191 | + inner->is_bind_multi_core = true; | ||
| 192 | + inner->repeat = Symbol("19"); | ||
| 193 | + inner->orig_axis = {origin.get()}; | ||
| 194 | + inner->parent_axis = {origin.get()}; | ||
| 195 | + outer->parent_axis = {origin.get()}; | ||
| 196 | + tuning_space->sub_axes.emplace_back(std::move(origin)); | ||
| 197 | + tuning_space->sub_axes.emplace_back(std::move(outer)); | ||
| 198 | + tuning_space->sub_axes.emplace_back(std::move(inner)); | ||
| 199 | + | ||
| 200 | + NodeInfo node; | ||
| 201 | + node.name = "fused"; | ||
| 202 | + node.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis; | ||
| 203 | + node.loop_axes = {tuning_space->sub_axes[2].get()}; | ||
| 204 | + ExeTimePassManager manager(tuning_space); | ||
| 205 | + CacheGuardInfo info; | ||
| 206 | + ASSERT_TRUE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 207 | + EXPECT_EQ(info.kind, CacheGuardKind::kFusedBroadcast); | ||
| 208 | + EXPECT_EQ(Str(info.period), "19"); | ||
| 209 | + EXPECT_EQ(Str(info.block_inner_extent), "19"); | ||
| 210 | + EXPECT_EQ(Str(info.loop_extent), "19"); | ||
| 211 | + EXPECT_TRUE(info.positive_range_proven); | ||
| 212 | + | ||
| 213 | + // The block-outer sibling shares the source parent but is not the guard period. | ||
| 214 | + auto block_outer = std::make_unique<SubAxis>(); | ||
| 215 | + block_outer->id = 4; | ||
| 216 | + block_outer->name = "block_outer"; | ||
| 217 | + block_outer->axis_type = AxisPosition::OUTER; | ||
| 218 | + block_outer->is_bind_multi_core = true; | ||
| 219 | + block_outer->repeat = Symbol("54"); | ||
| 220 | + block_outer->parent_axis = {tuning_space->sub_axes[0].get()}; | ||
| 221 | + tuning_space->sub_axes.emplace_back(std::move(block_outer)); | ||
| 222 | + ASSERT_TRUE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 223 | + EXPECT_EQ(Str(info.period), "19"); | ||
| 224 | + | ||
| 225 | + auto second_outer = std::make_unique<SubAxis>(); | ||
| 226 | + second_outer->id = 5; | ||
| 227 | + second_outer->name = "outer_2"; | ||
| 228 | + second_outer->axis_type = AxisPosition::OUTER; | ||
| 229 | + second_outer->repeat = Symbol("19"); | ||
| 230 | + second_outer->parent_axis = {tuning_space->sub_axes[0].get()}; | ||
| 231 | + tuning_space->sub_axes.emplace_back(std::move(second_outer)); | ||
| 232 | + EXPECT_FALSE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +TEST(CacheGuardInfoTest, ReconstructsOriginGuardAndRejectsUnsafeAxes) { | ||
| 236 | + auto tuning_space = std::make_shared<TuningSpace>(); | ||
| 237 | + auto inner = std::make_unique<SubAxis>(); | ||
| 238 | + inner->id = 1; | ||
| 239 | + inner->name = "block_inner"; | ||
| 240 | + inner->axis_type = AxisPosition::INNER; | ||
| 241 | + inner->is_bind_multi_core = true; | ||
| 242 | + inner->repeat = Symbol("19"); | ||
| 243 | + tuning_space->sub_axes.emplace_back(std::move(inner)); | ||
| 244 | + NodeInfo node; | ||
| 245 | + node.name = "origin"; | ||
| 246 | + node.exec_condition = af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis; | ||
| 247 | + node.loop_axes = {tuning_space->sub_axes[0].get()}; | ||
| 248 | + ExeTimePassManager manager(tuning_space); | ||
| 249 | + CacheGuardInfo info; | ||
| 250 | + ASSERT_TRUE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 251 | + EXPECT_EQ(info.kind, CacheGuardKind::kOriginBroadcast); | ||
| 252 | + EXPECT_EQ(Str(info.loop_extent), "19"); | ||
| 253 | + | ||
| 254 | + node.exec_condition = af::ExecuteCondition::kNoCache; | ||
| 255 | + EXPECT_FALSE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 256 | + node.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis; | ||
| 257 | + EXPECT_FALSE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 258 | + tuning_space->sub_axes[0]->is_bind_multi_core = false; | ||
| 259 | + EXPECT_FALSE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 260 | +} | ||
| 261 | + | ||
| 262 | +TEST(CacheGuardInfoTest, RejectsGuardWhenBlockInnerIsNotLoopLeaf) { | ||
| 263 | + auto tuning_space = MakeGuardTuningSpace(Symbol("4"), Symbol("2"), Symbol("2")); | ||
| 264 | + auto trailing_axis = std::make_unique<SubAxis>(); | ||
| 265 | + trailing_axis->id = 5; | ||
| 266 | + trailing_axis->name = "trailing_inner"; | ||
| 267 | + trailing_axis->axis_type = AxisPosition::INNER; | ||
| 268 | + trailing_axis->repeat = Symbol("2"); | ||
| 269 | + tuning_space->sub_axes.emplace_back(std::move(trailing_axis)); | ||
| 270 | + auto &node = tuning_space->node_infos.front(); | ||
| 271 | + node.loop_axes.emplace_back(tuning_space->sub_axes.back().get()); | ||
| 272 | + ExeTimePassManager manager(tuning_space); | ||
| 273 | + CacheGuardInfo info; | ||
| 274 | + EXPECT_FALSE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +TEST(CacheGuardInfoTest, NddmaGuardAllowsTrailingVectorAxis) { | ||
| 278 | + auto tuning_space = MakeGuardTuningSpace(Symbol("4"), Symbol("2"), Symbol("2")); | ||
| 279 | + auto trailing_axis = std::make_unique<SubAxis>(); | ||
| 280 | + trailing_axis->id = 5; | ||
| 281 | + trailing_axis->name = "trailing_vector"; | ||
| 282 | + trailing_axis->axis_type = AxisPosition::INNER; | ||
| 283 | + trailing_axis->repeat = Symbol("2"); | ||
| 284 | + tuning_space->sub_axes.emplace_back(std::move(trailing_axis)); | ||
| 285 | + auto &node = tuning_space->node_infos.front(); | ||
| 286 | + node.node_type = "Nddma"; | ||
| 287 | + node.loop_axes.emplace_back(tuning_space->sub_axes.back().get()); | ||
| 288 | + ExeTimePassManager manager(tuning_space); | ||
| 289 | + CacheGuardInfo info; | ||
| 290 | + EXPECT_TRUE(manager.TryBuildCacheGuardInfo(node, info)); | ||
| 291 | + EXPECT_EQ(info.kind, CacheGuardKind::kFusedBroadcast); | ||
| 292 | +} | ||
| 293 | + | ||
| 19 | class TestExeTimePass : public ::testing::Test { | 294 | class TestExeTimePass : public ::testing::Test { |
| 20 | public: | 295 | public: |
| 21 | static void TearDownTestCase() { | 296 | static void TearDownTestCase() { |
| @@ -28,6 +303,58 @@ class TestExeTimePass : public ::testing::Test { | |||
| 28 | void TearDown() override {} | 303 | void TearDown() override {} |
| 29 | }; | 304 | }; |
| 30 | 305 | ||
| 306 | +TEST_F(TestExeTimePass, FusedGuardUsesHitCount) { | ||
| 307 | + auto tuning_space = MakeGuardTuningSpace(Symbol("4"), Symbol("2"), Symbol("2")); | ||
| 308 | + const auto &node = tuning_space->node_infos.front(); | ||
| 309 | + ExeTimePassManager exe_mgr(tuning_space); | ||
| 310 | + auto exe = exe_mgr.UpdateNodeExeTime(node, Symbol("19")); | ||
| 311 | + EXPECT_EQ(exe.GetTernaryOpStr(), "1"); | ||
| 312 | +} | ||
| 313 | + | ||
| 314 | +TEST_F(TestExeTimePass, SymbolicCanonicalBlockSplitUsesHitCount) { | ||
| 315 | + const auto period = Symbol("period"); | ||
| 316 | + const auto inner = Symbol("inner"); | ||
| 317 | + auto tuning_space = MakeGuardTuningSpace(period, inner, af::sym::Ceiling(af::sym::Div(period, inner))); | ||
| 318 | + const auto &node = tuning_space->node_infos.front(); | ||
| 319 | + ExeTimePassManager exe_mgr(tuning_space); | ||
| 320 | + const auto exe = exe_mgr.UpdateNodeExeTime(node, Symbol("19")); | ||
| 321 | + EXPECT_NE(exe.GetTernaryOpStr(), "19"); | ||
| 322 | +} | ||
| 323 | + | ||
| 324 | +TEST_F(TestExeTimePass, OriginGuardUsesSingleHit) { | ||
| 325 | + auto tuning_space = MakeGuardTuningSpace(Symbol("4"), Symbol("2"), Symbol("2")); | ||
| 326 | + auto &node = tuning_space->node_infos.front(); | ||
| 327 | + node.exec_condition = af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis; | ||
| 328 | + ExeTimePassManager exe_mgr(tuning_space); | ||
| 329 | + auto exe = exe_mgr.UpdateNodeExeTime(node, Symbol("19")); | ||
| 330 | + EXPECT_EQ(exe.GetTernaryOpStr(), "1"); | ||
| 331 | +} | ||
| 332 | + | ||
| 333 | +TEST_F(TestExeTimePass, TailBlockUsesSingleGuardHit) { | ||
| 334 | + auto tuning_space = MakeGuardTuningSpace(Symbol("1024"), Symbol("19"), Symbol("54")); | ||
| 335 | + const auto &node = tuning_space->node_infos.front(); | ||
| 336 | + ExeTimePassManager exe_mgr(tuning_space); | ||
| 337 | + auto exe = exe_mgr.UpdateNodeExeTime(node, Symbol("19")); | ||
| 338 | + EXPECT_EQ(exe.GetTernaryOpStr(), "1"); | ||
| 339 | +} | ||
| 340 | + | ||
| 341 | +TEST_F(TestExeTimePass, NonUniformGuardFallsBackToLegacyRepeat) { | ||
| 342 | + auto tuning_space = MakeGuardTuningSpace(Symbol("5"), Symbol("6"), Symbol("54")); | ||
| 343 | + const auto &node = tuning_space->node_infos.front(); | ||
| 344 | + ExeTimePassManager exe_mgr(tuning_space); | ||
| 345 | + auto exe = exe_mgr.UpdateNodeExeTime(node, Symbol("19")); | ||
| 346 | + EXPECT_EQ(exe.GetTernaryOpStr(), "19"); | ||
| 347 | +} | ||
| 348 | + | ||
| 349 | +TEST_F(TestExeTimePass, MissingMetadataFallsBackToLegacyRepeat) { | ||
| 350 | + auto tuning_space = MakeGuardTuningSpace(Symbol("4"), Symbol("2"), Symbol("54")); | ||
| 351 | + auto &node = tuning_space->node_infos.front(); | ||
| 352 | + node.exec_condition = af::ExecuteCondition::kConditionInvalid; | ||
| 353 | + ExeTimePassManager exe_mgr(tuning_space); | ||
| 354 | + auto exe = exe_mgr.UpdateNodeExeTime(node, Symbol("19")); | ||
| 355 | + EXPECT_EQ(exe.GetTernaryOpStr(), "19"); | ||
| 356 | +} | ||
| 357 | + | ||
| 31 | void GetExeTime(const TuningSpacePtr &tuning_space, TernaryOp &exe_cast0, TernaryOp &exe_store) { | 358 | void GetExeTime(const TuningSpacePtr &tuning_space, TernaryOp &exe_cast0, TernaryOp &exe_store) { |
| 32 | ExeTimePassManager exe_mgr(tuning_space); | 359 | ExeTimePassManager exe_mgr(tuning_space); |
| 33 | Expr exe_time; | 360 | Expr exe_time; |
| @@ -267,17 +267,29 @@ TEST_F(TestAscendGraphParser, test_gather_graph_parse) { | |||
| 267 | TEST_F(TestAscendGraphParser, test_reduce_graph_parse) { | 267 | TEST_F(TestAscendGraphParser, test_reduce_graph_parse) { |
| 268 | af::AscGraph graph1("reduce_graph"); | 268 | af::AscGraph graph1("reduce_graph"); |
| 269 | ASSERT_EQ(af::ascir::cg::BuildReduceAscendGraphND(graph1), af::SUCCESS); | 269 | ASSERT_EQ(af::ascir::cg::BuildReduceAscendGraphND(graph1), af::SUCCESS); |
| 270 | + auto load1_node = graph1.FindNode("load1"); | ||
| 271 | + ASSERT_NE(load1_node, nullptr); | ||
| 272 | + load1_node->GetOpDesc()->SetType("Nddma"); | ||
| 273 | + load1_node->attr.type = "Nddma"; | ||
| 274 | + load1_node->attr.sched.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis; | ||
| 270 | att::TuningSpacePtr tuning_space = std::make_shared<att::TuningSpace>(); | 275 | att::TuningSpacePtr tuning_space = std::make_shared<att::TuningSpace>(); |
| 271 | EXPECT_NE(tuning_space, nullptr); | 276 | EXPECT_NE(tuning_space, nullptr); |
| 272 | att::AscendGraphParser ascend_graph_parser(tuning_space); | 277 | att::AscendGraphParser ascend_graph_parser(tuning_space); |
| 273 | EXPECT_EQ(ascend_graph_parser.GraphParser(graph1), af::SUCCESS); | 278 | EXPECT_EQ(ascend_graph_parser.GraphParser(graph1), af::SUCCESS); |
| 274 | int32_t cache_count = 0; | 279 | int32_t cache_count = 0; |
| 280 | + int32_t load_cache_count = 0; | ||
| 275 | for (const auto &node_info : ascend_graph_parser.tuning_space_->node_infos) { | 281 | for (const auto &node_info : ascend_graph_parser.tuning_space_->node_infos) { |
| 276 | if (node_info.exec_condition == af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis) { | 282 | if (node_info.exec_condition == af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis) { |
| 277 | cache_count++; | 283 | cache_count++; |
| 284 | + if (node_info.name == "load1") { | ||
| 285 | + load_cache_count++; | ||
| 286 | + } | ||
| 278 | } | 287 | } |
| 279 | } | 288 | } |
| 280 | - EXPECT_EQ(cache_count, 0); | 289 | + // Reduce nodes remain conservatively uncached, while codegen-compatible |
| 290 | + // Load/Nddma/Broadcast nodes retain their explicit cache condition. | ||
| 291 | + EXPECT_EQ(cache_count, 1); | ||
| 292 | + EXPECT_EQ(load_cache_count, 1); | ||
| 281 | } | 293 | } |
| 282 | 294 | ||
| 283 | extern AxisPosition ConvertAxisType(const af::Axis::Type &type); | 295 | extern AxisPosition ConvertAxisType(const af::Axis::Type &type); |
| @@ -42,6 +42,7 @@ const std::string op_name = "OpTest"; | |||
| 42 | 42 | ||
| 43 | namespace att { | 43 | namespace att { |
| 44 | namespace { | 44 | namespace { |
| 45 | + | ||
| 45 | size_t CountSubstr(const std::string &text, const std::string &pattern) { | 46 | size_t CountSubstr(const std::string &text, const std::string &pattern) { |
| 46 | size_t count = 0U; | 47 | size_t count = 0U; |
| 47 | size_t pos = text.find(pattern); | 48 | size_t pos = text.find(pattern); |
| @@ -100,6 +101,229 @@ class MockTilingCodeGenerator : public TilingCodeGenerator { | |||
| 100 | 101 | ||
| 101 | class GeneratorUT : public testing::Test {}; | 102 | class GeneratorUT : public testing::Test {}; |
| 102 | 103 | ||
| 104 | +TEST(GeneratorUT, SerializedEdgeDiscoveryIsDeterministicAndDeduplicated) { | ||
| 105 | + TilingCodeGenConfig config; | ||
| 106 | + TilingModelInfo model_infos{CreateModelInfo()}; | ||
| 107 | + ScoreFuncs score_funcs; | ||
| 108 | + MockHighPerfTilingCodeGenImpl gen_impl("test", config, model_infos, score_funcs, false); | ||
| 109 | + const std::map<size_t, std::pair<std::string, std::string>> graph_info = { | ||
| 110 | + {0U, {"ScheduleResult0", "group0"}}, {1U, {"ScheduleResult1", "group1"}}, {2U, {"ScheduleResult2", "group2"}}}; | ||
| 111 | + std::map<size_t, std::map<size_t, std::map<std::string, af::Expression>>> var_relation; | ||
| 112 | + var_relation[1U][0U]["dep"] = CreateExpr(1); | ||
| 113 | + var_relation[1U][0U]["duplicate"] = CreateExpr(2); | ||
| 114 | + var_relation[1U][1U]["self"] = CreateExpr(3); | ||
| 115 | + var_relation[1U][99U]["unknown"] = CreateExpr(4); | ||
| 116 | + var_relation[0U][1U]["reverse"] = CreateExpr(5); | ||
| 117 | + | ||
| 118 | + EXPECT_EQ(gen_impl.GetSerializedGroupEdges(graph_info, var_relation, false), | ||
| 119 | + (std::vector<std::pair<size_t, size_t>>{{0U, 1U}, {1U, 0U}})); | ||
| 120 | + EXPECT_TRUE(gen_impl.GetSerializedGroupEdges(graph_info, var_relation, true).empty()); | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +TEST(GeneratorUT, SerializedEdgeDiscoveryIgnoresEmptyInnerRelation) { | ||
| 124 | + TilingCodeGenConfig config; | ||
| 125 | + TilingModelInfo model_infos{CreateModelInfo()}; | ||
| 126 | + ScoreFuncs score_funcs; | ||
| 127 | + MockHighPerfTilingCodeGenImpl gen_impl("test", config, model_infos, score_funcs, false); | ||
| 128 | + const std::map<size_t, std::pair<std::string, std::string>> graph_info = {{0U, {"ScheduleResult0", "group0"}}, | ||
| 129 | + {1U, {"ScheduleResult1", "group1"}}}; | ||
| 130 | + std::map<size_t, std::map<size_t, std::map<std::string, af::Expression>>> var_relation; | ||
| 131 | + var_relation[1U][0U]; | ||
| 132 | + | ||
| 133 | + EXPECT_TRUE(gen_impl.GetSerializedGroupEdges(graph_info, var_relation, false).empty()); | ||
| 134 | + | ||
| 135 | + var_relation[1U][0U]["dep"] = CreateExpr(1); | ||
| 136 | + EXPECT_EQ(gen_impl.GetSerializedGroupEdges(graph_info, var_relation, false), | ||
| 137 | + (std::vector<std::pair<size_t, size_t>>{{0U, 1U}})); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +TEST(GeneratorUT, SerializedEdgePenaltyEmitsCappedExpressionAndModelLog) { | ||
| 141 | + TilingCodeGenConfig config; | ||
| 142 | + TilingModelInfo model_infos{CreateModelInfo()}; | ||
| 143 | + ScoreFuncs score_funcs; | ||
| 144 | + MockHighPerfTilingCodeGenImpl gen_impl("test", config, model_infos, score_funcs, false); | ||
| 145 | + const std::string source = gen_impl.GenSerializedEdgePenalty({"g0_perf", "g1_perf"}, {{0U, 1U}}, " "); | ||
| 146 | + EXPECT_NE(source.find("serialized_edge_base"), std::string::npos); | ||
| 147 | + EXPECT_NE(source.find("min(8000.0"), std::string::npos); | ||
| 148 | + EXPECT_NE(source.find("0.10"), std::string::npos); | ||
| 149 | + EXPECT_NE(source.find("max(g0_perf, g1_perf)"), std::string::npos); | ||
| 150 | + EXPECT_NE(source.find("serialized_edge_base += std::max(g0_perf, g1_perf)"), std::string::npos); | ||
| 151 | + EXPECT_NE(source.find("std::min(8000.000000, 0.100000 * serialized_edge_base)"), std::string::npos); | ||
| 152 | + EXPECT_NE(source.find("serialized_edge_model=serialized_edge_v1"), std::string::npos); | ||
| 153 | + EXPECT_TRUE(gen_impl.GenSerializedEdgePenalty({"g0_perf"}, {{0U, 1U}}, " ").empty()); | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +namespace { | ||
| 157 | +struct ScheduleResultSourceCase { | ||
| 158 | + TilingModelInfo model_infos; | ||
| 159 | + std::map<size_t, std::pair<std::string, std::string>> graph_info; | ||
| 160 | + std::map<std::string, std::set<std::string>> hardware_map; | ||
| 161 | +}; | ||
| 162 | + | ||
| 163 | +ScheduleResultSourceCase MakeTwoGroupScheduleResultSourceCase(const bool enable_group_parallel) { | ||
| 164 | + ScheduleResultSourceCase test_case; | ||
| 165 | + auto group0 = CreateModelInfo(); | ||
| 166 | + group0.schedule_group_ident = {0U, 0U, 0U}; | ||
| 167 | + group0.enable_group_parallel = enable_group_parallel; | ||
| 168 | + auto group1 = CreateModelInfo(); | ||
| 169 | + group1.schedule_group_ident = {0U, 0U, 1U}; | ||
| 170 | + group1.enable_group_parallel = enable_group_parallel; | ||
| 171 | + test_case.model_infos = {group0, group1}; | ||
| 172 | + test_case.graph_info = {{0U, {"ScheduleResult0", "group0"}}, {1U, {"ScheduleResult1", "group1"}}}; | ||
| 173 | + test_case.hardware_map = {{"group0", {"block_dim"}}, {"group1", {"block_dim"}}}; | ||
| 174 | + return test_case; | ||
| 175 | +} | ||
| 176 | +} // namespace | ||
| 177 | + | ||
| 178 | +TEST(GeneratorUT, GenUpdatePerfAddsSerializedEdgeToNonParallelObjective) { | ||
| 179 | + const auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 180 | + ScoreFuncs score_funcs; | ||
| 181 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 182 | + gen_impl.var_relations_[0U][0U][1U][0U]["dep"] = CreateExpr(1); | ||
| 183 | + | ||
| 184 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 185 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 186 | + EXPECT_NE(source.find("group_sum"), std::string::npos); | ||
| 187 | + EXPECT_NE(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 188 | + EXPECT_NE(source.find("objective=group_sum"), std::string::npos); | ||
| 189 | + EXPECT_NE(source.find("The value of graph0_result0 is %lf objective(group_sum(group0 + group1)=%lf + edge_model("), | ||
| 190 | + std::string::npos); | ||
| 191 | + EXPECT_NE(source.find("edge_model(serialized_edge_v1"), std::string::npos); | ||
| 192 | + EXPECT_NE(source.find("base=max(group0, group1)=%lf, penalty=min(8000.000000, 0.100000*base)"), std::string::npos); | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +TEST(GeneratorUT, GenUpdatePerfSingleGroupHasNoSerializedEdgePenalty) { | ||
| 196 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 197 | + test_case.graph_info.erase(1U); | ||
| 198 | + test_case.hardware_map.erase("group1"); | ||
| 199 | + test_case.model_infos.resize(1U); | ||
| 200 | + ScoreFuncs score_funcs; | ||
| 201 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 202 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 203 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 204 | + EXPECT_EQ(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 205 | + EXPECT_NE(source.find("edge_model(serialized_edge_v1, base=0=%lf, penalty=min(8000.000000, 0.100000*base)=%lf)=%lf"), | ||
| 206 | + std::string::npos); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +TEST(GeneratorUT, GenUpdatePerfParallelHasNoSerializedEdgePenalty) { | ||
| 210 | + const auto test_case = MakeTwoGroupScheduleResultSourceCase(true); | ||
| 211 | + ScoreFuncs score_funcs; | ||
| 212 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 213 | + gen_impl.var_relations_[0U][0U][1U][0U]["dep"] = CreateExpr(1); | ||
| 214 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 215 | + EXPECT_EQ(gen_impl.tiling_func_.GetOutputStr().find("serialized_edge_penalty"), std::string::npos); | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +TEST(GeneratorUT, GenUpdatePerfEmptyRelationKeepsGroupSumCode) { | ||
| 219 | + const auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 220 | + ScoreFuncs score_funcs; | ||
| 221 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 222 | + gen_impl.var_relations_[0U][0U][1U][0U]; | ||
| 223 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 224 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 225 | + EXPECT_NE(source.find("cur_perf = ScheduleResult0::GetPerf(group0_tiling_data);"), std::string::npos); | ||
| 226 | + EXPECT_NE(source.find("cur_perf += ScheduleResult1::GetPerf(group1_tiling_data);"), std::string::npos); | ||
| 227 | + EXPECT_EQ(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | +TEST(GeneratorUT, GenUpdatePerfMapsNonContiguousGroupIdsToPerfPositions) { | ||
| 231 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 232 | + test_case.graph_info = {{2U, {"ScheduleResult0", "group0"}}, {5U, {"ScheduleResult1", "group1"}}}; | ||
| 233 | + test_case.model_infos[0].schedule_group_ident.group_id = 2U; | ||
| 234 | + test_case.model_infos[1].schedule_group_ident.group_id = 5U; | ||
| 235 | + ScoreFuncs score_funcs; | ||
| 236 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 237 | + gen_impl.var_relations_[0U][0U][5U][2U]["dep"] = CreateExpr(1); | ||
| 238 | + | ||
| 239 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 240 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 241 | + EXPECT_NE(source.find("serialized_edge_base += std::max(ScheduleResult0::GetPerf(group0_tiling_data), "), | ||
| 242 | + std::string::npos); | ||
| 243 | + EXPECT_NE(source.find("ScheduleResult1::GetPerf(group1_tiling_data));"), std::string::npos); | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +TEST(GeneratorUT, GenUpdatePerfAddsSerializedEdgeFromSharedWorkspace) { | ||
| 247 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 248 | + test_case.model_infos[0].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 249 | + test_case.model_infos[1].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 250 | + ScoreFuncs score_funcs; | ||
| 251 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 252 | + | ||
| 253 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 254 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 255 | + EXPECT_NE(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 256 | + EXPECT_NE(source.find("objective=group_sum"), std::string::npos); | ||
| 257 | + EXPECT_NE(source.find("serialized_edge_base += std::max(ScheduleResult0::GetPerf(group0_tiling_data), "), | ||
| 258 | + std::string::npos); | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +TEST(GeneratorUT, GenUpdatePerfDistinctWorkspaceHasNoSerializedEdgePenalty) { | ||
| 262 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 263 | + test_case.model_infos[0].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 264 | + test_case.model_infos[1].workspace_size_map.emplace(7, CreateExpr(2048)); | ||
| 265 | + ScoreFuncs score_funcs; | ||
| 266 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 267 | + | ||
| 268 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 269 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 270 | + EXPECT_NE(source.find("cur_perf = ScheduleResult0::GetPerf(group0_tiling_data);"), std::string::npos); | ||
| 271 | + EXPECT_NE(source.find("cur_perf += ScheduleResult1::GetPerf(group1_tiling_data);"), std::string::npos); | ||
| 272 | + EXPECT_EQ(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +TEST(GeneratorUT, GenUpdatePerfParallelSharedWorkspaceHasNoSerializedEdgePenalty) { | ||
| 276 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(true); | ||
| 277 | + test_case.model_infos[0].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 278 | + test_case.model_infos[1].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 279 | + ScoreFuncs score_funcs; | ||
| 280 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 281 | + | ||
| 282 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 283 | + EXPECT_EQ(gen_impl.tiling_func_.GetOutputStr().find("serialized_edge_penalty"), std::string::npos); | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +TEST(GeneratorUT, GenUpdatePerfWorkspaceEdgeDeduplicatedWithVarRelationEdge) { | ||
| 287 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 288 | + test_case.model_infos[0].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 289 | + test_case.model_infos[1].workspace_size_map.emplace(5, CreateExpr(1024)); | ||
| 290 | + ScoreFuncs score_funcs; | ||
| 291 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 292 | + gen_impl.var_relations_[0U][0U][1U][0U]["dep"] = CreateExpr(1); | ||
| 293 | + | ||
| 294 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 295 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 296 | + EXPECT_EQ(CountSubstr(source, "serialized_edge_base += std::max("), 1U); | ||
| 297 | +} | ||
| 298 | + | ||
| 299 | +TEST(GeneratorUT, GenUpdatePerfInvalidEdgeDoesNotEmitUndefinedPenaltyLog) { | ||
| 300 | + auto model_info = CreateModelInfo(); | ||
| 301 | + model_info.schedule_group_ident = {0U, 0U, 0U}; | ||
| 302 | + ScoreFuncs score_funcs; | ||
| 303 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), {model_info}, score_funcs, false); | ||
| 304 | + | ||
| 305 | + ASSERT_EQ(gen_impl.GenUpdatePerf(0U, 0U, {"g0_perf"}, {{0U, 1U}}, {"g0_block"}, {}), af::SUCCESS); | ||
| 306 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 307 | + EXPECT_EQ(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 308 | + EXPECT_EQ(source.find("objective=group_sum"), std::string::npos); | ||
| 309 | +} | ||
| 310 | + | ||
| 311 | +TEST(GeneratorUT, GenUpdatePerfMissingHardwareGroupSkipsSerializedEdge) { | ||
| 312 | + auto test_case = MakeTwoGroupScheduleResultSourceCase(false); | ||
| 313 | + test_case.graph_info = {{2U, {"ScheduleResult0", "group0"}}, {5U, {"ScheduleResult1", "group1"}}}; | ||
| 314 | + test_case.model_infos[0].schedule_group_ident.group_id = 2U; | ||
| 315 | + test_case.model_infos[1].schedule_group_ident.group_id = 5U; | ||
| 316 | + test_case.hardware_map.erase("group1"); | ||
| 317 | + ScoreFuncs score_funcs; | ||
| 318 | + MockHighPerfTilingCodeGenImpl gen_impl("test", TilingCodeGenConfig(), test_case.model_infos, score_funcs, false); | ||
| 319 | + gen_impl.var_relations_[0U][0U][5U][2U]["dep"] = CreateExpr(1); | ||
| 320 | + | ||
| 321 | + ASSERT_EQ(gen_impl.GenSingleGroupScheduleResult(0U, 0U, test_case.graph_info, test_case.hardware_map), af::SUCCESS); | ||
| 322 | + const std::string source = gen_impl.tiling_func_.GetOutputStr(); | ||
| 323 | + EXPECT_EQ(source.find("serialized_edge_penalty"), std::string::npos); | ||
| 324 | + EXPECT_EQ(source.find("objective=group_sum"), std::string::npos); | ||
| 325 | +} | ||
| 326 | + | ||
| 103 | TEST(GeneratorUT, SourceDependenciesMergeAndRenderIncludes) { | 327 | TEST(GeneratorUT, SourceDependenciesMergeAndRenderIncludes) { |
| 104 | autofuse::GeneratedCode target; | 328 | autofuse::GeneratedCode target; |
| 105 | autofuse::RequireSystemHeader(target.dependencies, "vector"); | 329 | autofuse::RequireSystemHeader(target.dependencies, "vector"); |
| @@ -6154,6 +6154,48 @@ TEST(CodegenKernel, CacheGuardIsOnlyClosedWhenAConditionIsGenerated) { | |||
| 6154 | EXPECT_EQ(std::count(result.begin(), result.end(), '{'), std::count(result.begin(), result.end(), '}')); | 6154 | EXPECT_EQ(std::count(result.begin(), result.end(), '{'), std::count(result.begin(), result.end(), '}')); |
| 6155 | } | 6155 | } |
| 6156 | 6156 | ||
| 6157 | +TEST(CodegenKernel, CacheGuardPredicateUsesFirstMiddleAndLastTileForNineteen) { | ||
| 6158 | + af::SizeVar outer_size(af::Symbol("outer_size")); | ||
| 6159 | + af::SizeVar block_inner_extent(af::Symbol("19")); | ||
| 6160 | + af::Axis outer{.id = 0, .name = "outer", .size = outer_size.expr}; | ||
| 6161 | + af::Axis guarded{.id = 1, .name = "guarded", .size = af::Symbol("19")}; | ||
| 6162 | + af::Axis block_inner{ | ||
| 6163 | + .id = 2, .name = "block_inner", .type = af::Axis::Type::kAxisTypeBlockInner, .size = block_inner_extent.expr}; | ||
| 6164 | + | ||
| 6165 | + codegen::Tiler tiler; | ||
| 6166 | + tiler.AddSizeVar(outer_size); | ||
| 6167 | + tiler.AddSizeVar(block_inner_extent); | ||
| 6168 | + tiler.AddAxis(outer); | ||
| 6169 | + tiler.AddAxis(guarded); | ||
| 6170 | + tiler.AddAxis(block_inner); | ||
| 6171 | + tiler.axis_map.at(outer.id).is_split_b = true; | ||
| 6172 | + tiler.axis_map.at(guarded.id).is_split_b = true; | ||
| 6173 | + | ||
| 6174 | + codegen::Loop loop(guarded.id); | ||
| 6175 | + auto fused_call = new MockApiCall("fused_call"); | ||
| 6176 | + fused_call->enable_cache = true; | ||
| 6177 | + fused_call->exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis; | ||
| 6178 | + fused_call->unit = af::ComputeUnit::kUnitVector; | ||
| 6179 | + auto origin_call = new MockApiCall("origin_call"); | ||
| 6180 | + origin_call->enable_cache = true; | ||
| 6181 | + origin_call->exec_condition = af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis; | ||
| 6182 | + origin_call->unit = af::ComputeUnit::kUnitVector; | ||
| 6183 | + loop.AddCall(fused_call); | ||
| 6184 | + loop.AddCall(origin_call); | ||
| 6185 | + | ||
| 6186 | + codegen::TPipe tpipe("tpipe", tiler); | ||
| 6187 | + std::string result; | ||
| 6188 | + ASSERT_EQ(loop.Generate(tiler, tpipe, result), af::SUCCESS); | ||
| 6189 | + | ||
| 6190 | + // Tile 0 is covered by the explicit axis < 1 term; tiles 9 and 18 exercise the | ||
| 6191 | + // middle and tail positions of the period-19 predicate respectively. | ||
| 6192 | + EXPECT_NE(result.find("bool enable_cache_fused_brc_axis = (guarded < 1) || ((block_dim * block_inner_axis_size + " | ||
| 6193 | + "guarded) % guarded_loop_size < 1);"), | ||
| 6194 | + std::string::npos); | ||
| 6195 | + EXPECT_NE(result.find("bool enable_cache_origin_brc_axis = (guarded < 1);"), std::string::npos); | ||
| 6196 | + EXPECT_EQ(std::count(result.begin(), result.end(), '{'), std::count(result.begin(), result.end(), '}')); | ||
| 6197 | +} | ||
| 6198 | + | ||
| 6157 | TEST(CodegenKernel, ReduceDoubleTileUsesReduceSpecificCacheCondition) { | 6199 | TEST(CodegenKernel, ReduceDoubleTileUsesReduceSpecificCacheCondition) { |
| 6158 | af::SizeVar outer_size(af::Symbol("outer_size")); | 6200 | af::SizeVar outer_size(af::Symbol("outer_size")); |
| 6159 | af::SizeVar tile0_size(af::Symbol("tile0_size")); | 6201 | af::SizeVar tile0_size(af::Symbol("tile0_size")); |