已开启
Feat: 新增面向 arch22 的 aclblasSspr 接口 #333
guodong54_创建于 9 天前
Feat: 新增面向 arch22 的 aclblasSspr 接口 #333
已开启
共 6 个文件变更+843-0
| @@ -0,0 +1,97 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +// ---- kernel entry (local declaration, replacing aclblas_kernel_do.h) ---- | ||
| 24 | +void sspr_kernel_do(uint8_t* x, uint8_t* ap, const SsprTilingData& tiling, uint32_t numBlocks, void* stream); | ||
| 25 | + | ||
| 26 | +static aclblasStatus_t ValidateSsprParams( | ||
| 27 | + aclblasFillMode_t uplo, int incx, const float* alpha, const float* x, const float* ap) | ||
| 28 | +{ | ||
| 29 | + CHECK_RET( | ||
| 30 | + alpha != nullptr, OP_LOGE("aclblasSspr", "alpha must not be nullptr"); return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 31 | + CHECK_RET( | ||
| 32 | + uplo == ACLBLAS_UPPER || uplo == ACLBLAS_LOWER, | ||
| 33 | + OP_LOGE("aclblasSspr", "uplo must be UPPER(121) or LOWER(122), got %d", static_cast<int>(uplo)); | ||
| 34 | + return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 35 | + CHECK_RET(incx != 0, OP_LOGE("aclblasSspr", "incx must not be zero"); return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 36 | + CHECK_RET(incx != INT_MIN, OP_LOGE("aclblasSspr", "incx must not be INT_MIN"); return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 37 | + CHECK_RET(x != nullptr, OP_LOGE("aclblasSspr", "x must not be nullptr"); return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 38 | + CHECK_RET(ap != nullptr, OP_LOGE("aclblasSspr", "ap must not be nullptr"); return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 39 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +static SsprTilingData CalSsprTilingData(uint32_t useNumBlocks, int n, aclblasFillMode_t uplo, float alpha, int incx) | ||
| 43 | +{ | ||
| 44 | + SsprTilingData tilingData{}; | ||
| 45 | + uint32_t nU32 = static_cast<uint32_t>(n); | ||
| 46 | + uint32_t colsPerBlk = CeilDiv<uint32_t>(nU32, useNumBlocks); | ||
| 47 | + tilingData.numThreads = std::min(CeilAlign<uint32_t>(colsPerBlk, SIMT_MIN_THREAD_NUM), SIMT_MAX_THREAD_NUM); | ||
| 48 | + tilingData.columnsPerBlock = colsPerBlk; | ||
| 49 | + tilingData.n = nU32; | ||
| 50 | + tilingData.uplo = static_cast<uint32_t>(uplo); | ||
| 51 | + tilingData.alpha = alpha; | ||
| 52 | + tilingData.incx = static_cast<int64_t>(incx); | ||
| 53 | + return tilingData; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +aclblasStatus_t aclblasSspr( | ||
| 57 | + aclblasHandle_t handle, aclblasFillMode_t uplo, int n, const float* alpha, const float* x, int incx, float* ap) | ||
| 58 | +{ | ||
| 59 | + auto* h = handle; | ||
| 60 | + CHECK_RET(h != nullptr, OP_LOGE("aclblasSspr", "handle is nullptr"); return ACLBLAS_STATUS_HANDLE_IS_NULLPTR); | ||
| 61 | + | ||
| 62 | + CHECK_RET(n >= 0, OP_LOGE("aclblasSspr", "n must be >= 0, got %d", n); return ACLBLAS_STATUS_INVALID_VALUE); | ||
| 63 | + if (n == 0) { | ||
| 64 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + aclblasStatus_t st = ValidateSsprParams(uplo, incx, alpha, x, ap); | ||
| 68 | + if (st != ACLBLAS_STATUS_SUCCESS) { | ||
| 69 | + return st; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + float alphaVal = *alpha; | ||
| 73 | + if (alphaVal == 0.0f) { | ||
| 74 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + aclrtStream stream = h->stream; | ||
| 78 | + | ||
| 79 | + uint32_t aivCoreNum = GetAivCoreCount(); | ||
| 80 | + if (aivCoreNum == 0) { | ||
| 81 | + OP_LOGE("aclblasSspr", "vector core count is 0"); | ||
| 82 | + return ACLBLAS_STATUS_EXECUTION_FAILED; | ||
| 83 | + } | ||
| 84 | + uint32_t useNumBlocks = (incx != 1 && n >= 4096) | ||
| 85 | + ? aivCoreNum | ||
| 86 | + : std::min(CeilDiv<uint32_t>(n, SIMT_MIN_THREAD_NUM), aivCoreNum); | ||
| 87 | + | ||
| 88 | + SsprTilingData tiling = CalSsprTilingData(useNumBlocks, n, uplo, alphaVal, incx); | ||
| 89 | + | ||
| 90 | + OP_LOGI( | ||
| 91 | + "aclblasSspr", "launching kernel: blocks=%u, cores=%u, ubEligible=%d", useNumBlocks, aivCoreNum, | ||
| 92 | + (incx == 1 && n >= static_cast<int>(UB_THRESHOLD) && static_cast<uint32_t>(n) <= UB_X_FLOATS)); | ||
| 93 | + | ||
| 94 | + sspr_kernel_do((uint8_t*)const_cast<float*>(x), (uint8_t*)ap, tiling, useNumBlocks, stream); | ||
| 95 | + | ||
| 96 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 97 | +} | ||
| @@ -0,0 +1,501 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +using namespace AscendC; | ||
| 18 | + | ||
| 19 | +namespace { | ||
| 20 | +constexpr uint32_t kChunkFloats = 8192; | ||
| 21 | +constexpr uint32_t kStridedTileFloats = 512; | ||
| 22 | +constexpr uint32_t kMaxPackedStride = 128; | ||
| 23 | +constexpr uint32_t kCanonicalXFloats = 32768 + 8; | ||
| 24 | +constexpr uint32_t kMaxCanonicalN = 32768; | ||
| 25 | +constexpr uint32_t kPackSpanFloats = 4096; | ||
| 26 | +constexpr uint32_t kPackOffsetFloats = 4096; | ||
| 27 | +constexpr uint32_t kLocalRowFloats = 4096; | ||
| 28 | +constexpr uint32_t kLocalColFloats = 8192; | ||
| 29 | + | ||
| 30 | +// Cost of the columns [0, columns), used to split columns across AI vector cores. | ||
| 31 | +// | ||
| 32 | +// A core's time is not proportional to the number of packed elements it owns. Owning k | ||
| 33 | +// columns costs roughly | ||
| 34 | +// | ||
| 35 | +// sum over the k columns of len + k * p + q * (extra transfers) | ||
| 36 | +// | ||
| 37 | +// where p is a flat charge every column pays regardless of length (AP base address, scalar | ||
| 38 | +// broadcast of x[col], loop and descriptor setup, first DMA round trip) and q is charged | ||
| 39 | +// again for each further kChunkFloats piece a long column has to walk. Equalising element | ||
| 40 | +// counts ignores both: at n = 32768 the slowest core took 2x the mean, because half the | ||
| 41 | +// columns of a triangular matrix are short and a core owning many of them pays p many times | ||
| 42 | +// while moving few elements. | ||
| 43 | +// | ||
| 44 | +// Implemented below as kCostPerColumn * columns + kCostPerElement * elements + | ||
| 45 | +// kCostPerTransfer * transfers, which is the same function since every column contributes at | ||
| 46 | +// least one transfer: | ||
| 47 | +// | ||
| 48 | +// kCostPerColumn + len + kCostPerTransfer * ceil(len / kChunkFloats) | ||
| 49 | +// = (kCostPerColumn + kCostPerTransfer) + len + kCostPerTransfer * floor((len - 1) / kChunkFloats) | ||
| 50 | +// \___________ p ________________/ \_ extra transfers _/ | ||
| 51 | +// | ||
| 52 | +// Note which quantity is actually identifiable. For len <= kChunkFloats the ceiling is 1, so | ||
| 53 | +// only the sum p = kCostPerColumn + kCostPerTransfer affects the split; the two constants are | ||
| 54 | +// separable only where columns span several transfers. Measurements bear that out: candidates | ||
| 55 | +// sharing a sum scored within 0.2 percentage points of each other, far inside the ~2.7% per | ||
| 56 | +// case run-to-run spread, so do not read the individual values as measurements of a per-column | ||
| 57 | +// and a per-transfer cost. What the data supports is p in the 4096-8192 range (p = 0 scores | ||
| 58 | +// 0.71x, i.e. worse than an equal-column split) and a smaller positive extra-transfer charge. | ||
| 59 | +// | ||
| 60 | +// Selected by timing 44 (n, uplo, incx) configurations over a grid of candidates on 910B3, | ||
| 61 | +// then 9 repeats of the top four. A later LOWER-only sweep tested 25 coefficient pairs and | ||
| 62 | +// confirmed four finalists at warmup 300 / measurement 1000 over seven sizes. The alternatives | ||
| 63 | +// gained at most 2.0% geometric mean but regressed individual sizes by up to 10.2%, so LOWER | ||
| 64 | +// retains the same coefficients as UPPER. Each term has an O(1) closed form so the boundary | ||
| 65 | +// search below stays O(log n). | ||
| 66 | +constexpr uint64_t kCostPerColumn = 2048; // with kCostPerTransfer: p = 6144 per column | ||
| 67 | +constexpr uint64_t kCostPerElement = 1; // per packed element streamed | ||
| 68 | +constexpr uint64_t kCostPerTransfer = 4096; // per kChunkFloats-sized transfer | ||
| 69 | + | ||
| 70 | +// sum_{len=1..m} ceil(len / grain) | ||
| 71 | +__aicore__ inline uint64_t CeilRunSum(uint64_t m, uint64_t grain) | ||
| 72 | +{ | ||
| 73 | + if (m == 0ULL) return 0ULL; | ||
| 74 | + const uint64_t q = m / grain; | ||
| 75 | + const uint64_t r = m - q * grain; | ||
| 76 | + return grain * q * (q + 1ULL) / 2ULL + r * (q + 1ULL); | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +__aicore__ inline uint64_t ColumnWorkPrefix(uint32_t columns, uint32_t n, uint32_t uplo) | ||
| 80 | +{ | ||
| 81 | + // Column c has length c+1 (UPPER) or n-c (LOWER); the sums below run over [0, columns). | ||
| 82 | + const uint64_t elements = uplo == ACLBLAS_UPPER | ||
| 83 | + ? static_cast<uint64_t>(columns) * (columns + 1ULL) / 2ULL | ||
| 84 | + : static_cast<uint64_t>(columns) * (2ULL * n - columns + 1ULL) / 2ULL; | ||
| 85 | + const uint64_t transfers = uplo == ACLBLAS_UPPER | ||
| 86 | + ? CeilRunSum(columns, kChunkFloats) | ||
| 87 | + : CeilRunSum(n, kChunkFloats) - CeilRunSum(static_cast<uint64_t>(n) - columns, kChunkFloats); | ||
| 88 | + return kCostPerColumn * static_cast<uint64_t>(columns) | ||
| 89 | + + kCostPerElement * elements | ||
| 90 | + + kCostPerTransfer * transfers; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +__aicore__ inline uint64_t PartitionTarget(uint64_t total, uint32_t part, uint32_t parts) | ||
| 94 | +{ | ||
| 95 | + return (total / parts) * part + (total % parts) * part / parts; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +__aicore__ inline uint32_t WeightedColumnBoundary( | ||
| 99 | + uint32_t part, uint32_t parts, uint32_t n, uint32_t uplo) | ||
| 100 | +{ | ||
| 101 | + if (part == 0U) return 0U; | ||
| 102 | + if (part >= parts) return n; | ||
| 103 | + const uint64_t target = PartitionTarget(ColumnWorkPrefix(n, n, uplo), part, parts); | ||
| 104 | + uint32_t lo = 0U; | ||
| 105 | + uint32_t hi = n; | ||
| 106 | + while (lo < hi) { | ||
| 107 | + const uint32_t mid = lo + (hi - lo) / 2U; | ||
| 108 | + if (ColumnWorkPrefix(mid, n, uplo) < target) lo = mid + 1U; | ||
| 109 | + else hi = mid; | ||
| 110 | + } | ||
| 111 | + return lo; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +__aicore__ inline uint64_t UpperColumnBase(uint32_t col) | ||
| 115 | +{ | ||
| 116 | + return static_cast<uint64_t>(col) * (col + 1ULL) / 2ULL; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +__aicore__ inline uint64_t LowerColumnBase(uint32_t col, uint32_t n) | ||
| 120 | +{ | ||
| 121 | + return static_cast<uint64_t>(col) * (2ULL * n - col + 1ULL) / 2ULL; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +__aicore__ inline uint64_t XPhysicalIndex(uint32_t logical, uint32_t n, int64_t incx) | ||
| 125 | +{ | ||
| 126 | + const uint64_t absIncx = static_cast<uint64_t>(incx >= 0 ? incx : -incx); | ||
| 127 | + return incx >= 0 ? static_cast<uint64_t>(logical) * absIncx | ||
| 128 | + : static_cast<uint64_t>(n - 1U - logical) * absIncx; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +__aicore__ inline void ScalarColumns( | ||
| 132 | + GlobalTensor<float>& xGm, GlobalTensor<float>& apGm, const SsprTilingData& tiling, | ||
| 133 | + uint32_t colStart, uint32_t colEnd) | ||
| 134 | +{ | ||
| 135 | + for (uint32_t col = colStart; col < colEnd; ++col) { | ||
| 136 | + const float axCol = tiling.alpha * xGm.GetValue(XPhysicalIndex(col, tiling.n, tiling.incx)); | ||
| 137 | + if (tiling.uplo == ACLBLAS_UPPER) { | ||
| 138 | + const uint64_t base = UpperColumnBase(col); | ||
| 139 | + for (uint32_t row = 0; row <= col; ++row) { | ||
| 140 | + const uint64_t apIndex = base + row; | ||
| 141 | + apGm.SetValue(apIndex, apGm.GetValue(apIndex) + | ||
| 142 | + axCol * xGm.GetValue(XPhysicalIndex(row, tiling.n, tiling.incx))); | ||
| 143 | + } | ||
| 144 | + } else { | ||
| 145 | + const uint64_t base = LowerColumnBase(col, tiling.n); | ||
| 146 | + for (uint32_t row = col; row < tiling.n; ++row) { | ||
| 147 | + const uint64_t apIndex = base + (row - col); | ||
| 148 | + apGm.SetValue(apIndex, apGm.GetValue(apIndex) + | ||
| 149 | + axCol * xGm.GetValue(XPhysicalIndex(row, tiling.n, tiling.incx))); | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | + | ||
| 156 | +__aicore__ inline void CanonicalizeStridedRange( | ||
| 157 | + LocalTensor<float> cache, LocalTensor<float> span, LocalTensor<uint32_t> offsets, | ||
| 158 | + __gm__ float* source, uint32_t n, int64_t incx, uint32_t logicalBase, uint32_t logicalCount) | ||
| 159 | +{ | ||
| 160 | + const uint32_t increment = static_cast<uint32_t>(incx > 0 ? incx : -incx); | ||
| 161 | + auto ubSpan = reinterpret_cast<__ubuf__ float*>(span.GetPhyAddr()); | ||
| 162 | + auto ubCache = reinterpret_cast<__ubuf__ float*>(cache.GetPhyAddr()); | ||
| 163 | + | ||
| 164 | + // Gather() writes to cache[localStart], and localStart advances by logicalTile every | ||
| 165 | + // iteration. A UB vector destination must begin on a 32-byte (8-float) boundary, so | ||
| 166 | + // logicalTile must be a multiple of 8. The span buffer additionally bounds one tile to | ||
| 167 | + // (logicalTile - 1) * increment + 1 <= kPackSpanFloats floats. Both constraints together | ||
| 168 | + // require increment <= kPackSpanFloats / 8; larger strides cannot fill even 8 logical | ||
| 169 | + // elements from one span read and are handled by the scalar path below. | ||
| 170 | + const uint32_t logicalTile = (kPackSpanFloats / increment) & ~7U; | ||
| 171 | + if (logicalTile == 0U) { | ||
| 172 | + // Rare very large stride. Move one element at a time: correctness over throughput. | ||
| 173 | + for (uint32_t i = 0; i < logicalCount; ++i) { | ||
| 174 | + const uint64_t physical = XPhysicalIndex(logicalBase + i, n, incx); | ||
| 175 | + set_flag(PIPE_S, PIPE_MTE2, EVENT_ID1); | ||
| 176 | + wait_flag(PIPE_S, PIPE_MTE2, EVENT_ID1); | ||
| 177 | + copy_gm_to_ubuf_align_b32(ubSpan, source + physical, 0, 1, sizeof(float), 0, 0, 0, 0); | ||
| 178 | + set_flag(PIPE_MTE2, PIPE_S, EVENT_ID1); | ||
| 179 | + wait_flag(PIPE_MTE2, PIPE_S, EVENT_ID1); | ||
| 180 | + ubCache[i] = ubSpan[0]; | ||
| 181 | + } | ||
| 182 | + set_flag(PIPE_S, PIPE_V, EVENT_ID1); | ||
| 183 | + wait_flag(PIPE_S, PIPE_V, EVENT_ID1); | ||
| 184 | + return; | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + for (uint32_t localStart = 0; localStart < logicalCount; localStart += logicalTile) { | ||
| 188 | + const uint32_t count = (logicalCount - localStart < logicalTile) | ||
| 189 | + ? logicalCount - localStart : logicalTile; | ||
| 190 | + const uint32_t globalStart = logicalBase + localStart; | ||
| 191 | + const uint32_t physicalStart = incx > 0 ? globalStart * increment | ||
| 192 | + : (n - globalStart - count) * increment; | ||
| 193 | + const uint32_t spanCount = (count - 1U) * increment + 1U; | ||
| 194 | + for (uint32_t i = 0; i < count; ++i) { | ||
| 195 | + const uint32_t sourceIndex = incx > 0 ? i * increment : (count - 1U - i) * increment; | ||
| 196 | + offsets.SetValue(i, sourceIndex * sizeof(float)); | ||
| 197 | + } | ||
| 198 | + set_flag(PIPE_S, PIPE_MTE2, EVENT_ID1); | ||
| 199 | + wait_flag(PIPE_S, PIPE_MTE2, EVENT_ID1); | ||
| 200 | + copy_gm_to_ubuf_align_b32( | ||
| 201 | + ubSpan, source + physicalStart, 0, 1, spanCount * sizeof(float), 0, 0, 0, 0); | ||
| 202 | + set_flag(PIPE_MTE2, PIPE_V, EVENT_ID1); | ||
| 203 | + wait_flag(PIPE_MTE2, PIPE_V, EVENT_ID1); | ||
| 204 | + Gather(cache[localStart], span, offsets, 0U, count); | ||
| 205 | + PipeBarrier<PIPE_V>(); | ||
| 206 | + set_flag(PIPE_V, PIPE_S, EVENT_ID1); | ||
| 207 | + wait_flag(PIPE_V, PIPE_S, EVENT_ID1); | ||
| 208 | + } | ||
| 209 | +} | ||
| 210 | +__aicore__ inline void CachedStridedColumns( | ||
| 211 | + __gm__ float* gmX, GlobalTensor<float>& apGm, const SsprTilingData& tiling, | ||
| 212 | + uint32_t colStart, uint32_t colEnd) | ||
| 213 | +{ | ||
| 214 | + constexpr uint32_t kRowTile = kLocalRowFloats; | ||
| 215 | + TPipe pipe; | ||
| 216 | + TBuf<TPosition::VECCALC> xColBuf; | ||
| 217 | + TBuf<TPosition::VECCALC> xRowBuf; | ||
| 218 | + TBuf<TPosition::VECCALC> spanBuf; | ||
| 219 | + TBuf<TPosition::VECCALC> offsetBuf; | ||
| 220 | + TBuf<TPosition::VECCALC> xWorkBuf; | ||
| 221 | + TQue<QuePosition::VECIN, 2> apQueue; | ||
| 222 | + TQue<QuePosition::VECOUT, 2> outQueue; | ||
| 223 | + pipe.InitBuffer(xColBuf, kLocalColFloats * sizeof(float)); | ||
| 224 | + pipe.InitBuffer(xRowBuf, kRowTile * sizeof(float)); | ||
| 225 | + pipe.InitBuffer(spanBuf, kPackSpanFloats * sizeof(float)); | ||
| 226 | + pipe.InitBuffer(offsetBuf, kPackOffsetFloats * sizeof(uint32_t)); | ||
| 227 | + pipe.InitBuffer(xWorkBuf, kRowTile * sizeof(float)); | ||
| 228 | + pipe.InitBuffer(apQueue, 2, kRowTile * sizeof(float)); | ||
| 229 | + pipe.InitBuffer(outQueue, 2, kRowTile * sizeof(float)); | ||
| 230 | + LocalTensor<float> xCol = xColBuf.Get<float>(); | ||
| 231 | + LocalTensor<float> xRow = xRowBuf.Get<float>(); | ||
| 232 | + LocalTensor<float> xWork = xWorkBuf.Get<float>(); | ||
| 233 | + LocalTensor<float> span = spanBuf.Get<float>(); | ||
| 234 | + LocalTensor<uint32_t> offsets = offsetBuf.Get<uint32_t>(); | ||
| 235 | + auto ubXCol = reinterpret_cast<__ubuf__ float*>(xCol.GetPhyAddr()); | ||
| 236 | + auto ubOffsets = reinterpret_cast<__ubuf__ uint32_t*>(offsets.GetPhyAddr()); | ||
| 237 | + const uint32_t colCount = colEnd - colStart; | ||
| 238 | + if (colCount > kLocalColFloats) return; | ||
| 239 | + CanonicalizeStridedRange(xCol, span, offsets, gmX, tiling.n, tiling.incx, colStart, colCount); | ||
| 240 | + | ||
| 241 | + for (uint32_t tileRow = 0; tileRow < tiling.n; tileRow += kRowTile) { | ||
| 242 | + const uint32_t tileEnd = (tiling.n - tileRow < kRowTile) ? tiling.n : tileRow + kRowTile; | ||
| 243 | + const uint32_t fullCount = tileEnd - tileRow; | ||
| 244 | + CanonicalizeStridedRange(xRow, span, offsets, gmX, tiling.n, tiling.incx, tileRow, fullCount); | ||
| 245 | + const DataCopyExtParams fullCopy{1, fullCount * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 246 | + const DataCopyPadExtParams<float> pad{true, 0, 0, 0.0f}; | ||
| 247 | + | ||
| 248 | + const uint32_t rectBegin = tiling.uplo == ACLBLAS_UPPER | ||
| 249 | + ? (colStart > tileEnd ? colStart : tileEnd) : colStart; | ||
| 250 | + const uint32_t rectEnd = tiling.uplo == ACLBLAS_UPPER | ||
| 251 | + ? colEnd : (colEnd < tileRow ? colEnd : tileRow); | ||
| 252 | + if (rectBegin < rectEnd) { | ||
| 253 | + const uint32_t firstCol = rectBegin; | ||
| 254 | + const uint64_t firstOffset = tiling.uplo == ACLBLAS_UPPER | ||
| 255 | + ? UpperColumnBase(firstCol) + tileRow | ||
| 256 | + : LowerColumnBase(firstCol, tiling.n) + (tileRow - firstCol); | ||
| 257 | + LocalTensor<float> firstAp = apQueue.AllocTensor<float>(); | ||
| 258 | + DataCopyPad(firstAp, apGm[firstOffset], fullCopy, pad); | ||
| 259 | + apQueue.EnQue(firstAp); | ||
| 260 | + for (uint32_t col = rectBegin; col < rectEnd; ++col) { | ||
| 261 | + if (col + 1U < rectEnd) { | ||
| 262 | + const uint32_t nextCol = col + 1U; | ||
| 263 | + const uint64_t nextOffset = tiling.uplo == ACLBLAS_UPPER | ||
| 264 | + ? UpperColumnBase(nextCol) + tileRow | ||
| 265 | + : LowerColumnBase(nextCol, tiling.n) + (tileRow - nextCol); | ||
| 266 | + LocalTensor<float> nextAp = apQueue.AllocTensor<float>(); | ||
| 267 | + DataCopyPad(nextAp, apGm[nextOffset], fullCopy, pad); | ||
| 268 | + apQueue.EnQue(nextAp); | ||
| 269 | + } | ||
| 270 | + LocalTensor<float> apIn = apQueue.DeQue<float>(); | ||
| 271 | + LocalTensor<float> out = outQueue.AllocTensor<float>(); | ||
| 272 | + const float axCol = tiling.alpha * ubXCol[col - colStart]; | ||
| 273 | + Muls(out, xRow, axCol, fullCount); | ||
| 274 | + Add(out, out, apIn, fullCount); | ||
| 275 | + outQueue.EnQue(out); | ||
| 276 | + apQueue.FreeTensor(apIn); | ||
| 277 | + out = outQueue.DeQue<float>(); | ||
| 278 | + const uint64_t apOffset = tiling.uplo == ACLBLAS_UPPER | ||
| 279 | + ? UpperColumnBase(col) + tileRow | ||
| 280 | + : LowerColumnBase(col, tiling.n) + (tileRow - col); | ||
| 281 | + DataCopyPad(apGm[apOffset], out, fullCopy); | ||
| 282 | + outQueue.FreeTensor(out); | ||
| 283 | + } | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + const uint32_t diagBegin = colStart > tileRow ? colStart : tileRow; | ||
| 287 | + const uint32_t diagEnd = colEnd < tileEnd ? colEnd : tileEnd; | ||
| 288 | + if (diagBegin < diagEnd) { | ||
| 289 | + if (tiling.uplo == ACLBLAS_UPPER) { | ||
| 290 | + for (uint32_t col = diagBegin; col < diagEnd; ++col) { | ||
| 291 | + const uint32_t count = col + 1U - tileRow; | ||
| 292 | + const uint64_t apOffset = UpperColumnBase(col) + tileRow; | ||
| 293 | + DataCopyExtParams copy{1, count * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 294 | + LocalTensor<float> apIn = apQueue.AllocTensor<float>(); | ||
| 295 | + DataCopyPad(apIn, apGm[apOffset], copy, pad); | ||
| 296 | + apQueue.EnQue(apIn); | ||
| 297 | + apIn = apQueue.DeQue<float>(); | ||
| 298 | + LocalTensor<float> out = outQueue.AllocTensor<float>(); | ||
| 299 | + const float axCol = tiling.alpha * ubXCol[col - colStart]; | ||
| 300 | + Muls(out, xRow, axCol, count); | ||
| 301 | + Add(out, out, apIn, count); | ||
| 302 | + outQueue.EnQue(out); | ||
| 303 | + apQueue.FreeTensor(apIn); | ||
| 304 | + out = outQueue.DeQue<float>(); | ||
| 305 | + DataCopyPad(apGm[apOffset], out, copy); | ||
| 306 | + outQueue.FreeTensor(out); | ||
| 307 | + } | ||
| 308 | + } else { | ||
| 309 | + for (uint32_t phase = 0; phase < 8U; ++phase) { | ||
| 310 | + for (uint32_t i = 0; i < fullCount; ++i) | ||
| 311 | + ubOffsets[i] = (i + phase) * sizeof(float); | ||
| 312 | + set_flag(PIPE_S, PIPE_V, EVENT_ID2); | ||
| 313 | + wait_flag(PIPE_S, PIPE_V, EVENT_ID2); | ||
| 314 | + uint32_t col = diagBegin + ((phase + 8U - (diagBegin & 7U)) & 7U); | ||
| 315 | + for (; col < diagEnd; col += 8U) { | ||
| 316 | + const uint32_t localStart = col - tileRow; | ||
| 317 | + const uint32_t count = tileEnd - col; | ||
| 318 | + Gather(xWork, xRow[localStart - phase], offsets, 0U, count); | ||
| 319 | + PipeBarrier<PIPE_V>(); | ||
| 320 | + const uint64_t apOffset = LowerColumnBase(col, tiling.n); | ||
| 321 | + DataCopyExtParams copy{1, count * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 322 | + LocalTensor<float> apIn = apQueue.AllocTensor<float>(); | ||
| 323 | + DataCopyPad(apIn, apGm[apOffset], copy, pad); | ||
| 324 | + apQueue.EnQue(apIn); | ||
| 325 | + apIn = apQueue.DeQue<float>(); | ||
| 326 | + LocalTensor<float> out = outQueue.AllocTensor<float>(); | ||
| 327 | + const float axCol = tiling.alpha * ubXCol[col - colStart]; | ||
| 328 | + Muls(out, xWork, axCol, count); | ||
| 329 | + Add(out, out, apIn, count); | ||
| 330 | + outQueue.EnQue(out); | ||
| 331 | + apQueue.FreeTensor(apIn); | ||
| 332 | + out = outQueue.DeQue<float>(); | ||
| 333 | + DataCopyPad(apGm[apOffset], out, copy); | ||
| 334 | + outQueue.FreeTensor(out); | ||
| 335 | + } | ||
| 336 | + set_flag(PIPE_V, PIPE_S, EVENT_ID2); | ||
| 337 | + wait_flag(PIPE_V, PIPE_S, EVENT_ID2); | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | + } | ||
| 341 | + } | ||
| 342 | +} | ||
| 343 | + | ||
| 344 | +__aicore__ inline void UnifiedContiguousColumns( | ||
| 345 | + GlobalTensor<float>& xGm, GlobalTensor<float>& apGm, const SsprTilingData& tiling, | ||
| 346 | + uint32_t colStart, uint32_t colEnd) | ||
| 347 | +{ | ||
| 348 | + constexpr uint32_t kRowTile = 16384U; | ||
| 349 | + constexpr uint32_t kPipelineFloats = 4096U; | ||
| 350 | + TPipe pipe; | ||
| 351 | + TBuf<TPosition::VECCALC> xTileBuf; | ||
| 352 | + TQue<QuePosition::VECIN, 1> xQueue; | ||
| 353 | + TQue<QuePosition::VECIN, 2> apQueue; | ||
| 354 | + TQue<QuePosition::VECOUT, 2> outQueue; | ||
| 355 | + pipe.InitBuffer(xTileBuf, kRowTile * sizeof(float)); | ||
| 356 | + pipe.InitBuffer(xQueue, 1, kPipelineFloats * sizeof(float)); | ||
| 357 | + pipe.InitBuffer(apQueue, 2, kPipelineFloats * sizeof(float)); | ||
| 358 | + pipe.InitBuffer(outQueue, 2, kPipelineFloats * sizeof(float)); | ||
| 359 | + LocalTensor<float> xTile = xTileBuf.Get<float>(); | ||
| 360 | + const DataCopyPadExtParams<float> pad{true, 0, 0, 0.0f}; | ||
| 361 | + | ||
| 362 | + for (uint32_t tileRow = 0; tileRow < tiling.n; tileRow += kRowTile) { | ||
| 363 | + const uint32_t tileEnd = (tiling.n - tileRow < kRowTile) ? tiling.n : tileRow + kRowTile; | ||
| 364 | + const uint32_t tileCount = tileEnd - tileRow; | ||
| 365 | + const DataCopyExtParams tileCopy{1, tileCount * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 366 | + DataCopyPad(xTile, xGm[tileRow], tileCopy, pad); | ||
| 367 | + PipeBarrier<PIPE_ALL>(); | ||
| 368 | + | ||
| 369 | + for (uint32_t col = colStart; col < colEnd; ++col) { | ||
| 370 | + const uint32_t rowBegin = tiling.uplo == ACLBLAS_UPPER ? 0U : col; | ||
| 371 | + const uint32_t rowEnd = tiling.uplo == ACLBLAS_UPPER ? col + 1U : tiling.n; | ||
| 372 | + uint32_t row = rowBegin > tileRow ? rowBegin : tileRow; | ||
| 373 | + const uint32_t segmentEnd = rowEnd < tileEnd ? rowEnd : tileEnd; | ||
| 374 | + if (row >= segmentEnd) continue; | ||
| 375 | + const uint64_t apBase = tiling.uplo == ACLBLAS_UPPER ? UpperColumnBase(col) | ||
| 376 | + : LowerColumnBase(col, tiling.n); | ||
| 377 | + const float axCol = tiling.alpha * xGm.GetValue(col); | ||
| 378 | + | ||
| 379 | + // A LOWER diagonal suffix may start at an arbitrary Unified Buffer offset. | ||
| 380 | + // Read only the first partial pipeline segment directly; every later | ||
| 381 | + // 4096-element segment starts at a zero-based aligned offset in xTile. | ||
| 382 | + const uint32_t localStart = row - tileRow; | ||
| 383 | + if ((localStart & (kPipelineFloats - 1U)) != 0U) { | ||
| 384 | + uint32_t alignedRow = (row + kPipelineFloats - 1U) & ~(kPipelineFloats - 1U); | ||
| 385 | + if (alignedRow > segmentEnd) alignedRow = segmentEnd; | ||
| 386 | + const uint32_t count = alignedRow - row; | ||
| 387 | + const uint64_t apOffset = apBase + row - rowBegin; | ||
| 388 | + const DataCopyExtParams copy{1, count * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 389 | + LocalTensor<float> xIn = xQueue.AllocTensor<float>(); | ||
| 390 | + LocalTensor<float> apIn = apQueue.AllocTensor<float>(); | ||
| 391 | + DataCopyPad(xIn, xGm[row], copy, pad); | ||
| 392 | + DataCopyPad(apIn, apGm[apOffset], copy, pad); | ||
| 393 | + xQueue.EnQue(xIn); | ||
| 394 | + apQueue.EnQue(apIn); | ||
| 395 | + xIn = xQueue.DeQue<float>(); | ||
| 396 | + apIn = apQueue.DeQue<float>(); | ||
| 397 | + LocalTensor<float> out = outQueue.AllocTensor<float>(); | ||
| 398 | + Muls(out, xIn, axCol, count); | ||
| 399 | + Add(out, out, apIn, count); | ||
| 400 | + outQueue.EnQue(out); | ||
| 401 | + xQueue.FreeTensor(xIn); | ||
| 402 | + apQueue.FreeTensor(apIn); | ||
| 403 | + out = outQueue.DeQue<float>(); | ||
| 404 | + DataCopyPad(apGm[apOffset], out, copy); | ||
| 405 | + outQueue.FreeTensor(out); | ||
| 406 | + row = alignedRow; | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + if (row < segmentEnd) { | ||
| 410 | + uint32_t currentCount = (segmentEnd - row < kPipelineFloats) | ||
| 411 | + ? segmentEnd - row : kPipelineFloats; | ||
| 412 | + uint64_t currentOffset = apBase + row - rowBegin; | ||
| 413 | + DataCopyExtParams currentCopy{ | ||
| 414 | + 1, currentCount * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 415 | + LocalTensor<float> firstAp = apQueue.AllocTensor<float>(); | ||
| 416 | + DataCopyPad(firstAp, apGm[currentOffset], currentCopy, pad); | ||
| 417 | + apQueue.EnQue(firstAp); | ||
| 418 | + | ||
| 419 | + while (row < segmentEnd) { | ||
| 420 | + const uint32_t nextRow = row + currentCount; | ||
| 421 | + if (nextRow < segmentEnd) { | ||
| 422 | + const uint32_t nextCount = (segmentEnd - nextRow < kPipelineFloats) | ||
| 423 | + ? segmentEnd - nextRow : kPipelineFloats; | ||
| 424 | + const uint64_t nextOffset = apBase + nextRow - rowBegin; | ||
| 425 | + const DataCopyExtParams nextCopy{ | ||
| 426 | + 1, nextCount * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 427 | + LocalTensor<float> nextAp = apQueue.AllocTensor<float>(); | ||
| 428 | + DataCopyPad(nextAp, apGm[nextOffset], nextCopy, pad); | ||
| 429 | + apQueue.EnQue(nextAp); | ||
| 430 | + } | ||
| 431 | + | ||
| 432 | + LocalTensor<float> apIn = apQueue.DeQue<float>(); | ||
| 433 | + LocalTensor<float> out = outQueue.AllocTensor<float>(); | ||
| 434 | + Muls(out, xTile[row - tileRow], axCol, currentCount); | ||
| 435 | + Add(out, out, apIn, currentCount); | ||
| 436 | + outQueue.EnQue(out); | ||
| 437 | + apQueue.FreeTensor(apIn); | ||
| 438 | + out = outQueue.DeQue<float>(); | ||
| 439 | + DataCopyPad(apGm[currentOffset], out, currentCopy); | ||
| 440 | + outQueue.FreeTensor(out); | ||
| 441 | + | ||
| 442 | + row = nextRow; | ||
| 443 | + if (row < segmentEnd) { | ||
| 444 | + currentCount = (segmentEnd - row < kPipelineFloats) | ||
| 445 | + ? segmentEnd - row : kPipelineFloats; | ||
| 446 | + currentOffset = apBase + row - rowBegin; | ||
| 447 | + currentCopy = DataCopyExtParams{ | ||
| 448 | + 1, currentCount * static_cast<uint32_t>(sizeof(float)), 0, 0, 0}; | ||
| 449 | + } | ||
| 450 | + } | ||
| 451 | + } | ||
| 452 | + } | ||
| 453 | + } | ||
| 454 | +} | ||
| 455 | + | ||
| 456 | + | ||
| 457 | +} | ||
| 458 | + | ||
| 459 | +extern "C" __global__ __aicore__ __vector__ void sspr_kernel( | ||
| 460 | + GM_ADDR x, GM_ADDR ap, const SsprTilingData tiling) | ||
| 461 | +{ | ||
| 462 | + KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY); | ||
| 463 | + | ||
| 464 | + GlobalTensor<float> xGm; | ||
| 465 | + GlobalTensor<float> apGm; | ||
| 466 | + const uint64_t absIncx = static_cast<uint64_t>(tiling.incx >= 0 ? tiling.incx : -tiling.incx); | ||
| 467 | + xGm.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(x), | ||
| 468 | + static_cast<uint64_t>(tiling.n - 1U) * absIncx + 1ULL); | ||
| 469 | + apGm.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(ap), | ||
| 470 | + static_cast<uint64_t>(tiling.n) * (tiling.n + 1ULL) / 2ULL); | ||
| 471 | + | ||
| 472 | + const uint32_t block = GetBlockIdx(); | ||
| 473 | + const uint32_t blocks = GetBlockNum(); | ||
| 474 | + uint32_t colStart; | ||
| 475 | + uint32_t colEnd; | ||
| 476 | + // The cost model is accurate for every n and uplo, so it replaces the previous | ||
| 477 | + // equal-column split unconditionally. The old code only enabled a weighted split for | ||
| 478 | + // strided large-n cases because its element-count model was wrong for short columns. | ||
| 479 | + colStart = WeightedColumnBoundary(block, blocks, tiling.n, tiling.uplo); | ||
| 480 | + colEnd = WeightedColumnBoundary(block + 1U, blocks, tiling.n, tiling.uplo); | ||
| 481 | + if (colStart >= colEnd) return; | ||
| 482 | + | ||
| 483 | + | ||
| 484 | + if (tiling.incx == 1) { | ||
| 485 | + UnifiedContiguousColumns(xGm, apGm, tiling, colStart, colEnd); | ||
| 486 | + return; | ||
| 487 | + } | ||
| 488 | + if (tiling.n <= kMaxCanonicalN && | ||
| 489 | + static_cast<uint32_t>(tiling.incx > 0 ? tiling.incx : -tiling.incx) <= kMaxPackedStride) { | ||
| 490 | + CachedStridedColumns(reinterpret_cast<__gm__ float*>(x), apGm, tiling, colStart, colEnd); | ||
| 491 | + return; | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + ScalarColumns(xGm, apGm, tiling, colStart, colEnd); | ||
| 495 | +} | ||
| 496 | + | ||
| 497 | +void sspr_kernel_do( | ||
| 498 | + GM_ADDR x, GM_ADDR ap, const SsprTilingData& tiling, uint32_t numBlocks, void* stream) | ||
| 499 | +{ | ||
| 500 | + sspr_kernel<<<numBlocks, nullptr, stream>>>(x, ap, tiling); | ||
| 501 | +} | ||
| @@ -0,0 +1,32 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +// UB-x path x vector cache size (32 KB = 8192 floats). 32KB leaves 216KB DCache for AP streaming. | ||
| 16 | +// Ascend950 profiling shows 32KB optimal across all n values (48KB/64KB both underperform). | ||
| 17 | +inline constexpr uint32_t UB_X_FLOATS = 8192; | ||
| 18 | + | ||
| 19 | +// n >= UB_THRESHOLD enables UB-x path. Ascend950 profiling (SPR_PROFILE_REPEAT=5, median): | ||
| 20 | +// n<128: DCache natural caching suffices, GM path faster | ||
| 21 | +// n>=128: DCache contention becomes significant, UB-x isolation wins | ||
| 22 | +// Both UPPER and LOWER share the same threshold. | ||
| 23 | +inline constexpr uint32_t UB_THRESHOLD = 128; | ||
| 24 | + | ||
| 25 | +struct SsprTilingData { | ||
| 26 | + uint32_t numThreads; // threads per block | ||
| 27 | + uint32_t columnsPerBlock; // columns per block | ||
| 28 | + uint32_t n; // matrix order | ||
| 29 | + uint32_t uplo; // ACLBLAS_UPPER(121) or ACLBLAS_LOWER(122) | ||
| 30 | + float alpha; // scalar alpha (dereferenced from pointer) | ||
| 31 | + int64_t incx; // x vector stride | ||
| 32 | +}; | ||
| @@ -0,0 +1,71 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +// NPU wrapper for aclblasSspr — same signature as the API. | ||
| 21 | +// For n <= 0 or null handle, passes through directly to hit host-side | ||
| 22 | +// parameter validation / early-return paths. | ||
| 23 | +// Otherwise, allocates device memory, copies H2D, invokes kernel, | ||
| 24 | +// synchronises, copies D2H, and frees. | ||
| 25 | +inline aclblasStatus_t aclblasSspr_npu( | ||
| 26 | + aclblasHandle_t handle, | ||
| 27 | + aclblasFillMode_t uplo, | ||
| 28 | + int n, | ||
| 29 | + const float* alpha, | ||
| 30 | + const float* x, | ||
| 31 | + int incx, | ||
| 32 | + float* ap) | ||
| 33 | +{ | ||
| 34 | + if (handle == nullptr || n <= 0) { | ||
| 35 | + return aclblasSspr(handle, uplo, n, alpha, x, incx, ap); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + const int allocN = std::max(1, n); | ||
| 39 | + const int absIncx = std::abs(incx); | ||
| 40 | + const size_t xBytes = static_cast<size_t>((allocN - 1) * absIncx + 1) * sizeof(float); | ||
| 41 | + const size_t apBytes = static_cast<size_t>(allocN) * (allocN + 1) / 2 * sizeof(float); | ||
| 42 | + | ||
| 43 | + void* dX = nullptr; | ||
| 44 | + void* dAP = nullptr; | ||
| 45 | + aclError aclRet; | ||
| 46 | + | ||
| 47 | + if (x != nullptr) { | ||
| 48 | + aclRet = aclrtMalloc(&dX, xBytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 49 | + if (aclRet != ACL_SUCCESS) return ACLBLAS_STATUS_ALLOC_FAILED; | ||
| 50 | + aclRet = aclrtMemcpy(dX, xBytes, x, xBytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 51 | + if (aclRet != ACL_SUCCESS) { aclrtFree(dX); return ACLBLAS_STATUS_INTERNAL_ERROR; } | ||
| 52 | + } | ||
| 53 | + if (ap != nullptr) { | ||
| 54 | + aclRet = aclrtMalloc(&dAP, apBytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 55 | + if (aclRet != ACL_SUCCESS) { if (dX) aclrtFree(dX); return ACLBLAS_STATUS_ALLOC_FAILED; } | ||
| 56 | + aclRet = aclrtMemcpy(dAP, apBytes, ap, apBytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 57 | + if (aclRet != ACL_SUCCESS) { if (dX) aclrtFree(dX); aclrtFree(dAP); return ACLBLAS_STATUS_INTERNAL_ERROR; } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + aclblasStatus_t ret = aclblasSspr(handle, uplo, n, alpha, | ||
| 61 | + static_cast<const float*>(dX), incx, static_cast<float*>(dAP)); | ||
| 62 | + | ||
| 63 | + aclrtSynchronizeDevice(); | ||
| 64 | + if (ret == ACLBLAS_STATUS_SUCCESS && ap != nullptr) { | ||
| 65 | + aclrtMemcpy(ap, apBytes, dAP, apBytes, ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + if (dX) aclrtFree(dX); | ||
| 69 | + if (dAP) aclrtFree(dAP); | ||
| 70 | + return ret; | ||
| 71 | +} | ||
| @@ -0,0 +1,83 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +class SsprArch22Test : public BlasTest<SsprParam> { }; | ||
| 22 | + | ||
| 23 | +TEST_F(SsprArch22Test, NullHandle) | ||
| 24 | +{ | ||
| 25 | + float alphaVal = 1.0f; | ||
| 26 | + aclblasStatus_t ret = aclblasSspr(nullptr, ACLBLAS_UPPER, 4, &alphaVal, nullptr, 1, nullptr); | ||
| 27 | + EXPECT_EQ(ret, ACLBLAS_STATUS_HANDLE_IS_NULLPTR); | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +INSTANTIATE_TEST_SUITE_P( | ||
| 31 | + Sspr, SsprArch22Test, | ||
| 32 | + ::testing::ValuesIn(GetCasesFromCsv<SsprParam>(ReplaceFileExtension2Csv(__FILE__))), | ||
| 33 | + PrintCaseInfoString<SsprParam>); | ||
| 34 | + | ||
| 35 | +TEST_P(SsprArch22Test, CsvDriven) | ||
| 36 | +{ | ||
| 37 | + const auto& p = GetParam(); | ||
| 38 | + | ||
| 39 | + int absIncx = std::abs(p.incx); | ||
| 40 | + size_t xLen = (p.n > 0) ? static_cast<size_t>((p.n - 1) * absIncx + 1) : 0; | ||
| 41 | + size_t apLen = (p.n > 0) ? static_cast<size_t>(p.n) * (p.n + 1) / 2 : 0; | ||
| 42 | + | ||
| 43 | + // Generate x vector (strided, supports negative incx) | ||
| 44 | + std::vector<float> xHost = makeBlasStrided(p.n, p.incx, p.x, p.randomSeed); | ||
| 45 | + | ||
| 46 | + // Generate packed AP (triangular / random) | ||
| 47 | + // When ap uses UPPER/LOWER pattern in its BlasFillMode, makeBlasPacked preserves it. | ||
| 48 | + // Otherwise, use the uplo to select triangular layout. | ||
| 49 | + bool useUpper = (p.uplo == ACLBLAS_UPPER); | ||
| 50 | + std::vector<float> apHost; | ||
| 51 | + if (p.ap.pattern == BlasFillMode::P_UPPER || p.ap.pattern == BlasFillMode::P_LOWER) { | ||
| 52 | + apHost = makeBlasPacked(p.n, p.ap, p.randomSeed); | ||
| 53 | + } else { | ||
| 54 | + apHost = makeBlasTriangular(p.n, useUpper, p.ap, p.randomSeed); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + // Copy apHost for alpha=0 exact verification | ||
| 58 | + std::vector<float> apOrig = apHost; | ||
| 59 | + | ||
| 60 | + const float* xPtr = xHost.empty() ? nullptr : xHost.data(); | ||
| 61 | + float* apPtr = apHost.empty() ? nullptr : apHost.data(); | ||
| 62 | + const float* alphaPtr = p.alphaNull ? nullptr : &p.alpha; | ||
| 63 | + | ||
| 64 | + aclblasStatus_t ret = aclblasSspr_npu( | ||
| 65 | + SsprArch22Test::handle_, p.uplo, p.n, alphaPtr, xPtr, p.incx, apPtr); | ||
| 66 | + EXPECT_EQ(static_cast<int>(ret), static_cast<int>(p.expectResult)); | ||
| 67 | + if (p.expectResult != ACLBLAS_STATUS_SUCCESS) return; | ||
| 68 | + if (p.n == 0) return; | ||
| 69 | + | ||
| 70 | + // Compute CPU golden | ||
| 71 | + std::vector<float> golden = apOrig; | ||
| 72 | + aclblasSspr_cpu( | ||
| 73 | + SsprArch22Test::handle_, p.uplo, p.n, &p.alpha, xHost.data(), p.incx, golden.data()); | ||
| 74 | + | ||
| 75 | + VerifyConfig cfg; | ||
| 76 | + if (p.alpha == 0.0f && !p.alphaNull) { | ||
| 77 | + cfg.mode = PrecisionMode::EXACT; | ||
| 78 | + EXPECT_TRUE(Verifier::verifyVector(apPtr, golden.data(), apLen, 1, cfg, p.caseName)); | ||
| 79 | + } else { | ||
| 80 | + applyMixedTolerance(cfg, ACL_FLOAT, golden.data(), apLen); | ||
| 81 | + EXPECT_TRUE(Verifier::verifyVector(apPtr, golden.data(), apLen, 1, cfg, p.caseName)); | ||
| 82 | + } | ||
| 83 | +} | ||
| @@ -0,0 +1,59 @@ | |||
| 1 | +case_name,description,uplo,n,alpha,x,incx,ap,expect_result,random_seed | ||
| 2 | +TC_L0_02,n_negative,ACLBLAS_UPPER,-5,1.0,NULLPTR,1,NULLPTR,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 3 | +TC_L0_03,n_zero_upper,ACLBLAS_UPPER,0,1.0,NULLPTR,1,NULLPTR,ACLBLAS_STATUS_SUCCESS,42 | ||
| 4 | +TC_L0_04,uplo_invalid,INVALID,4,1.0,NULLPTR,1,NULLPTR,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 5 | +TC_L0_05,incx_zero,ACLBLAS_UPPER,4,1.0,NULLPTR,0,NULLPTR,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 6 | +TC_L0_06,incx_intmin,ACLBLAS_UPPER,4,1.0,NULLPTR,-2147483648,NULLPTR,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 7 | +TC_L0_07,alpha_null,ACLBLAS_UPPER,4,NULLPTR,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 8 | +TC_L0_08,x_null,ACLBLAS_UPPER,4,1.0,NULLPTR,1,RANDOM_2_2,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 9 | +TC_L0_09,ap_null,ACLBLAS_UPPER,4,1.0,RANDOM_2_2,1,NULLPTR,ACLBLAS_STATUS_INVALID_VALUE,42 | ||
| 10 | +TC_L0_10,alpha_zero,ACLBLAS_UPPER,4,0.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 11 | +TC_L0_11,n1_upper,ACLBLAS_UPPER,1,1.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 12 | +TC_L0_12,n1_lower,ACLBLAS_LOWER,1,1.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 13 | +TC_L0_13,n4_upper,ACLBLAS_UPPER,4,1.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 14 | +TC_L0_14,n4_lower,ACLBLAS_LOWER,4,1.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 15 | +TC_L0_15,n4_upper_neg_alpha,ACLBLAS_UPPER,4,-1.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 16 | +TC_L0_16,n4_lower_neg_alpha,ACLBLAS_LOWER,4,-1.0,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 17 | +TC_L0_17,n4_upper_neg_incx,ACLBLAS_UPPER,4,1.0,RANDOM_2_2,-1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 18 | +TC_L0_18,n4_lower_neg_incx,ACLBLAS_LOWER,4,1.0,RANDOM_2_2,-1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 19 | +TC_L0_19,n4_upper_incx2,ACLBLAS_UPPER,4,1.0,RANDOM_2_2,2,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 20 | +TC_L0_20,n4_lower_incx2,ACLBLAS_LOWER,4,1.0,RANDOM_2_2,2,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 21 | +TC_L0_21,n64_upper_ubx,ACLBLAS_UPPER,64,0.5,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 22 | +TC_L0_22,n64_lower_ubx,ACLBLAS_LOWER,64,0.5,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 23 | +TC_L0_23,n10000_upper_gm_fallback,ACLBLAS_UPPER,10000,0.5,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 24 | +TC_L0_24,n10000_lower_gm_fallback,ACLBLAS_LOWER,10000,0.5,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 25 | +TC_L0_25,n10000_upper_incx2,ACLBLAS_UPPER,10000,1.0,RANDOM_2_2,2,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 26 | +TC_L0_26,n10000_lower_neg_incx,ACLBLAS_LOWER,10000,1.0,RANDOM_2_2,-1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,42 | ||
| 27 | +TC_A22_001,n7_upper_vec_tail,ACLBLAS_UPPER,7,0.75,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260851 | ||
| 28 | +TC_A22_002,n8_lower_block,ACLBLAS_LOWER,8,-0.5,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260852 | ||
| 29 | +TC_A22_003,n9_upper_vec_tail,ACLBLAS_UPPER,9,1.25,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260853 | ||
| 30 | +TC_A22_004,n39_lower_core_boundary,ACLBLAS_LOWER,39,0.75,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260854 | ||
| 31 | +TC_A22_005,n40_upper_core_boundary,ACLBLAS_UPPER,40,0.75,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260855 | ||
| 32 | +TC_A22_006,n41_lower_core_boundary,ACLBLAS_LOWER,41,0.75,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260856 | ||
| 33 | +TC_A22_007,n63_upper_repeat_tail,ACLBLAS_UPPER,63,0.625,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260857 | ||
| 34 | +TC_A22_008,n64_lower_repeat,ACLBLAS_LOWER,64,0.625,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260858 | ||
| 35 | +TC_A22_009,n65_upper_repeat_tail,ACLBLAS_UPPER,65,0.625,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260859 | ||
| 36 | +TC_A22_010,n257_upper_incx_neg2,ACLBLAS_UPPER,257,1.125,RANDOM_2_2,-2,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260860 | ||
| 37 | +TC_A22_011,n257_lower_incx_neg2,ACLBLAS_LOWER,257,1.125,RANDOM_2_2,-2,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260861 | ||
| 38 | +TC_A22_012,n513_upper_incx32,ACLBLAS_UPPER,513,0.875,RANDOM_2_2,32,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260862 | ||
| 39 | +TC_A22_013,n513_lower_incx_neg32,ACLBLAS_LOWER,513,0.875,RANDOM_2_2,-32,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260863 | ||
| 40 | +TC_A22_014,n1025_upper_incx128,ACLBLAS_UPPER,1025,-0.625,RANDOM_2_2,128,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260864 | ||
| 41 | +TC_A22_015,n1025_lower_incx_neg128,ACLBLAS_LOWER,1025,-0.625,RANDOM_2_2,-128,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260865 | ||
| 42 | +TC_A22_016,n16385_upper_incx16,ACLBLAS_UPPER,16385,0.75,RANDOM_2_2,16,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260866 | ||
| 43 | +TC_A22_017,n16385_lower_incx_neg16,ACLBLAS_LOWER,16385,0.75,RANDOM_2_2,-16,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260867 | ||
| 44 | +TC_A22_018,n257_upper_incx3,ACLBLAS_UPPER,257,1.125,RANDOM_2_2,3,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260868 | ||
| 45 | +TC_A22_019,n257_lower_incx3,ACLBLAS_LOWER,257,1.125,RANDOM_2_2,3,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260869 | ||
| 46 | +TC_A22_020,n513_upper_incx5,ACLBLAS_UPPER,513,0.875,RANDOM_2_2,5,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260870 | ||
| 47 | +TC_A22_021,n513_lower_incx_neg5,ACLBLAS_LOWER,513,0.875,RANDOM_2_2,-5,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260871 | ||
| 48 | +TC_A22_022,n1025_upper_incx7,ACLBLAS_UPPER,1025,-0.625,RANDOM_2_2,7,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260872 | ||
| 49 | +TC_A22_023,n1025_lower_incx_neg7,ACLBLAS_LOWER,1025,-0.625,RANDOM_2_2,-7,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260873 | ||
| 50 | +TC_A22_024,n4097_upper_incx9,ACLBLAS_UPPER,4097,0.75,RANDOM_2_2,9,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260874 | ||
| 51 | +TC_A22_025,n4097_lower_incx_neg9,ACLBLAS_LOWER,4097,0.75,RANDOM_2_2,-9,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260875 | ||
| 52 | +TC_A22_026,n8193_upper_incx33,ACLBLAS_UPPER,8193,1.375,RANDOM_2_2,33,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260876 | ||
| 53 | +TC_A22_027,n8193_lower_incx_neg33,ACLBLAS_LOWER,8193,1.375,RANDOM_2_2,-33,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260877 | ||
| 54 | +TC_UNIFIED_001,n127_upper_threshold_minus_one,ACLBLAS_UPPER,127,0.875,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260878 | ||
| 55 | +TC_UNIFIED_002,n127_lower_threshold_minus_one,ACLBLAS_LOWER,127,0.875,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260879 | ||
| 56 | +TC_UNIFIED_003,n128_upper_threshold_exact,ACLBLAS_UPPER,128,0.875,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260880 | ||
| 57 | +TC_UNIFIED_004,n128_lower_threshold_exact,ACLBLAS_LOWER,128,0.875,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260881 | ||
| 58 | +TC_UNIFIED_005,n129_upper_threshold_plus_one,ACLBLAS_UPPER,129,0.875,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260882 | ||
| 59 | +TC_UNIFIED_006,n129_lower_threshold_plus_one,ACLBLAS_LOWER,129,0.875,RANDOM_2_2,1,RANDOM_2_2,ACLBLAS_STATUS_SUCCESS,20260883 | ||
🟡 Medium Priority
建议:删除未使用的 absIncx/xLen(第 39-40 行),或改为先判断
p.incx == INT_MIN再计算,并将(p.n - 1) * absIncx + 1提升为 64 位运算避免有符号溢出。