已开启
Feat: 新增面向arch22的aclblasCaxpy任意incx/incy支持 #334
guodong54_创建于 29 天前
Feat: 新增面向arch22的aclblasCaxpy任意incx/incy支持 #334
已开启
共 13 个文件变更+2072-376
| @@ -0,0 +1,159 @@ | |||
| 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 | +enum class CaxpyStrideClass : uint32_t { | ||
| 17 | + DENSE = 0, | ||
| 18 | + X_ONLY_POSITIVE = 1, | ||
| 19 | + Y_ONLY_POSITIVE = 2, | ||
| 20 | + DUAL_SYMMETRIC_POSITIVE = 3, | ||
| 21 | + DUAL_ASYMMETRIC_POSITIVE = 4, | ||
| 22 | + NEGATIVE_MIXED = 5, | ||
| 23 | +}; | ||
| 24 | + | ||
| 25 | +enum class CaxpyKernelVariant : uint32_t { | ||
| 26 | + DENSE_PIPELINED_ATOMIC = 0, | ||
| 27 | + STRIDED_SCALAR = 1, | ||
| 28 | + STRIDED_SHARED_PIPELINED_ATOMIC = 2, | ||
| 29 | + STRIDED_DISJOINT_PIPELINED_ATOMIC = 3, | ||
| 30 | + STRIDED_SHARED_PIPELINED_SCALAR_TAIL = 4, | ||
| 31 | + STRIDED_DISJOINT_PIPELINED_SCALAR_TAIL = 5, | ||
| 32 | +}; | ||
| 33 | + | ||
| 34 | +constexpr uint64_t CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK = 16; | ||
| 35 | +constexpr uint64_t CAXPY_STRIDED_MAX_PER_BLOCK_PER_WAVE = 8192; | ||
| 36 | +// Fixed tile capacity used by the disjoint pipeline and as the shared | ||
| 37 | +// pipeline's UB buffer allocation size (see CAXPY_STRIDED_TILE_COUNT in | ||
| 38 | +// caxpy_kernel.cpp, which must stay equal to this value). | ||
| 39 | +constexpr uint64_t CAXPY_STRIDED_TILE_COUNT = 512; | ||
| 40 | +// The shared pipeline's lowScratch UB scratch buffer used for the raw span | ||
| 41 | +// read is 38 KiB (CAXPY_DENSE_TILE_BYTES in caxpy_kernel.cpp). A span read of | ||
| 42 | +// `tile` complex elements at increment `incx` covers | ||
| 43 | +// ((tile-1)*|incx|+1) complex elements == ((tile-1)*|incx|+1)*8 bytes of | ||
| 44 | +// physical address range, which must fit in that buffer. Solving for the | ||
| 45 | +// largest tile <= CAXPY_STRIDED_TILE_COUNT that satisfies this bound gives | ||
| 46 | +// the runtime tile capacity the shared pipeline should use for a given incx; | ||
| 47 | +// this generalizes the old fixed CAXPY_SHARED_X_SPAN_MAX_INCREMENT=9 cutoff | ||
| 48 | +// (which was really just "does tile=512 fit"), letting larger |incx| still | ||
| 49 | +// use the faster span-read shared pipeline instead of falling back to | ||
| 50 | +// disjoint sparse reads, as long as the shrunk tile stays usable. | ||
| 51 | +constexpr uint64_t CAXPY_SHARED_LOW_SCRATCH_BYTES = 38 * 1024; | ||
| 52 | +constexpr uint64_t CAXPY_COMPLEX_BYTES = 8; | ||
| 53 | + | ||
| 54 | +// Largest tile (in complex elements, capped at CAXPY_STRIDED_TILE_COUNT) whose | ||
| 55 | +// span read at the given |incx| fits in the shared pipeline's lowScratch | ||
| 56 | +// buffer. absIncx must be >= 1 (callers only invoke this for direct-strided, | ||
| 57 | +// non-dense cases where incx != 0). | ||
| 58 | +constexpr uint64_t CaxpySharedTileCapacity(uint64_t absIncx) | ||
| 59 | +{ | ||
| 60 | + const uint64_t maxSpanComplexElements = CAXPY_SHARED_LOW_SCRATCH_BYTES / CAXPY_COMPLEX_BYTES; | ||
| 61 | + // tile such that (tile-1)*absIncx + 1 <= maxSpanComplexElements | ||
| 62 | + // => tile <= (maxSpanComplexElements - 1) / absIncx + 1 | ||
| 63 | + const uint64_t maxTileForIncrement = (maxSpanComplexElements - 1) / absIncx + 1; | ||
| 64 | + return maxTileForIncrement < CAXPY_STRIDED_TILE_COUNT ? maxTileForIncrement : CAXPY_STRIDED_TILE_COUNT; | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +// The shared pipeline's tail-splitting logic (shared_tile_bounds in | ||
| 68 | +// caxpy_kernel.cpp) rebalances the last two tiles when the natural final tile | ||
| 69 | +// would be shorter than CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK+1 | ||
| 70 | +// (CAXPY_INTERLEAVE_COUNT+1 in caxpy_kernel.cpp, same constant) elements, by | ||
| 71 | +// splitting their combined length roughly in half. For that split to never | ||
| 72 | +// itself produce a half shorter than the interleave width, the worst case | ||
| 73 | +// (combined length == tileCapacity+1, the smallest total that still triggers | ||
| 74 | +// the split branch) must divide into two halves each >= | ||
| 75 | +// CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK+1: ceil((tileCapacity+1)/2) and | ||
| 76 | +// floor((tileCapacity+1)/2) both >= CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK+1, | ||
| 77 | +// which holds starting at tileCapacity == 2*(CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK+1)-1. | ||
| 78 | +// A smaller usable-capacity threshold (originally CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK+1 | ||
| 79 | +// itself) let the split produce a too-short half, caught by the | ||
| 80 | +// dynamic_shared_boundary_usable regression test (n=8000, incx=303, tile=17) | ||
| 81 | +// only in a Release build where the resulting silent wrong compute went | ||
| 82 | +// uncaught -- a shrunk-tile-capacity variant of the same class of bug as the | ||
| 83 | +// earlier fixed-tile computeOffset drift. | ||
| 84 | +constexpr uint64_t CAXPY_SHARED_MIN_USABLE_TILE_CAPACITY = 2 * (CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK + 1) - 1; | ||
| 85 | + | ||
| 86 | +// CAXPY_SHARED_MIN_USABLE_TILE_CAPACITY only bounds *correctness*: below it | ||
| 87 | +// the tail-split can produce too-short tiles. It says nothing about | ||
| 88 | +// performance. As |incx| grows, the shrunk tile's span read | ||
| 89 | +// ((tile-1)*|incx|+1 complex elements) carries a shrinking fraction of useful | ||
| 90 | +// data -- at incx=16 the span is ~6% useful, at incx=100 it is ~1% -- so GM | ||
| 91 | +// bandwidth spent reading skipped-over elements eventually exceeds what the | ||
| 92 | +// disjoint pipeline's per-element sparse reads cost instead. Measured on | ||
| 93 | +// Ascend 910B3 / CANN 9.0 A3, n=1048576 (xlarge, enough tiles per core to | ||
| 94 | +// reach steady state and not be dominated by per-tile dispatch overhead): | ||
| 95 | +// shared span-read is faster than disjoint through incx<=60, comparable | ||
| 96 | +// around incx=60-65, and progressively worse beyond that (roughly +7% at | ||
| 97 | +// incx=70, +97% at incx=150 in one measured run). This cutoff is therefore a | ||
| 98 | +// measured performance crossover, not a UB-buffer-fit or correctness limit; | ||
| 99 | +// re-measure if the UB budget, tile capacity formula, or disjoint pipeline's | ||
| 100 | +// per-element cost changes. | ||
| 101 | +constexpr uint64_t CAXPY_SHARED_SPAN_PERFORMANCE_MAX_INCREMENT = 60; | ||
| 102 | + | ||
| 103 | +constexpr uint64_t CaxpyFinalWaveCount(uint64_t n, uint32_t numBlocks) | ||
| 104 | +{ | ||
| 105 | + const uint64_t waveCapacity = static_cast<uint64_t>(numBlocks) * CAXPY_STRIDED_MAX_PER_BLOCK_PER_WAVE; | ||
| 106 | + if (n <= waveCapacity) | ||
| 107 | + return n; | ||
| 108 | + const uint64_t remainder = n % waveCapacity; | ||
| 109 | + return remainder == 0 ? waveCapacity : remainder; | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +// Preconditions are enforced by aclblasCaxpy argument validation and execution | ||
| 113 | +// setup: incx and incy are non-zero and are not INT64_MIN; numBlocks is non-zero. | ||
| 114 | +constexpr CaxpyStrideClass ClassifyCaxpyStride(int64_t incx, int64_t incy) | ||
| 115 | +{ | ||
| 116 | + if (incx < 0 || incy < 0) | ||
| 117 | + return CaxpyStrideClass::NEGATIVE_MIXED; | ||
| 118 | + if (incx == 1 && incy == 1) | ||
| 119 | + return CaxpyStrideClass::DENSE; | ||
| 120 | + if (incy == 1) | ||
| 121 | + return CaxpyStrideClass::X_ONLY_POSITIVE; | ||
| 122 | + if (incx == 1) | ||
| 123 | + return CaxpyStrideClass::Y_ONLY_POSITIVE; | ||
| 124 | + if (incx == incy) | ||
| 125 | + return CaxpyStrideClass::DUAL_SYMMETRIC_POSITIVE; | ||
| 126 | + return CaxpyStrideClass::DUAL_ASYMMETRIC_POSITIVE; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +constexpr uint64_t CaxpyAbsIncrement(int64_t increment) | ||
| 130 | +{ | ||
| 131 | + return increment > 0 ? static_cast<uint64_t>(increment) : static_cast<uint64_t>(-(increment + 1)) + 1; | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +constexpr bool UseCaxpySharedXSpanPipeline(int64_t incx) | ||
| 135 | +{ | ||
| 136 | + const uint64_t absIncx = CaxpyAbsIncrement(incx); | ||
| 137 | + return absIncx <= CAXPY_SHARED_SPAN_PERFORMANCE_MAX_INCREMENT && | ||
| 138 | + CaxpySharedTileCapacity(absIncx) >= CAXPY_SHARED_MIN_USABLE_TILE_CAPACITY; | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +constexpr CaxpyKernelVariant SelectCaxpyKernelVariant( | ||
| 142 | + uint64_t n, int64_t incx, int64_t incy, bool directStrided, uint32_t numBlocks) | ||
| 143 | +{ | ||
| 144 | + const CaxpyStrideClass strideClass = ClassifyCaxpyStride(incx, incy); | ||
| 145 | + if (strideClass == CaxpyStrideClass::DENSE || !directStrided) | ||
| 146 | + return CaxpyKernelVariant::DENSE_PIPELINED_ATOMIC; | ||
| 147 | + const bool sharedXSpan = UseCaxpySharedXSpanPipeline(incx); | ||
| 148 | + const uint64_t finalWaveCount = CaxpyFinalWaveCount(n, numBlocks); | ||
| 149 | + const uint64_t finalWaveBlocks = finalWaveCount < numBlocks ? finalWaveCount : numBlocks; | ||
| 150 | + const uint64_t minBlockCount = finalWaveCount / finalWaveBlocks; | ||
| 151 | + if (minBlockCount <= CAXPY_STRIDED_SCALAR_MAX_PER_BLOCK) | ||
| 152 | + return n == finalWaveCount ? CaxpyKernelVariant::STRIDED_SCALAR : | ||
| 153 | + (sharedXSpan ? CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_SCALAR_TAIL : | ||
| 154 | + CaxpyKernelVariant::STRIDED_DISJOINT_PIPELINED_SCALAR_TAIL); | ||
| 155 | + return sharedXSpan ? CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC : | ||
| 156 | + CaxpyKernelVariant::STRIDED_DISJOINT_PIPELINED_ATOMIC; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | + | ||
| @@ -14,6 +14,8 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 18 | + | ||
| 17 | 19 | ||
| 18 | 20 | ||
| 19 | 21 | ||
| @@ -22,30 +24,58 @@ | |||
| 22 | 24 | ||
| 23 | 25 | ||
| 24 | 26 | ||
| 27 | + | ||
| 28 | + | ||
| 25 | 29 | ||
| 26 | -void caxpy_kernel_do(uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, | 30 | +void caxpy_kernel_do( |
Z | |||
| 27 | - uint32_t numBlocks, void *stream); | 31 | + uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, uint32_t numBlocks, void* stream); |
| 32 | +void caxpy_strided_shared_kernel_do( | ||
| 33 | + uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, uint32_t numBlocks, void* stream); | ||
| 34 | +void caxpy_strided_disjoint_kernel_do( | ||
| 35 | + uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, uint32_t numBlocks, void* stream); | ||
| 36 | +void caxpy_strided_scalar_kernel_do(uint8_t* x, uint8_t* y, uint8_t* tilingGm, uint32_t numBlocks, void* stream); | ||
| 28 | 37 | ||
| 29 | -constexpr uint32_t COMPLEX_NUM = 2; | 38 | +constexpr uint32_t FLOATS_PER_COMPLEX = 2; |
| 30 | constexpr uint32_t K_FACTOR_4 = 4; | 39 | constexpr uint32_t K_FACTOR_4 = 4; |
| 31 | constexpr uint32_t DEFAULT_VECTOR_NUM = 40; | 40 | constexpr uint32_t DEFAULT_VECTOR_NUM = 40; |
| 41 | +static_assert( | ||
| 42 | + DEFAULT_VECTOR_NUM == CAXPY_MAX_VECTOR_CORES, | ||
| 43 | + "Host block cap must match the tiling struct's per-block array size"); | ||
| 32 | constexpr uint32_t MAX_DATA_COUNT = 38 * 1024 / sizeof(float); | 44 | constexpr uint32_t MAX_DATA_COUNT = 38 * 1024 / sizeof(float); |
| 45 | +constexpr uint32_t STRIDED_TILE_COMPLEX_COUNT = 512; | ||
| 46 | +constexpr uint32_t BLOCKED_FLOATS_PER_COMPLEX = 8; | ||
| 47 | +constexpr uint32_t GATHER_PADDING_COUNT = 64; | ||
| 48 | +constexpr uint32_t X_NORMALIZE_OFFSET_COUNT = | ||
| 49 | + STRIDED_TILE_COMPLEX_COUNT * BLOCKED_FLOATS_PER_COMPLEX + GATHER_PADDING_COUNT; | ||
| 50 | +constexpr uint32_t STORE_OFFSET_COUNT = STRIDED_TILE_COMPLEX_COUNT * FLOATS_PER_COMPLEX + GATHER_PADDING_COUNT; | ||
| 51 | +constexpr uint32_t X_NORMALIZE_OFFSET_START = MAX_DATA_COUNT; | ||
| 52 | +constexpr uint32_t STORE_OFFSET_START = X_NORMALIZE_OFFSET_START + X_NORMALIZE_OFFSET_COUNT; | ||
| 53 | +constexpr uint32_t Y_GATHER_OFFSET_COUNT = STRIDED_TILE_COMPLEX_COUNT * FLOATS_PER_COMPLEX + GATHER_PADDING_COUNT; | ||
| 54 | +constexpr uint32_t Y_GATHER_OFFSET_START = STORE_OFFSET_START + STORE_OFFSET_COUNT; | ||
| 55 | +constexpr uint32_t DENSE_SWAP_OFFSET_START = Y_GATHER_OFFSET_START + Y_GATHER_OFFSET_COUNT; | ||
| 56 | +constexpr uint32_t MASK_DATA_COUNT = DENSE_SWAP_OFFSET_START + MAX_DATA_COUNT; | ||
| 57 | +static_assert(STORE_OFFSET_START + STORE_OFFSET_COUNT <= MASK_DATA_COUNT); | ||
| 58 | +static_assert(Y_GATHER_OFFSET_START + Y_GATHER_OFFSET_COUNT <= MASK_DATA_COUNT); | ||
| 59 | +static_assert(DENSE_SWAP_OFFSET_START + MAX_DATA_COUNT <= MASK_DATA_COUNT); | ||
| 33 | 60 | ||
| 34 | -struct CaxpyTilingData { | 61 | +// CaxpyTilingData is defined once in the shared header and included by both the |
| 35 | - uint32_t n; | 62 | +// host and the device kernel; see caxpy_tiling_data.h. CAXPY_MAX_VECTOR_CORES |
| 36 | - float alphaReal; | 63 | +// from that header equals DEFAULT_VECTOR_NUM below. |
| 37 | - float alphaImag; | ||
| 38 | - uint32_t startOffset[40]; | ||
| 39 | - uint32_t calNum[40]; | ||
| 40 | -}; | ||
| 41 | 64 | ||
| 42 | -CaxpyTilingData CalTilingData(uint32_t n, uint32_t vecCoreNum, float alphaReal, float alphaImag) | 65 | +CaxpyTilingData CalTilingData( |
| 66 | + uint32_t n, uint32_t vecCoreNum, float alphaReal, float alphaImag, int64_t incx, int64_t incy, | ||
| 67 | + uint32_t logicalBase = 0, uint32_t totalN = 0) | ||
| 43 | { | 68 | { |
| 44 | - CaxpyTilingData tilingData; | 69 | + CaxpyTilingData tilingData{}; |
| 45 | - tilingData.n = n * COMPLEX_NUM; | 70 | + tilingData.packedFloatCount = n * FLOATS_PER_COMPLEX; |
| 46 | tilingData.alphaReal = alphaReal; | 71 | tilingData.alphaReal = alphaReal; |
| 47 | tilingData.alphaImag = alphaImag; | 72 | tilingData.alphaImag = alphaImag; |
| 73 | + tilingData.totalN = totalN == 0 ? n : totalN; | ||
| 74 | + tilingData.incx = incx; | ||
| 75 | + tilingData.incy = incy; | ||
| 48 | 76 | ||
| 77 | + // Guard against division by zero below. Callers currently always pass a | ||
| 78 | + // non-zero count, but this helper stays self-contained. | ||
| 49 | if (vecCoreNum == 0) { | 79 | if (vecCoreNum == 0) { |
| 50 | vecCoreNum = 1; | 80 | vecCoreNum = 1; |
| 51 | } | 81 | } |
| @@ -56,98 +86,456 @@ CaxpyTilingData CalTilingData(uint32_t n, uint32_t vecCoreNum, float alphaReal, | |||
| 56 | tilingData.calNum[i] = 0; | 86 | tilingData.calNum[i] = 0; |
| 57 | } | 87 | } |
| 58 | 88 | ||
| 59 | - uint32_t rowNumEachCore = n / vecCoreNum; | 89 | + // Distribute n complex elements across vecCoreNum blocks as evenly as |
| 60 | - uint32_t remainRowNum = n % vecCoreNum; | 90 | + // possible: the first `remainder` blocks take one extra element. When |
| 91 | + // n < vecCoreNum, baseComplexPerCore is 0 and only the first n blocks get | ||
| 92 | + // one element each; the trailing blocks keep calNum == 0 and are skipped by | ||
| 93 | + // the device (it guards on calNum > 0), so no special-case branch is needed. | ||
| 94 | + const uint32_t baseComplexPerCore = n / vecCoreNum; | ||
| 95 | + const uint32_t remainder = n % vecCoreNum; | ||
| 61 | 96 | ||
| 62 | - if (rowNumEachCore == 0) { | 97 | + uint32_t currOffset = logicalBase * FLOATS_PER_COMPLEX; |
| 63 | - for (uint32_t i = 0; i < remainRowNum; i++) { | 98 | + for (uint32_t i = 0; i < vecCoreNum; i++) { |
| 64 | - tilingData.calNum[i] = COMPLEX_NUM; | 99 | + const uint32_t complexThisCore = baseComplexPerCore + (i < remainder ? 1 : 0); |
| 65 | - tilingData.startOffset[i] = i * COMPLEX_NUM; | 100 | + tilingData.calNum[i] = complexThisCore * FLOATS_PER_COMPLEX; |
| 66 | - } | 101 | + tilingData.startOffset[i] = currOffset; |
| 67 | - } else { | 102 | + currOffset += complexThisCore * FLOATS_PER_COMPLEX; |
| 68 | - uint32_t currOffset = 0; | ||
| 69 | - uint32_t currNum; | ||
| 70 | - for (uint32_t i = 0; i < vecCoreNum; i++) { | ||
| 71 | - if (i < remainRowNum) { | ||
| 72 | - currNum = rowNumEachCore + 1; | ||
| 73 | - } else { | ||
| 74 | - currNum = rowNumEachCore; | ||
| 75 | - } | ||
| 76 | - tilingData.calNum[i] = currNum * COMPLEX_NUM; | ||
| 77 | - tilingData.startOffset[i] = currOffset; | ||
| 78 | - currOffset += currNum * COMPLEX_NUM; | ||
| 79 | - } | ||
| 80 | } | 103 | } |
| 81 | 104 | ||
| 82 | return tilingData; | 105 | return tilingData; |
| 83 | } | 106 | } |
| 84 | 107 | ||
| 85 | -void GenMaskData(uint32_t* maskData) | 108 | +// Magnitude of a BLAS increment. Computed as -(inc+1)+1 rather than -inc so |
| 109 | +// that INT64_MIN would not overflow -- though callers reject INT64_MIN upstream | ||
| 110 | +// (ValidateCaxpyArguments), this keeps the helper correct in isolation. | ||
| 111 | +uint64_t AbsIncrement(int64_t increment) | ||
| 86 | { | 112 | { |
| 87 | - uint32_t offsetNum = COMPLEX_NUM; | 113 | + return increment > 0 ? static_cast<uint64_t>(increment) : static_cast<uint64_t>(-(increment + 1)) + 1; |
| 88 | - uint32_t complexCount = MAX_DATA_COUNT / COMPLEX_NUM; | 114 | +} |
| 115 | + | ||
| 116 | +bool CheckedVectorSpan(int64_t n, int64_t increment, uint64_t& span) | ||
| 117 | +{ | ||
| 118 | + const uint64_t absIncrement = AbsIncrement(increment); | ||
| 119 | + if (n <= 1) { | ||
| 120 | + span = 1; | ||
| 121 | + return true; | ||
| 122 | + } | ||
| 123 | + const uint64_t logicalDistance = static_cast<uint64_t>(n - 1); | ||
| 124 | + if (logicalDistance > (std::numeric_limits<uint64_t>::max() - 1) / absIncrement) | ||
| 125 | + return false; | ||
| 126 | + span = 1 + logicalDistance * absIncrement; | ||
| 127 | + return span <= std::numeric_limits<size_t>::max() / sizeof(aclblasComplex); | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +// Maps a logical element index (0..n-1) to its physical index in memory. For a | ||
| 131 | +// negative increment BLAS walks the vector backwards, so logical 0 sits at the | ||
| 132 | +// far end. | ||
| 133 | +uint64_t PhysicalIndex(uint64_t logicalIndex, uint64_t n, int64_t increment) | ||
| 134 | +{ | ||
| 135 | + const uint64_t absIncrement = AbsIncrement(increment); | ||
| 136 | + return increment > 0 ? logicalIndex * absIncrement : (n - 1 - logicalIndex) * absIncrement; | ||
| 137 | +} | ||
| 138 | + | ||
| 139 | +// The mask buffer holds precomputed gather/scatter offset tables so the device | ||
| 140 | +// kernel never has to derive strided addresses on its hot path. Each Gen*Offsets | ||
| 141 | +// routine fills one section (see the *_OFFSET_START layout constants above). | ||
| 142 | + | ||
| 143 | +// Interleaved -> planar: real parts land in the first half, imaginary in the | ||
| 144 | +// second, so downstream vector ops see two contiguous real-valued lanes. | ||
| 145 | +void GenComputeOffsets(uint32_t* offsets) | ||
| 146 | +{ | ||
| 147 | + const uint32_t complexCount = MAX_DATA_COUNT / FLOATS_PER_COMPLEX; | ||
| 89 | for (uint32_t i = 0; i < complexCount; i++) { | 148 | for (uint32_t i = 0; i < complexCount; i++) { |
| 90 | - maskData[offsetNum * i] = K_FACTOR_4 * i; | 149 | + offsets[FLOATS_PER_COMPLEX * i] = K_FACTOR_4 * i; |
| 91 | - maskData[offsetNum * i + 1] = K_FACTOR_4 * (i + complexCount); | 150 | + offsets[FLOATS_PER_COMPLEX * i + 1] = K_FACTOR_4 * (i + complexCount); |
| 92 | } | 151 | } |
| 93 | } | 152 | } |
| 94 | 153 | ||
| 95 | -aclblasStatus_t aclblasCaxpy( | 154 | +// Swaps real/imaginary byte positions within each complex element in place. |
| 96 | - aclblasHandle_t handle, int n, const aclblasComplex* alpha, const aclblasComplex* x, int incx, | 155 | +void GenDenseSwapOffsets(uint32_t* offsets) |
| 97 | - aclblasComplex* y, int incy) | ||
| 98 | { | 156 | { |
| 99 | - if (alpha == nullptr) { | 157 | + const uint32_t complexCount = MAX_DATA_COUNT / FLOATS_PER_COMPLEX; |
| 158 | + for (uint32_t i = 0; i < complexCount; ++i) { | ||
| 159 | + offsets[FLOATS_PER_COMPLEX * i] = (FLOATS_PER_COMPLEX * i + 1) * sizeof(float); | ||
| 160 | + offsets[FLOATS_PER_COMPLEX * i + 1] = FLOATS_PER_COMPLEX * i * sizeof(float); | ||
| 161 | + } | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +// Gathers strided x into the blocked (8-float-per-complex) working layout. For | ||
| 165 | +// increments where the shared pipeline's span read still fits its lowScratch | ||
| 166 | +// UB buffer (UseCaxpySharedXSpanPipeline), the span read brings the data in | ||
| 167 | +// contiguously and only the blocked layout stride remains; larger increments | ||
| 168 | +// fall back to the disjoint pipeline, which reads each element individually | ||
| 169 | +// and does not use this span-read addressing at all. | ||
| 170 | +void GenXNormalizeOffsets(uint32_t* offsets, uint32_t xIncrement) | ||
| 171 | +{ | ||
| 172 | + for (uint32_t i = 0; i < STRIDED_TILE_COMPLEX_COUNT; ++i) { | ||
| 173 | + const uint32_t sourceByteOffset = | ||
| 174 | + UseCaxpySharedXSpanPipeline(static_cast<int64_t>(xIncrement)) ? | ||
| 175 | + i * xIncrement * FLOATS_PER_COMPLEX * sizeof(float) : | ||
| 176 | + i * BLOCKED_FLOATS_PER_COMPLEX * sizeof(float); | ||
| 177 | + for (uint32_t lane = 0; lane < BLOCKED_FLOATS_PER_COMPLEX; ++lane) { | ||
| 178 | + offsets[i * BLOCKED_FLOATS_PER_COMPLEX + lane] = | ||
| 179 | + lane == 1 ? sourceByteOffset + sizeof(float) : sourceByteOffset; | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +// Scatters results from the blocked layout back to packed interleaved complex. | ||
| 185 | +void GenStoreOffsets(uint32_t* offsets) | ||
| 186 | +{ | ||
| 187 | + for (uint32_t i = 0; i < STRIDED_TILE_COMPLEX_COUNT; ++i) { | ||
| 188 | + const uint32_t sourceByteOffset = i * BLOCKED_FLOATS_PER_COMPLEX * sizeof(float); | ||
| 189 | + offsets[i * FLOATS_PER_COMPLEX] = sourceByteOffset; | ||
| 190 | + offsets[i * FLOATS_PER_COMPLEX + 1] = sourceByteOffset + sizeof(float); | ||
| 191 | + } | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +// Gathers strided y (real, imag) pairs for the accumulation step. | ||
| 195 | +void GenYGatherOffsets(uint32_t* offsets, uint32_t yIncrement) | ||
| 196 | +{ | ||
| 197 | + for (uint32_t i = 0; i < STRIDED_TILE_COMPLEX_COUNT; ++i) { | ||
| 198 | + const uint32_t sourceByteOffset = i * yIncrement * FLOATS_PER_COMPLEX * sizeof(float); | ||
| 199 | + offsets[i * FLOATS_PER_COMPLEX] = sourceByteOffset; | ||
| 200 | + offsets[i * FLOATS_PER_COMPLEX + 1] = sourceByteOffset + sizeof(float); | ||
| 201 | + } | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | +void GenMaskData(uint32_t* maskData, uint32_t xIncrement, uint32_t yIncrement) | ||
| 205 | +{ | ||
| 206 | + std::fill(maskData, maskData + MASK_DATA_COUNT, 0); | ||
| 207 | + GenComputeOffsets(maskData); | ||
| 208 | + GenXNormalizeOffsets(maskData + X_NORMALIZE_OFFSET_START, xIncrement); | ||
| 209 | + GenStoreOffsets(maskData + STORE_OFFSET_START); | ||
| 210 | + GenYGatherOffsets(maskData + Y_GATHER_OFFSET_START, yIncrement); | ||
| 211 | + GenDenseSwapOffsets(maskData + DENSE_SWAP_OFFSET_START); | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +aclblasStatus_t GetCaxpyMaskCache(aclblasHandle_t handle, uint64_t absIncx, uint64_t absIncy, uint8_t** maskDevice) | ||
| 215 | +{ | ||
| 216 | + auto* h = handle; | ||
| 217 | + if (h->caxpy_mask_cache == nullptr) { | ||
| 218 | + void* buffer = nullptr; | ||
| 219 | + const aclError aclRet = aclrtMalloc(&buffer, MASK_DATA_COUNT * sizeof(uint32_t), ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 220 | + CHECK_RET( | ||
| 221 | + aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", aclRet); | ||
| 222 | + return ACLBLAS_STATUS_ALLOC_FAILED); | ||
| 223 | + h->caxpy_mask_cache = buffer; | ||
| 224 | + } | ||
| 225 | + if (h->caxpy_mask_cache_incx != absIncx || h->caxpy_mask_cache_incy != absIncy) { | ||
| 226 | + // Kernels from earlier calls may still be reading the cached mask; drain the | ||
| 227 | + // stream before overwriting it in place. | ||
| 228 | + const aclblasStatus_t syncStatus = SynchronizeHandleStream(h); | ||
| 229 | + CHECK_RET(syncStatus == ACLBLAS_STATUS_SUCCESS, return syncStatus); | ||
| 230 | + std::vector<uint32_t> maskHost(MASK_DATA_COUNT); | ||
| 231 | + GenMaskData(maskHost.data(), static_cast<uint32_t>(absIncx), static_cast<uint32_t>(absIncy)); | ||
| 232 | + const aclError aclRet = aclrtMemcpy( | ||
| 233 | + h->caxpy_mask_cache, MASK_DATA_COUNT * sizeof(uint32_t), maskHost.data(), | ||
| 234 | + MASK_DATA_COUNT * sizeof(uint32_t), ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 235 | + CHECK_RET( | ||
| 236 | + aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", aclRet); | ||
| 237 | + return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 238 | + h->caxpy_mask_cache_incx = absIncx; | ||
| 239 | + h->caxpy_mask_cache_incy = absIncy; | ||
| 240 | + } | ||
| 241 | + *maskDevice = reinterpret_cast<uint8_t*>(h->caxpy_mask_cache); | ||
| 242 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +struct CaxpyExecution { | ||
| 246 | + int64_t n; | ||
| 247 | + int64_t incx; | ||
| 248 | + int64_t incy; | ||
| 249 | + uint32_t numBlocks; | ||
| 250 | + bool useStridedKernel; | ||
| 251 | + bool packX; | ||
| 252 | + bool packY; | ||
| 253 | + CaxpyKernelVariant kernelVariant; | ||
| 254 | + aclrtStream stream; | ||
| 255 | + uint8_t* tilingDevice; | ||
| 256 | + uint8_t* maskDevice; | ||
| 257 | + CaxpyTilingData tiling; | ||
| 258 | +}; | ||
| 259 | + | ||
| 260 | +struct CaxpyPackedBuffers { | ||
| 261 | + uint8_t* x = nullptr; | ||
| 262 | + uint8_t* y = nullptr; | ||
| 263 | +}; | ||
| 264 | + | ||
| 265 | +aclblasStatus_t ValidateCaxpyArguments( | ||
| 266 | + aclblasHandle_t handle, int64_t n, const aclblasComplex* alpha, const aclblasComplex* x, int64_t incx, | ||
| 267 | + const aclblasComplex* y, int64_t incy) | ||
| 268 | +{ | ||
| 269 | + if (handle == nullptr || alpha == nullptr || n < 0 || incx == 0 || incy == 0) | ||
Z 【逻辑】 同仓其它接口(含本批 ![]() ![]() | |||
| 270 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
Z 另返回错误前增加打印, 方便定位 ![]() ![]() | |||
| 271 | + if (incx == std::numeric_limits<int64_t>::min() || incy == std::numeric_limits<int64_t>::min()) { | ||
| 272 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
| 273 | + } | ||
| 274 | + if (n > 0 && (x == nullptr || y == nullptr)) | ||
| 275 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
| 276 | + if (n > std::numeric_limits<uint32_t>::max() / FLOATS_PER_COMPLEX) | ||
| 277 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
| 278 | + uint64_t span = 0; | ||
| 279 | + if (n > 0 && (!CheckedVectorSpan(n, incx, span) || !CheckedVectorSpan(n, incy, span))) { | ||
| 100 | return ACLBLAS_STATUS_INVALID_VALUE; | 280 | return ACLBLAS_STATUS_INVALID_VALUE; |
| 101 | } | 281 | } |
| 102 | - auto* h = handle; | ||
| 103 | - aclrtStream useStream = h->stream; | ||
| 104 | - | ||
| 105 | - uint32_t numBlocks = DEFAULT_VECTOR_NUM; | ||
| 106 | - | ||
| 107 | - float alphaReal = alpha->real; | ||
| 108 | - float alphaImag = alpha->imag; | ||
| 109 | - | ||
| 110 | - CaxpyTilingData tiling = CalTilingData(static_cast<uint32_t>(n), numBlocks, alphaReal, alphaImag); | ||
| 111 | - | ||
| 112 | - uint32_t maskSize = MAX_DATA_COUNT * sizeof(uint32_t) * COMPLEX_NUM; | ||
| 113 | - | ||
| 114 | - uint8_t* maskDevice = nullptr; | ||
| 115 | - uint8_t* tilingDevice = nullptr; | ||
| 116 | - | ||
| 117 | - uint32_t* maskHost = new uint32_t[MAX_DATA_COUNT * COMPLEX_NUM]; | ||
| 118 | - GenMaskData(maskHost); | ||
| 119 | - | ||
| 120 | - aclError aclRet = aclrtMalloc((void**)&maskDevice, maskSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 121 | - CHECK_RET( | ||
| 122 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", aclRet); delete[] maskHost; | ||
| 123 | - return ACLBLAS_STATUS_ALLOC_FAILED); | ||
| 124 | - | ||
| 125 | - aclRet = aclrtMalloc((void**)&tilingDevice, sizeof(CaxpyTilingData), ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 126 | - CHECK_RET( | ||
| 127 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", aclRet); aclrtFree(maskDevice); | ||
| 128 | - delete[] maskHost; return ACLBLAS_STATUS_ALLOC_FAILED); | ||
| 129 | - | ||
| 130 | - aclRet = aclrtMemcpy(maskDevice, maskSize, maskHost, maskSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 131 | - CHECK_RET( | ||
| 132 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", aclRet); aclrtFree(tilingDevice); | ||
| 133 | - aclrtFree(maskDevice); delete[] maskHost; return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 134 | - | ||
| 135 | - aclRet = | ||
| 136 | - aclrtMemcpy(tilingDevice, sizeof(CaxpyTilingData), &tiling, sizeof(CaxpyTilingData), ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 137 | - CHECK_RET( | ||
| 138 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", aclRet); aclrtFree(tilingDevice); | ||
| 139 | - aclrtFree(maskDevice); delete[] maskHost; return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 140 | - | ||
| 141 | - caxpy_kernel_do(reinterpret_cast<uint8_t*>(const_cast<aclblasComplex*>(x)), maskDevice, reinterpret_cast<uint8_t*>(y), nullptr, | ||
| 142 | - tilingDevice, numBlocks, useStream); | ||
| 143 | - aclRet = aclrtSynchronizeStream(useStream); | ||
| 144 | - CHECK_RET( | ||
| 145 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", aclRet); aclrtFree(tilingDevice); | ||
| 146 | - aclrtFree(maskDevice); delete[] maskHost; return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 147 | - | ||
| 148 | - aclrtFree(maskDevice); | ||
| 149 | - aclrtFree(tilingDevice); | ||
| 150 | - delete[] maskHost; | ||
| 151 | - | ||
| 152 | return ACLBLAS_STATUS_SUCCESS; | 282 | return ACLBLAS_STATUS_SUCCESS; |
| 153 | -} | 283 | +} |
| 284 | + | ||
| 285 | +aclblasStatus_t BuildCaxpyExecution( | ||
| 286 | + aclblasHandle_t handle, int64_t n, const aclblasComplex& alpha, int64_t incx, int64_t incy, | ||
| 287 | + CaxpyExecution& execution) | ||
| 288 | +{ | ||
| 289 | + uint32_t numBlocks = GetAivCoreCount(); | ||
| 290 | + CHECK_RET(numBlocks != 0, LOG_PRINT("GetAivCoreCount failed.\n"); return ACLBLAS_STATUS_EXECUTION_FAILED); | ||
| 291 | + numBlocks = std::min(numBlocks, DEFAULT_VECTOR_NUM); | ||
| 292 | + const uint64_t absIncx = AbsIncrement(incx); | ||
| 293 | + const uint64_t absIncy = AbsIncrement(incy); | ||
| 294 | + const uint64_t maxIncrement = std::max(absIncx, absIncy); | ||
| 295 | + const bool incrementFits = maxIncrement <= std::numeric_limits<uint32_t>::max() / sizeof(aclblasComplex); | ||
| 296 | + const bool addressFits = | ||
| 297 | + n <= 1 || (incrementFits && static_cast<uint64_t>(n - 1) <= | ||
| 298 | + std::numeric_limits<uint32_t>::max() / (FLOATS_PER_COMPLEX * maxIncrement)); | ||
| 299 | + const bool contiguous = incx == 1 && incy == 1; | ||
| 300 | + const bool useStridedKernel = !contiguous && incrementFits && addressFits; | ||
| 301 | + const CaxpyKernelVariant kernelVariant = | ||
| 302 | + SelectCaxpyKernelVariant(static_cast<uint64_t>(n), incx, incy, useStridedKernel, numBlocks); | ||
| 303 | + auto* h = handle; | ||
| 304 | + CHECK_RET( | ||
| 305 | + sizeof(CaxpyTilingData) <= GetEffectiveWorkspaceSize(h), | ||
| 306 | + LOG_PRINT("workspace need %zu, available %zu\n", sizeof(CaxpyTilingData), GetEffectiveWorkspaceSize(h)); | ||
| 307 | + return ACLBLAS_STATUS_EXECUTION_FAILED); | ||
| 308 | + uint8_t* maskDevice = nullptr; | ||
| 309 | + if (kernelVariant != CaxpyKernelVariant::STRIDED_SCALAR) { | ||
| 310 | + const aclblasStatus_t maskStatus = GetCaxpyMaskCache(handle, absIncx, absIncy, &maskDevice); | ||
| 311 | + CHECK_RET(maskStatus == ACLBLAS_STATUS_SUCCESS, return maskStatus); | ||
| 312 | + } | ||
| 313 | + // When a vector is strided but we are not taking the strided kernel path, | ||
| 314 | + // fall back to packing it into a contiguous scratch buffer and running the | ||
| 315 | + // dense kernel. Unit-stride vectors need no packing. | ||
| 316 | + const bool packX = !contiguous && !useStridedKernel && incx != 1; | ||
| 317 | + const bool packY = !contiguous && !useStridedKernel && incy != 1; | ||
| 318 | + execution = { | ||
| 319 | + n, | ||
| 320 | + incx, | ||
| 321 | + incy, | ||
| 322 | + numBlocks, | ||
| 323 | + useStridedKernel, | ||
| 324 | + packX, | ||
| 325 | + packY, | ||
| 326 | + kernelVariant, | ||
| 327 | + h->stream, | ||
| 328 | + reinterpret_cast<uint8_t*>(GetEffectiveWorkspace(h)), | ||
| 329 | + maskDevice, | ||
| 330 | + CalTilingData( | ||
| 331 | + static_cast<uint32_t>(n), numBlocks, alpha.real, alpha.imag, useStridedKernel ? incx : 1, | ||
| 332 | + useStridedKernel ? incy : 1)}; | ||
| 333 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +void FreePackedBuffers(CaxpyPackedBuffers& buffers) | ||
| 337 | +{ | ||
| 338 | + if (buffers.y != nullptr) | ||
| 339 | + aclrtFree(buffers.y); | ||
| 340 | + if (buffers.x != nullptr) | ||
| 341 | + aclrtFree(buffers.x); | ||
| 342 | + buffers = {}; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +aclblasStatus_t AllocatePackedBuffers(const CaxpyExecution& execution, CaxpyPackedBuffers& buffers) | ||
| 346 | +{ | ||
| 347 | + const size_t bytes = static_cast<size_t>(execution.n) * sizeof(aclblasComplex); | ||
| 348 | + if (execution.packX) { | ||
| 349 | + const aclError result = aclrtMalloc(reinterpret_cast<void**>(&buffers.x), bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 350 | + CHECK_RET( | ||
| 351 | + result == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", result); | ||
| 352 | + return ACLBLAS_STATUS_ALLOC_FAILED); | ||
| 353 | + } | ||
| 354 | + if (execution.packY) { | ||
| 355 | + const aclError result = aclrtMalloc(reinterpret_cast<void**>(&buffers.y), bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 356 | + CHECK_RET( | ||
| 357 | + result == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", result); FreePackedBuffers(buffers); | ||
| 358 | + return ACLBLAS_STATUS_ALLOC_FAILED); | ||
| 359 | + } | ||
| 360 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 361 | +} | ||
| 362 | + | ||
| 363 | +aclblasStatus_t CopyStridedVector( | ||
| 364 | + uint8_t* packed, uint8_t* strided, uint64_t n, int64_t increment, aclrtStream stream, bool unpack) | ||
| 365 | +{ | ||
| 366 | + for (uint64_t i = 0; i < n; ++i) { | ||
| 367 | + const size_t packedOffset = static_cast<size_t>(i) * sizeof(aclblasComplex); | ||
| 368 | + const size_t stridedOffset = static_cast<size_t>(PhysicalIndex(i, n, increment)) * sizeof(aclblasComplex); | ||
| 369 | + uint8_t* destination = unpack ? strided + stridedOffset : packed + packedOffset; | ||
| 370 | + uint8_t* source = unpack ? packed + packedOffset : strided + stridedOffset; | ||
| 371 | + const aclError result = aclrtMemcpyAsync( | ||
| 372 | + destination, sizeof(aclblasComplex), source, sizeof(aclblasComplex), ACL_MEMCPY_DEVICE_TO_DEVICE, stream); | ||
| 373 | + CHECK_RET( | ||
| 374 | + result == ACL_SUCCESS, LOG_PRINT("aclrtMemcpyAsync failed. ERROR: %d\n", result); | ||
| 375 | + return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 376 | + } | ||
| 377 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 378 | +} | ||
| 379 | + | ||
| 380 | +aclblasStatus_t PreparePackedInputs( | ||
| 381 | + const CaxpyExecution& execution, const aclblasComplex* x, aclblasComplex* y, CaxpyPackedBuffers& buffers) | ||
| 382 | +{ | ||
| 383 | + aclblasStatus_t status = AllocatePackedBuffers(execution, buffers); | ||
| 384 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, return status); | ||
| 385 | + if (execution.packX) { | ||
| 386 | + status = CopyStridedVector( | ||
| 387 | + buffers.x, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(x)), execution.n, execution.incx, | ||
| 388 | + execution.stream, false); | ||
| 389 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, FreePackedBuffers(buffers); return status); | ||
| 390 | + } | ||
| 391 | + if (execution.packY) { | ||
| 392 | + status = CopyStridedVector( | ||
| 393 | + buffers.y, reinterpret_cast<uint8_t*>(y), execution.n, execution.incy, execution.stream, false); | ||
| 394 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, FreePackedBuffers(buffers); return status); | ||
| 395 | + } | ||
| 396 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 397 | +} | ||
| 398 | + | ||
| 399 | +aclblasStatus_t UploadTiling(const CaxpyExecution& execution, const CaxpyTilingData& tiling) | ||
| 400 | +{ | ||
| 401 | + const aclError result = aclrtMemcpyAsync( | ||
| 402 | + execution.tilingDevice, sizeof(tiling), &tiling, sizeof(tiling), ACL_MEMCPY_HOST_TO_DEVICE, execution.stream); | ||
| 403 | + CHECK_RET( | ||
| 404 | + result == ACL_SUCCESS, LOG_PRINT("aclrtMemcpyAsync failed. ERROR: %d\n", result); | ||
| 405 | + return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 406 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 407 | +} | ||
| 408 | + | ||
| 409 | +// Builds tiling for a strided launch covering `count` complex elements across | ||
| 410 | +// `blocks` blocks. CalTilingData fills the leading word with packedFloatCount, | ||
| 411 | +// but the strided kernels expect the active block count there, so we switch the | ||
| 412 | +// union to the activeBlocks view (see CaxpyTilingData in caxpy_tiling_data.h). | ||
| 413 | +CaxpyTilingData BuildStridedTiling( | ||
| 414 | + const CaxpyExecution& execution, const aclblasComplex& alpha, uint32_t count, uint32_t blocks) | ||
| 415 | +{ | ||
| 416 | + CaxpyTilingData tiling = CalTilingData( | ||
| 417 | + count, blocks, alpha.real, alpha.imag, execution.incx, execution.incy, 0, count); | ||
| 418 | + tiling.activeBlocks = blocks; | ||
| 419 | + // Only the shared pipeline's run_shared_pipeline/shared_tile_bounds read | ||
| 420 | + // this field; the disjoint and scalar kernel entry points ignore it and | ||
| 421 | + // keep using the fixed CAXPY_STRIDED_TILE_COUNT capacity, so it is safe | ||
| 422 | + // to always populate it here for every strided variant. | ||
| 423 | + tiling.sharedTileCapacity = static_cast<uint32_t>(CaxpySharedTileCapacity(AbsIncrement(execution.incx))); | ||
| 424 | + return tiling; | ||
| 425 | +} | ||
| 426 | + | ||
| 427 | +uint64_t CaxpySegmentStorageOffset(uint64_t totalN, uint64_t segmentStart, uint64_t segmentN, int64_t increment) | ||
| 428 | +{ | ||
| 429 | + const uint64_t absIncrement = AbsIncrement(increment); | ||
| 430 | + return increment > 0 ? segmentStart * absIncrement : (totalN - segmentStart - segmentN) * absIncrement; | ||
| 431 | +} | ||
| 432 | + | ||
| 433 | +void LaunchCaxpyStridedPipelined(const CaxpyExecution& execution, uint8_t* x, uint8_t* y, uint32_t blocks) | ||
| 434 | +{ | ||
| 435 | + if (execution.kernelVariant == CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC || | ||
| 436 | + execution.kernelVariant == CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_SCALAR_TAIL) { | ||
| 437 | + caxpy_strided_shared_kernel_do( | ||
| 438 | + x, execution.maskDevice, y, nullptr, execution.tilingDevice, blocks, execution.stream); | ||
| 439 | + return; | ||
| 440 | + } | ||
| 441 | + caxpy_strided_disjoint_kernel_do( | ||
| 442 | + x, execution.maskDevice, y, nullptr, execution.tilingDevice, blocks, execution.stream); | ||
| 443 | +} | ||
| 444 | + | ||
| 445 | +aclblasStatus_t LaunchCaxpyStridedSegment( | ||
| 446 | + const CaxpyExecution& execution, const aclblasComplex& alpha, const aclblasComplex* x, aclblasComplex* y, | ||
| 447 | + uint64_t segmentStart, uint64_t segmentN, bool scalar) | ||
| 448 | +{ | ||
| 449 | + const uint32_t blocks = static_cast<uint32_t>(std::min<uint64_t>(execution.numBlocks, segmentN)); | ||
| 450 | + CaxpyTilingData tiling = BuildStridedTiling(execution, alpha, static_cast<uint32_t>(segmentN), blocks); | ||
| 451 | + const aclblasStatus_t status = UploadTiling(execution, tiling); | ||
| 452 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, return status); | ||
| 453 | + | ||
| 454 | + const uint64_t totalN = static_cast<uint64_t>(execution.n); | ||
| 455 | + const uint64_t xOffset = CaxpySegmentStorageOffset(totalN, segmentStart, segmentN, execution.incx); | ||
| 456 | + const uint64_t yOffset = CaxpySegmentStorageOffset(totalN, segmentStart, segmentN, execution.incy); | ||
| 457 | + auto* segmentX = const_cast<aclblasComplex*>(x + xOffset); | ||
| 458 | + auto* segmentY = y + yOffset; | ||
| 459 | + if (scalar) { | ||
| 460 | + caxpy_strided_scalar_kernel_do( | ||
| 461 | + reinterpret_cast<uint8_t*>(segmentX), reinterpret_cast<uint8_t*>(segmentY), execution.tilingDevice, blocks, | ||
| 462 | + execution.stream); | ||
| 463 | + } else { | ||
| 464 | + LaunchCaxpyStridedPipelined( | ||
| 465 | + execution, reinterpret_cast<uint8_t*>(segmentX), reinterpret_cast<uint8_t*>(segmentY), blocks); | ||
| 466 | + } | ||
| 467 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 468 | +} | ||
| 469 | + | ||
| 470 | +aclblasStatus_t LaunchCaxpy( | ||
| 471 | + const CaxpyExecution& execution, const aclblasComplex& alpha, const aclblasComplex* x, aclblasComplex* y, | ||
| 472 | + const CaxpyPackedBuffers& buffers) | ||
| 473 | +{ | ||
| 474 | + if (execution.useStridedKernel) { | ||
| 475 | + if (execution.kernelVariant == CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_SCALAR_TAIL || | ||
| 476 | + execution.kernelVariant == CaxpyKernelVariant::STRIDED_DISJOINT_PIPELINED_SCALAR_TAIL) { | ||
| 477 | + const uint64_t tailN = CaxpyFinalWaveCount(static_cast<uint64_t>(execution.n), execution.numBlocks); | ||
| 478 | + const uint64_t prefixN = static_cast<uint64_t>(execution.n) - tailN; | ||
| 479 | + aclblasStatus_t status = LaunchCaxpyStridedSegment(execution, alpha, x, y, 0, prefixN, false); | ||
| 480 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, return status); | ||
| 481 | + status = LaunchCaxpyStridedSegment(execution, alpha, x, y, prefixN, tailN, true); | ||
| 482 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, return status); | ||
| 483 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 484 | + } | ||
| 485 | + const uint32_t blocks = static_cast<uint32_t>(std::min<uint64_t>(execution.numBlocks, execution.n)); | ||
| 486 | + CaxpyTilingData tiling = BuildStridedTiling(execution, alpha, static_cast<uint32_t>(execution.n), blocks); | ||
| 487 | + const aclblasStatus_t status = UploadTiling(execution, tiling); | ||
| 488 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, return status); | ||
| 489 | + if (execution.kernelVariant == CaxpyKernelVariant::STRIDED_SCALAR) { | ||
| 490 | + caxpy_strided_scalar_kernel_do( | ||
| 491 | + const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(x)), reinterpret_cast<uint8_t*>(y), | ||
| 492 | + execution.tilingDevice, blocks, execution.stream); | ||
| 493 | + } else { | ||
| 494 | + LaunchCaxpyStridedPipelined( | ||
| 495 | + execution, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(x)), reinterpret_cast<uint8_t*>(y), | ||
| 496 | + blocks); | ||
| 497 | + } | ||
| 498 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 499 | + } | ||
| 500 | + const aclblasStatus_t status = UploadTiling(execution, execution.tiling); | ||
| 501 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, return status); | ||
| 502 | + caxpy_kernel_do( | ||
| 503 | + execution.packX ? buffers.x : const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(x)), execution.maskDevice, | ||
| 504 | + execution.packY ? buffers.y : reinterpret_cast<uint8_t*>(y), nullptr, execution.tilingDevice, | ||
| 505 | + execution.numBlocks, execution.stream); | ||
| 506 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 507 | +} | ||
| 508 | + | ||
| 509 | +aclblasStatus_t FinishCaxpy(const CaxpyExecution& execution, aclblasComplex* y, CaxpyPackedBuffers& buffers) | ||
| 510 | +{ | ||
| 511 | + if (execution.packY) { | ||
| 512 | + const aclblasStatus_t status = CopyStridedVector( | ||
| 513 | + buffers.y, reinterpret_cast<uint8_t*>(y), execution.n, execution.incy, execution.stream, true); | ||
| 514 | + CHECK_RET(status == ACLBLAS_STATUS_SUCCESS, FreePackedBuffers(buffers); return status); | ||
| 515 | + } | ||
| 516 | + const aclError result = aclrtSynchronizeStream(execution.stream); | ||
| 517 | + CHECK_RET( | ||
Z 【逻辑】公开接口在 调用方应通过 handle 上的 stream 自行同步。请去掉接口内同步;若打包回退路径必须等拷贝完成,也只应作用在那条回退上,不要绑死整条 Caxpy。 ![]() ![]() | |||
| 518 | + result == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", result); | ||
| 519 | + FreePackedBuffers(buffers); return ACLBLAS_STATUS_INTERNAL_ERROR); | ||
| 520 | + FreePackedBuffers(buffers); | ||
| 521 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 522 | +} | ||
| 523 | + | ||
| 524 | +aclblasStatus_t aclblasCaxpy( | ||
| 525 | + aclblasHandle_t handle, int n, const aclblasComplex* alpha, const aclblasComplex* x, int incx, aclblasComplex* y, | ||
| 526 | + int incy) | ||
| 527 | +{ | ||
| 528 | + const aclblasStatus_t argumentStatus = ValidateCaxpyArguments(handle, n, alpha, x, incx, y, incy); | ||
| 529 | + CHECK_RET(argumentStatus == ACLBLAS_STATUS_SUCCESS, return argumentStatus); | ||
| 530 | + if (n == 0) | ||
| 531 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 532 | + CaxpyExecution execution{}; | ||
| 533 | + const aclblasStatus_t buildStatus = BuildCaxpyExecution(handle, n, *alpha, incx, incy, execution); | ||
| 534 | + CHECK_RET(buildStatus == ACLBLAS_STATUS_SUCCESS, return buildStatus); | ||
| 535 | + CaxpyPackedBuffers buffers; | ||
| 536 | + const aclblasStatus_t prepareStatus = PreparePackedInputs(execution, x, y, buffers); | ||
| 537 | + CHECK_RET(prepareStatus == ACLBLAS_STATUS_SUCCESS, return prepareStatus); | ||
| 538 | + const aclblasStatus_t launchStatus = LaunchCaxpy(execution, *alpha, x, y, buffers); | ||
| 539 | + CHECK_RET(launchStatus == ACLBLAS_STATUS_SUCCESS, FreePackedBuffers(buffers); return launchStatus); | ||
| 540 | + return FinishCaxpy(execution, y, buffers); | ||
| 541 | +} | ||
| @@ -0,0 +1,64 @@ | |||
| 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 | +// Single source of truth for the CAXPY host->device tiling block. BOTH the host | ||
| 17 | +// (caxpy_host.cpp) and the device kernel (caxpy_kernel.cpp) include this header | ||
| 18 | +// and access fields by name. Do not re-declare this layout anywhere else and do | ||
| 19 | +// not read it via hard-coded byte offsets -- that is exactly the drift hazard | ||
| 20 | +// this shared definition exists to remove. | ||
| 21 | + | ||
| 22 | +constexpr uint32_t CAXPY_MAX_VECTOR_CORES = 40; | ||
| 23 | + | ||
| 24 | +struct CaxpyTilingData { | ||
| 25 | + // Leading word is variant-dependent, so it is exposed through two named | ||
| 26 | + // views over the same storage instead of a single misleadingly-named field: | ||
| 27 | + // - packedFloatCount: dense kernel; number of packed floats to process | ||
| 28 | + // (complex count * 2). | ||
| 29 | + // - activeBlocks: strided kernels; number of blocks that actually run. | ||
| 30 | + // The device kernel selects the view matching the entry point it was | ||
| 31 | + // launched through. Both are uint32_t at offset 0, so the on-wire layout is | ||
| 32 | + // identical to a plain leading uint32_t -- existing binaries are unaffected. | ||
| 33 | + union { | ||
| 34 | + uint32_t packedFloatCount; | ||
| 35 | + uint32_t activeBlocks; | ||
| 36 | + }; | ||
| 37 | + float alphaReal; | ||
| 38 | + float alphaImag; | ||
| 39 | + uint32_t startOffset[CAXPY_MAX_VECTOR_CORES]; // per-block start, in floats | ||
| 40 | + uint32_t calNum[CAXPY_MAX_VECTOR_CORES]; // per-block element count, in floats | ||
| 41 | + uint32_t totalN; | ||
| 42 | + int64_t incx; | ||
| 43 | + int64_t incy; | ||
| 44 | + // Shared-pipeline span-read tile capacity, in complex elements. Computed by | ||
| 45 | + // the host from incx so that the span read | ||
| 46 | + // ((sharedTileCapacity-1)*|incx|+1) complex elements always fits in the | ||
| 47 | + // 38 KiB lowScratch UB buffer -- see CaxpySharedTileCapacity in | ||
| 48 | + // caxpy_dispatch_policy.h for the formula. Unused (left at its default of | ||
| 49 | + // CAXPY_STRIDED_TILE_COUNT) by the disjoint and scalar kernel variants, | ||
| 50 | + // which never span-read. | ||
| 51 | + uint32_t sharedTileCapacity; | ||
| 52 | +}; | ||
| 53 | + | ||
| 54 | +// The device kernel historically read these fields via literal byte offsets. | ||
| 55 | +// Pin them so any accidental layout change is caught at compile time on both | ||
| 56 | +// sides, and so the values remain greppable next to the struct. | ||
| 57 | +static_assert(offsetof(CaxpyTilingData, alphaReal) == 4, "CAXPY tiling layout changed"); | ||
| 58 | +static_assert(offsetof(CaxpyTilingData, alphaImag) == 8, "CAXPY tiling layout changed"); | ||
| 59 | +static_assert(offsetof(CaxpyTilingData, startOffset) == 12, "CAXPY tiling layout changed"); | ||
| 60 | +static_assert(offsetof(CaxpyTilingData, calNum) == 172, "CAXPY tiling layout changed"); | ||
| 61 | +static_assert(offsetof(CaxpyTilingData, totalN) == 332, "CAXPY tiling layout changed"); | ||
| 62 | +static_assert(offsetof(CaxpyTilingData, incx) == 336, "CAXPY tiling layout changed"); | ||
| 63 | +static_assert(offsetof(CaxpyTilingData, incy) == 344, "CAXPY tiling layout changed"); | ||
| 64 | +static_assert(offsetof(CaxpyTilingData, sharedTileCapacity) == 352, "CAXPY tiling layout changed"); | ||
| @@ -64,6 +64,13 @@ aclblasStatus_t aclblasDestroy(aclblasHandle_t handle) | |||
| 64 | 64 | ||
| 65 | FreeLibraryWorkspace(h); | 65 | FreeLibraryWorkspace(h); |
| 66 | 66 | ||
| 67 | + if (h->caxpy_mask_cache != nullptr) { | ||
| 68 | + aclrtFree(h->caxpy_mask_cache); | ||
| 69 | + h->caxpy_mask_cache = nullptr; | ||
| 70 | + h->caxpy_mask_cache_incx = 0; | ||
| 71 | + h->caxpy_mask_cache_incy = 0; | ||
| 72 | + } | ||
| 73 | + | ||
| 67 | h->stream = nullptr; | 74 | h->stream = nullptr; |
| 68 | 75 | ||
| 69 | delete h; | 76 | delete h; |
| @@ -55,6 +55,15 @@ struct _aclblas_handle { | |||
| 55 | 55 | ||
| 56 | /** Size of the last library-managed workspace buffer (preserved across user switches). */ | 56 | /** Size of the last library-managed workspace buffer (preserved across user switches). */ |
| 57 | size_t library_workspace_size = 0; | 57 | size_t library_workspace_size = 0; |
| 58 | + | ||
| 59 | + /* ========== Cached caxpy gather-offset buffer ========== */ | ||
| 60 | + /** Device buffer holding the caxpy gather-offset table; owned by the handle and | ||
| 61 | + * built once per |incx| instead of being re-allocated and re-uploaded per call. */ | ||
| 62 | + void* caxpy_mask_cache = nullptr; | ||
| 63 | + /** The |incx| value the cached mask was built for; 0 means the cache content is invalid. */ | ||
| 64 | + uint64_t caxpy_mask_cache_incx = 0; | ||
| 65 | + /** The |incy| value the cached mask was built for; 0 means the cache content is invalid. */ | ||
| 66 | + uint64_t caxpy_mask_cache_incy = 0; | ||
| 58 | }; | 67 | }; |
| 59 | 68 | ||
| 60 | /** | 69 | /** |
| @@ -8,4 +8,14 @@ | |||
| 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 | 10 | ||
| 11 | -ops_blas_add_tests(${OPS_BLAS}) | 11 | +ops_blas_add_gtest_tests( |
| 12 | + ${OPS_BLAS} | ||
| 13 | + caxpy_test) | ||
| 14 | + | ||
| 15 | +ops_blas_add_tests( | ||
| 16 | + ${OPS_BLAS} | ||
| 17 | + caxpy_dispatch_policy_test) | ||
| 18 | + | ||
| 19 | +target_include_directories( | ||
| 20 | + caxpy_dispatch_policy_test PRIVATE | ||
| 21 | + ${CMAKE_SOURCE_DIR}/blas/axpy/arch22) | ||
| @@ -0,0 +1,121 @@ | |||
| 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 | +namespace { | ||
| 18 | +struct ClassificationCase { | ||
| 19 | + int64_t incx; | ||
| 20 | + int64_t incy; | ||
| 21 | + CaxpyStrideClass expected; | ||
| 22 | +}; | ||
| 23 | + | ||
| 24 | +struct PolicyCase { | ||
| 25 | + uint64_t n; | ||
| 26 | + int64_t incx; | ||
| 27 | + int64_t incy; | ||
| 28 | + bool directStrided; | ||
| 29 | + uint32_t numBlocks; | ||
| 30 | + CaxpyKernelVariant expected; | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +constexpr std::array<ClassificationCase, 15> CASES{{ | ||
| 35 | + {1, 1, CaxpyStrideClass::DENSE}, | ||
| 36 | + {2, 1, CaxpyStrideClass::X_ONLY_POSITIVE}, | ||
| 37 | + {9, 1, CaxpyStrideClass::X_ONLY_POSITIVE}, | ||
| 38 | + {1, 2, CaxpyStrideClass::Y_ONLY_POSITIVE}, | ||
| 39 | + {1, 10, CaxpyStrideClass::Y_ONLY_POSITIVE}, | ||
| 40 | + {2, 2, CaxpyStrideClass::DUAL_SYMMETRIC_POSITIVE}, | ||
| 41 | + {10, 10, CaxpyStrideClass::DUAL_SYMMETRIC_POSITIVE}, | ||
| 42 | + {2, 3, CaxpyStrideClass::DUAL_ASYMMETRIC_POSITIVE}, | ||
| 43 | + {10, 11, CaxpyStrideClass::DUAL_ASYMMETRIC_POSITIVE}, | ||
| 44 | + {-1, 1, CaxpyStrideClass::NEGATIVE_MIXED}, | ||
| 45 | + {1, -1, CaxpyStrideClass::NEGATIVE_MIXED}, | ||
| 46 | + {-2, 3, CaxpyStrideClass::NEGATIVE_MIXED}, | ||
| 47 | + {5, -3, CaxpyStrideClass::NEGATIVE_MIXED}, | ||
| 48 | + {-2, -3, CaxpyStrideClass::NEGATIVE_MIXED}, | ||
| 49 | + {-10, -10, CaxpyStrideClass::NEGATIVE_MIXED}, | ||
| 50 | +}}; | ||
| 51 | + | ||
| 52 | +constexpr std::array<PolicyCase, 23> POLICY_CASES{{ | ||
| 53 | + {1, 1, 1, false, 1, CaxpyKernelVariant::DENSE_PIPELINED_ATOMIC}, | ||
| 54 | + {8192, 1, 1, false, 40, CaxpyKernelVariant::DENSE_PIPELINED_ATOMIC}, | ||
| 55 | + {640, 2, 1, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 56 | + {640, 1, 3, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 57 | + {640, 5, 5, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 58 | + {640, 2, 3, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 59 | + {640, -2, 3, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 60 | + {641, 5, 1, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 61 | + {679, 5, 1, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 62 | + {17, 65537, -32769, true, 40, CaxpyKernelVariant::STRIDED_SCALAR}, | ||
| 63 | + {680, 9, 1, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 64 | + {680, 1, 9, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 65 | + {680, 5, 5, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 66 | + {680, 2, 3, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 67 | + {680, -2, -3, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 68 | + // incx=10 now stays on the shared span-read pipeline: it exceeds the old | ||
| 69 | + // fixed CAXPY_SHARED_X_SPAN_MAX_INCREMENT=9 cutoff but is well within | ||
| 70 | + // both the tile-capacity correctness floor and the measured performance | ||
| 71 | + // cap (CAXPY_SHARED_SPAN_PERFORMANCE_MAX_INCREMENT=60). This case used to | ||
| 72 | + // assert STRIDED_DISJOINT_PIPELINED_ATOMIC under the old fixed threshold; | ||
| 73 | + // that expectation is now wrong on purpose -- see caxpy_dispatch_policy.h | ||
| 74 | + // for the crossover measurement this depends on. | ||
| 75 | + {680, 10, 1, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 76 | + // incx=61 exceeds the performance cap and correctly falls back to | ||
| 77 | + // disjoint even though its shrunk tile (capacity 80) would still be | ||
| 78 | + // usable by the correctness floor alone. | ||
| 79 | + {680, 61, 1, true, 40, CaxpyKernelVariant::STRIDED_DISJOINT_PIPELINED_ATOMIC}, | ||
| 80 | + // incx=304 shrinks the tile capacity to 32, below | ||
| 81 | + // CAXPY_SHARED_MIN_USABLE_TILE_CAPACITY (33) -- the correctness floor, | ||
| 82 | + // not the performance cap, is what forces disjoint here. Kept as | ||
| 83 | + // defense-in-depth coverage of that floor in case the performance cap is | ||
| 84 | + // ever raised past this incx. | ||
| 85 | + {680, 304, 1, true, 40, CaxpyKernelVariant::STRIDED_DISJOINT_PIPELINED_ATOMIC}, | ||
| 86 | + {327680, 2, 3, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 87 | + {327681, 2, 3, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_SCALAR_TAIL}, | ||
| 88 | + {328359, -2, 3, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_SCALAR_TAIL}, | ||
| 89 | + {328360, 2, -3, true, 40, CaxpyKernelVariant::STRIDED_SHARED_PIPELINED_ATOMIC}, | ||
| 90 | + {640, 5, 5, false, 40, CaxpyKernelVariant::DENSE_PIPELINED_ATOMIC}, | ||
| 91 | +}}; | ||
| 92 | + | ||
| 93 | + | ||
| 94 | +} // namespace | ||
| 95 | + | ||
| 96 | +int main() | ||
| 97 | +{ | ||
| 98 | + bool passed = true; | ||
| 99 | + for (const ClassificationCase& test : CASES) { | ||
| 100 | + const CaxpyStrideClass actual = ClassifyCaxpyStride(test.incx, test.incy); | ||
| 101 | + if (actual == test.expected) | ||
| 102 | + continue; | ||
| 103 | + std::cerr << "classification mismatch: incx=" << test.incx << " incy=" << test.incy | ||
| 104 | + << " expected=" << static_cast<uint32_t>(test.expected) | ||
| 105 | + << " actual=" << static_cast<uint32_t>(actual) << '\n'; | ||
| 106 | + passed = false; | ||
| 107 | + } | ||
| 108 | + for (const PolicyCase& test : POLICY_CASES) { | ||
| 109 | + const CaxpyKernelVariant actual = | ||
| 110 | + SelectCaxpyKernelVariant(test.n, test.incx, test.incy, test.directStrided, test.numBlocks); | ||
| 111 | + if (actual == test.expected) | ||
| 112 | + continue; | ||
| 113 | + std::cerr << "policy mismatch: n=" << test.n << " incx=" << test.incx << " incy=" << test.incy | ||
| 114 | + << " direct_strided=" << test.directStrided | ||
| 115 | + << " expected=" << static_cast<uint32_t>(test.expected) | ||
| 116 | + << " actual=" << static_cast<uint32_t>(actual) << '\n'; | ||
| 117 | + passed = false; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + return passed ? 0 : 1; | ||
| 121 | +} | ||
| @@ -0,0 +1,93 @@ | |||
| 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 | +static inline bool CaxpyNeedPassThrough(aclblasHandle_t handle, int n) | ||
| 21 | +{ | ||
| 22 | + return handle == nullptr || n <= 0; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +static inline aclError CaxpyAllocCopyH2D(void*& dPtr, const void* hPtr, size_t bytes) | ||
| 26 | +{ | ||
| 27 | + dPtr = nullptr; | ||
| 28 | + if (hPtr == nullptr || bytes == 0) return ACL_SUCCESS; | ||
| 29 | + aclError ret = aclrtMalloc(&dPtr, bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 30 | + if (ret != ACL_SUCCESS) return ret; | ||
| 31 | + ret = aclrtMemcpy(dPtr, bytes, hPtr, bytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 32 | + if (ret != ACL_SUCCESS) { | ||
| 33 | + aclrtFree(dPtr); | ||
| 34 | + dPtr = nullptr; | ||
| 35 | + } | ||
| 36 | + return ret; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +static inline void CaxpyFreeAll(void* dX, void* dY) | ||
| 40 | +{ | ||
| 41 | + if (dX) aclrtFree(dX); | ||
Z 【编码规范】 本仓 ![]() ![]() | |||
| 42 | + if (dY) aclrtFree(dY); | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +// Physical storage span (in complex elements) covered by a length-n vector | ||
| 46 | +// with the given (possibly negative) increment. | ||
| 47 | +static inline size_t CaxpySpanElements(int n, int increment) | ||
| 48 | +{ | ||
| 49 | + if (n <= 0) return 0; | ||
| 50 | + const int64_t absIncrement = increment >= 0 ? static_cast<int64_t>(increment) : -static_cast<int64_t>(increment); | ||
| 51 | + return static_cast<size_t>((static_cast<int64_t>(n) - 1) * absIncrement + 1); | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +inline aclblasStatus_t aclblasCaxpy_npu( | ||
| 55 | + aclblasHandle_t handle, int n, const aclblasComplex* alpha, const aclblasComplex* x, int incx, | ||
| 56 | + aclblasComplex* y, int incy) | ||
| 57 | +{ | ||
| 58 | + if (CaxpyNeedPassThrough(handle, n)) { | ||
| 59 | + return aclblasCaxpy(handle, n, alpha, x, incx, y, incy); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + const size_t xBytes = CaxpySpanElements(n, incx) * sizeof(aclblasComplex); | ||
| 63 | + const size_t yBytes = CaxpySpanElements(n, incy) * sizeof(aclblasComplex); | ||
| 64 | + | ||
| 65 | + void* dX = nullptr; | ||
| 66 | + void* dY = nullptr; | ||
| 67 | + | ||
| 68 | + if (CaxpyAllocCopyH2D(dX, x, xBytes) != ACL_SUCCESS) { | ||
| 69 | + return ACLBLAS_STATUS_ALLOC_FAILED; | ||
| 70 | + } | ||
| 71 | + if (CaxpyAllocCopyH2D(dY, y, yBytes) != ACL_SUCCESS) { | ||
| 72 | + CaxpyFreeAll(dX, dY); | ||
| 73 | + return ACLBLAS_STATUS_ALLOC_FAILED; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + aclblasStatus_t ret = aclblasCaxpy( | ||
| 77 | + handle, n, alpha, static_cast<const aclblasComplex*>(dX), incx, static_cast<aclblasComplex*>(dY), incy); | ||
| 78 | + | ||
| 79 | + if (aclrtSynchronizeDevice() != ACL_SUCCESS) { | ||
| 80 | + CaxpyFreeAll(dX, dY); | ||
| 81 | + return ACLBLAS_STATUS_INTERNAL_ERROR; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + if (ret == ACLBLAS_STATUS_SUCCESS && y != nullptr && dY != nullptr) { | ||
| 85 | + if (aclrtMemcpy(y, yBytes, dY, yBytes, ACL_MEMCPY_DEVICE_TO_HOST) != ACL_SUCCESS) { | ||
| 86 | + CaxpyFreeAll(dX, dY); | ||
| 87 | + return ACLBLAS_STATUS_INTERNAL_ERROR; | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + CaxpyFreeAll(dX, dY); | ||
| 92 | + return ret; | ||
| 93 | +} | ||
| @@ -8,158 +8,111 @@ | |||
| 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 | 10 | ||
| 11 | -/* ! | ||
| 12 | - * \file caxpy_test.cpp | ||
| 13 | - * \brief Test for aclblasCaxpy interface | ||
| 14 | - */ | ||
| 15 | - | ||
| 16 | - | ||
| 17 | - | ||
| 18 | 11 | ||
| 19 | - | ||
| 20 | - | ||
| 21 | - | ||
| 22 | - | ||
| 23 | - | ||
| 24 | 12 | ||
| 25 | -#define CHECK_RET(cond, return_expr) \ | 13 | +#include "verify.h" |
| 26 | - do { \ | 14 | +#include "blas_test.h" |
| 27 | - if (!(cond)) { \ | 15 | +#include "csv_loader.h" |
| 28 | - return_expr; \ | 16 | +#include "fill.h" |
| 29 | - } \ | 17 | +#include "caxpy_param.h" |
| 30 | - } while (0) | 18 | +#include "caxpy_golden.h" |
| 19 | + | ||
| 31 | 20 | ||
| 32 | -#define LOG_PRINT(message, ...) \ | 21 | +namespace { |
| 33 | - do { \ | ||
| 34 | - printf(message, ##__VA_ARGS__); \ | ||
| 35 | - } while (0) | ||
| 36 | 22 | ||
| 37 | -uint32_t VerifyCaxpyResult(std::vector<aclblasComplex>& output, std::vector<aclblasComplex>& golden) | 23 | +constexpr aclblasComplex GUARD_VALUE{12345.25F, -54321.5F}; |
| 24 | +constexpr size_t GUARD_COUNT = 64; | ||
| 25 | + | ||
| 26 | +// Deterministic per-index complex value generator: independent of BlasFillMode | ||
| 27 | +// so cross-checking against the reference cblas-style AXPY is exact, matching | ||
| 28 | +// the layout the legacy hardcoded-array caxpy_test used before the CSV | ||
| 29 | +// migration. | ||
| 30 | +aclblasComplex MakeStridedValue(size_t index, int32_t salt) | ||
| 38 | { | 31 | { |
| 39 | - auto printTensor = [](std::vector<aclblasComplex>& tensor, const char* name) { | 32 | + const float real = 0.125F * static_cast<float>(static_cast<int32_t>(index % 29) - 14 + salt); |
| 40 | - constexpr size_t maxPrintSize = 10; | 33 | + const float imag = 0.0625F * static_cast<float>(static_cast<int32_t>(index % 23) - 11 - salt); |
| 41 | - std::cout << name << ": "; | 34 | + return {real, imag}; |
| 42 | - for (size_t i = 0; i < std::min(tensor.size(), maxPrintSize); i++) { | ||
| 43 | - std::cout << "(" << tensor[i].real << "," << tensor[i].imag << ") "; | ||
| 44 | - } | ||
| 45 | - if (tensor.size() > maxPrintSize) { | ||
| 46 | - std::cout << "..."; | ||
| 47 | - } | ||
| 48 | - std::cout << std::endl; | ||
| 49 | - }; | ||
| 50 | - printTensor(output, "Output"); | ||
| 51 | - printTensor(golden, "Golden"); | ||
| 52 | - constexpr float EPSILON = 1e-3f; | ||
| 53 | - for (size_t i = 0; i < output.size(); i++) { | ||
| 54 | - float diff = blasComplexAbs(output[i] - golden[i]); | ||
| 55 | - if (diff > EPSILON) { | ||
| 56 | - std::cout << "[Failed] Caxpy Index " << i << ": output=(" << output[i].real << "," << output[i].imag | ||
| 57 | - << ") golden=(" << golden[i].real << "," << golden[i].imag << ") diff=" << diff << std::endl; | ||
| 58 | - return 1; | ||
| 59 | - } | ||
| 60 | - } | ||
| 61 | - std::cout << "[Success] Caxpy accuracy verification passed." << std::endl; | ||
| 62 | - return 0; | ||
| 63 | } | 35 | } |
| 64 | 36 | ||
| 65 | -int32_t TestCaxpy(aclblasHandle handle, aclrtStream stream) | 37 | +// Build a length-n complex vector (logical, stride-independent index order) |
| 38 | +// with GUARD_COUNT sentinel padding on both sides so any out-of-bounds | ||
| 39 | +// read/write by the operator under test corrupts a detectable guard region. | ||
| 40 | +std::vector<aclblasComplex> MakeGuardedVector(int n, int increment, int32_t salt) | ||
| 66 | { | 41 | { |
| 67 | - constexpr uint32_t totalLength = 8 * 2048; | 42 | + const size_t span = CaxpySpanElements(n, increment); |
| 68 | - constexpr aclblasComplex valueX{1.0f, 0.5f}; | 43 | + std::vector<aclblasComplex> data(GUARD_COUNT + span + GUARD_COUNT, GUARD_VALUE); |
| 69 | - constexpr aclblasComplex valueY{2.0f, 1.0f}; | 44 | + for (size_t i = 0; i < span; ++i) |
| 70 | - constexpr aclblasComplex alpha{2.0f, 1.0f}; | 45 | + data[GUARD_COUNT + i] = MakeStridedValue(i, salt); |
| 71 | - std::vector<aclblasComplex> x(totalLength, valueX); | 46 | + return data; |
| 72 | - std::vector<aclblasComplex> y(totalLength, valueY); | ||
| 73 | - int incx = 1; | ||
| 74 | - int incy = 1; | ||
| 75 | - | ||
| 76 | - aclblasComplex* xDevice = nullptr; | ||
| 77 | - aclblasComplex* yDevice = nullptr; | ||
| 78 | - size_t xByteSize = totalLength * sizeof(aclblasComplex); | ||
| 79 | - size_t yByteSize = totalLength * sizeof(aclblasComplex); | ||
| 80 | - aclError aclRet = aclrtMalloc((void**)&xDevice, xByteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 81 | - CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc xDevice failed. ERROR: %d\n", aclRet); return aclRet); | ||
| 82 | - aclRet = aclrtMalloc((void**)&yDevice, yByteSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 83 | - CHECK_RET( | ||
| 84 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMalloc yDevice failed. ERROR: %d\n", aclRet); aclrtFree(xDevice); | ||
| 85 | - return aclRet); | ||
| 86 | - aclRet = aclrtMemcpy(xDevice, xByteSize, x.data(), xByteSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 87 | - CHECK_RET( | ||
| 88 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy xDevice failed. ERROR: %d\n", aclRet); aclrtFree(xDevice); | ||
| 89 | - aclrtFree(yDevice); return aclRet); | ||
| 90 | - aclRet = aclrtMemcpy(yDevice, yByteSize, y.data(), yByteSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 91 | - CHECK_RET( | ||
| 92 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy yDevice failed. ERROR: %d\n", aclRet); aclrtFree(xDevice); | ||
| 93 | - aclrtFree(yDevice); return aclRet); | ||
| 94 | - | ||
| 95 | - std::cout << "========== Testing aclblasCaxpy ==========" << std::endl; | ||
| 96 | - std::cout << "Formula: y = alpha * x + y" << std::endl; | ||
| 97 | - std::cout << "alpha = (" << alpha.real << ", " << alpha.imag << ")" << std::endl; | ||
| 98 | - std::cout << "x = (" << valueX.real << ", " << valueX.imag << ") * " << totalLength << std::endl; | ||
| 99 | - std::cout << "y = (" << valueY.real << ", " << valueY.imag << ") * " << totalLength << std::endl; | ||
| 100 | - | ||
| 101 | - auto ret = aclblasCaxpy(handle, totalLength, &alpha, xDevice, incx, yDevice, incy); | ||
| 102 | - CHECK_RET( | ||
| 103 | - ret == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasCaxpy failed. ERROR: %d\n", ret); aclrtFree(xDevice); | ||
| 104 | - aclrtFree(yDevice); return ret); | ||
| 105 | - | ||
| 106 | - aclRet = aclrtSynchronizeStream(stream); | ||
| 107 | - CHECK_RET( | ||
| 108 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", aclRet); aclrtFree(xDevice); | ||
| 109 | - aclrtFree(yDevice); return aclRet); | ||
| 110 | - aclRet = aclrtMemcpy(y.data(), yByteSize, yDevice, yByteSize, ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 111 | - CHECK_RET( | ||
| 112 | - aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy y failed. ERROR: %d\n", aclRet); aclrtFree(xDevice); | ||
| 113 | - aclrtFree(yDevice); return aclRet); | ||
| 114 | - aclrtFree(xDevice); | ||
| 115 | - aclrtFree(yDevice); | ||
| 116 | - | ||
| 117 | - std::vector<aclblasComplex> golden(totalLength); | ||
| 118 | - for (size_t i = 0; i < totalLength; i++) { | ||
| 119 | - golden[i] = alpha * x[i] + valueY; | ||
| 120 | - } | ||
| 121 | - | ||
| 122 | - return VerifyCaxpyResult(y, golden); | ||
| 123 | } | 47 | } |
| 124 | 48 | ||
| 125 | -int32_t main(int32_t argc, char* argv[]) | 49 | +} // namespace |
| 126 | -{ | ||
| 127 | - int32_t deviceId = 0; | ||
| 128 | 50 | ||
| 129 | - aclInit(nullptr); | 51 | +// ── Test fixture ────────────────────────────────────────────────────────────── |
| 130 | - aclrtSetDevice(deviceId); | 52 | +class CaxpyArch22Test : public BlasTest<CaxpyParam> { }; |
| 131 | 53 | ||
| 132 | - aclblasHandle_t handle = nullptr; | 54 | +// ── TEST_F: null handle / null alpha (not expressible via CSV) ─────────────── |
| 133 | - auto ret = aclblasCreate(&handle); | 55 | +// aclblasCaxpy's ValidateCaxpyArguments treats handle==nullptr and |
| 134 | - CHECK_RET(ret == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasCreate failed. ERROR: %d\n", ret); return ret); | 56 | +// alpha==nullptr identically: ACLBLAS_STATUS_INVALID_VALUE (unlike some other |
| 57 | +// operators that return ACLBLAS_STATUS_HANDLE_IS_NULLPTR for a null handle). | ||
| 58 | +TEST_F(CaxpyArch22Test, NullHandle) { | ||
| 59 | + aclblasComplex alpha{1.0F, 0.0F}; | ||
| 60 | + aclblasStatus_t ret = aclblasCaxpy_npu(nullptr, 1, &alpha, nullptr, 1, nullptr, 1); | ||
| 61 | + EXPECT_EQ(static_cast<int>(ret), static_cast<int>(ACLBLAS_STATUS_INVALID_VALUE)); | ||
| 62 | +} | ||
| 135 | 63 | ||
| 136 | - aclrtStream stream = nullptr; | 64 | +TEST_F(CaxpyArch22Test, NullAlpha) { |
| 137 | - aclrtCreateStream(&stream); | 65 | + aclblasComplex x{1.0F, 0.0F}; |
| 138 | - ret = aclblasSetStream(handle, stream); | 66 | + aclblasComplex y{1.0F, 0.0F}; |
| 139 | - CHECK_RET(ret == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasSetStream failed. ERROR: %d\n", ret); return ret); | 67 | + aclblasStatus_t ret = aclblasCaxpy_npu(CaxpyArch22Test::handle_, 1, nullptr, &x, 1, &y, 1); |
| 68 | + EXPECT_EQ(static_cast<int>(ret), static_cast<int>(ACLBLAS_STATUS_INVALID_VALUE)); | ||
| 69 | +} | ||
| 140 | 70 | ||
| 141 | - int32_t result = 0; | 71 | +// ── CSV parameterised test suite ───────────────────────────────────────────── |
| 72 | +INSTANTIATE_TEST_SUITE_P( | ||
| 73 | + Caxpy, CaxpyArch22Test, | ||
| 74 | + ::testing::ValuesIn(GetCasesFromCsv<CaxpyParam>(ReplaceFileExtension2Csv(__FILE__))), | ||
| 75 | + PrintCaseInfoString<CaxpyParam>); | ||
| 142 | 76 | ||
| 143 | - result = TestCaxpy(handle, stream); | 77 | +// ── TEST_P: 5-step CSV-driven flow ─────────────────────────────────────────── |
| 144 | - if (result != 0) { | 78 | +TEST_P(CaxpyArch22Test, CsvDriven) { |
| 145 | - std::cout << "[FAIL] Caxpy test failed" << std::endl; | 79 | + const auto& p = GetParam(); |
| 146 | - aclblasDestroy(handle); | 80 | + const aclblasComplex alpha{p.alphaReal, p.alphaImag}; |
| 147 | - aclrtDestroyStream(stream); | ||
| 148 | - aclrtResetDevice(deviceId); | ||
| 149 | - aclFinalize(); | ||
| 150 | - return result; | ||
| 151 | - } | ||
| 152 | - std::cout << "[PASS] Caxpy test passed" << std::endl; | ||
| 153 | 81 | ||
| 154 | - aclblasDestroy(handle); | 82 | + // Step 1: Generate host data (guarded x/y storage, deterministic values) |
| 155 | - aclrtDestroyStream(stream); | 83 | + std::vector<aclblasComplex> xHost; |
| 156 | - aclrtResetDevice(deviceId); | 84 | + if (p.nullx == 0 && p.n > 0) |
| 157 | - aclFinalize(); | 85 | + xHost = MakeGuardedVector(p.n, p.incx, 3); |
| 86 | + std::vector<aclblasComplex> yHost; | ||
| 87 | + if (p.nully == 0 && p.n > 0) | ||
| 88 | + yHost = MakeGuardedVector(p.n, p.incy, -5); | ||
| 158 | 89 | ||
| 159 | - std::cout << "========================================" << std::endl; | 90 | + const aclblasComplex* xPtr = xHost.empty() ? nullptr : xHost.data() + GUARD_COUNT; |
| 160 | - std::cout << "Test Summary:" << std::endl; | 91 | + aclblasComplex* yPtr = yHost.empty() ? nullptr : yHost.data() + GUARD_COUNT; |
| 161 | - std::cout << " Passed: 1 - Caxpy" << std::endl; | ||
| 162 | - std::cout << "========================================" << std::endl; | ||
| 163 | 92 | ||
| 164 | - return 0; | 93 | + // Step 2: Execute on NPU (wrapper handles device memory, nullptr passthrough) |
| 165 | -} | 94 | + aclblasStatus_t ret = aclblasCaxpy_npu(CaxpyArch22Test::handle_, p.n, &alpha, xPtr, p.incx, yPtr, p.incy); |
| 95 | + | ||
| 96 | + // Step 3: Verify expected return code | ||
| 97 | + EXPECT_EQ(static_cast<int>(ret), static_cast<int>(p.expectResult)); | ||
| 98 | + if (p.expectResult != ACLBLAS_STATUS_SUCCESS) return; | ||
| 99 | + if (p.n == 0) return; | ||
| 100 | + | ||
| 101 | + // Step 4: Compute golden on CPU over the same guarded storage, so any | ||
| 102 | + // stray write into the guard region is caught by the full-buffer compare. | ||
| 103 | + std::vector<aclblasComplex> xGolden = p.nullx == 0 ? MakeGuardedVector(p.n, p.incx, 3) : std::vector<aclblasComplex>{}; | ||
| 104 | + std::vector<aclblasComplex> yGolden = p.nully == 0 ? MakeGuardedVector(p.n, p.incy, -5) : std::vector<aclblasComplex>{}; | ||
| 105 | + const aclblasComplex* xGoldenPtr = xGolden.empty() ? nullptr : xGolden.data() + GUARD_COUNT; | ||
| 106 | + aclblasComplex* yGoldenPtr = yGolden.empty() ? nullptr : yGolden.data() + GUARD_COUNT; | ||
| 107 | + aclblasStatus_t cpuRet = aclblasCaxpy_cpu(CaxpyArch22Test::handle_, p.n, &alpha, xGoldenPtr, p.incx, yGoldenPtr, p.incy); | ||
| 108 | + EXPECT_EQ(static_cast<int>(cpuRet), static_cast<int>(ACLBLAS_STATUS_SUCCESS)); | ||
| 109 | + | ||
| 110 | + // Step 5: Precision verification, real/imag components split (MERE/MARE). | ||
| 111 | + VerifyConfig cfg; | ||
| 112 | + cfg.mode = PrecisionMode::MERE_MARE; | ||
| 113 | + cfg.mereThreshold = p.mereThreshold; | ||
| 114 | + cfg.mareMultiplier = p.mareMultiplier; | ||
| 115 | + | ||
| 116 | + EXPECT_TRUE(Verifier::verifyMereMareComplexFloat( | ||
| 117 | + yHost.data(), yGolden.data(), yHost.size(), cfg.mereThreshold, cfg.mareMultiplier, 0.0, p.caseName)); | ||
| 118 | +} | ||
| @@ -0,0 +1,92 @@ | |||
| 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 the License for the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, 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 | +# Migrated from the legacy hardcoded CAXPY_CASES array (see git history) to | ||
| 11 | +# the repository's CSV-driven test convention. n/incx/incy/alpha match the | ||
| 12 | +# original cases one-to-one; mere_threshold/mare_multiplier use the FP32 | ||
| 13 | +# MERE/MARE defaults from getMereThreshold(ACL_FLOAT) (2^-13) x10. | ||
| 14 | +case_name,description,n,incx,incy,alpha_real,alpha_imag,nullx,nully,expect_result,mere_threshold,mare_multiplier,random_seed | ||
| 15 | +TC_zero,zero,0,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 16 | +TC_one,one,1,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 17 | +TC_lane_2,lane 2,2,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 18 | +TC_lane_3,lane 3,3,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 19 | +TC_lane_31,lane 31,31,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 20 | +TC_lane_32,lane 32,32,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 21 | +TC_lane_33,lane 33,33,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 22 | +TC_lane_63,lane 63,63,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 23 | +TC_lane_64,lane 64,64,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 24 | +TC_lane_65,lane 65,65,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 25 | +TC_core_39,core 39,39,1,1,0.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 26 | +TC_core_40,core 40,40,1,1,0.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 27 | +TC_core_41,core 41,41,1,1,0.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 28 | +TC_strided_x,strided x,257,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 29 | +TC_vector_reorder_tail_1,vector reorder tail 1,1,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 30 | +TC_vector_reorder_tail_7,vector reorder tail 7,7,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 31 | +TC_vector_reorder_tail_8,vector reorder tail 8,8,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 32 | +TC_vector_reorder_tail_9,vector reorder tail 9,9,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 33 | +TC_vector_reorder_tile,vector reorder tile,512,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 34 | +TC_scalar_block_15,scalar block 15,600,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 35 | +TC_mixed_scalar_block_15_16,mixed scalar block 15 16,601,-2,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 36 | +TC_scalar_block_16,scalar block 16,640,2,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 37 | +TC_mixed_scalar_block_16_17,mixed scalar block 16 17,641,5,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 38 | +TC_scalar_upper_bound_16_17,scalar upper bound 16 17,679,-5,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 39 | +TC_scalar_wide_increment,scalar wide increment,17,65537,-32769,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 40 | +TC_vector_reorder_negative_tail,vector reorder negative tail,513,-10,-11,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 41 | +TC_strided_y,strided y,257,1,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 42 | +TC_strided_xy,strided xy,257,2,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 43 | +TC_stride_5_x,stride 5 x,257,5,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 44 | +TC_stride_5_y,stride 5 y,257,1,5,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 45 | +TC_stride_5_9,stride 5 9,257,5,9,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 46 | +TC_stride_9_x,stride 9 x,257,9,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 47 | +TC_stride_10_x_fallback,stride 10 x fallback,257,10,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 48 | +TC_stride_5_multicore,stride 5 multicore,4097,5,5,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 49 | +TC_shared_raw_inc2_multitile_tail,shared raw inc2 multitile tail,40961,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 50 | +TC_shared_tail_1,shared tail 1,41000,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 51 | +TC_shared_tail_8,shared tail 8,41280,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 52 | +TC_shared_tail_9,shared tail 9,41320,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 53 | +TC_shared_tail_16,shared tail 16,41600,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 54 | +TC_shared_tail_17,shared tail 17,41640,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 55 | +TC_shared_tail_1_incy1,shared tail 1 incy1,41000,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 56 | +TC_shared_tail_1_negative,shared tail 1 negative,41000,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 57 | +TC_shared_tail_multi_4tile,shared tail multi 4tile,61441,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 58 | +TC_shared_raw_inc5_multitile_tail,shared raw inc5 multitile tail,40961,5,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 59 | +TC_shared_raw_inc9_multitile_tail,shared raw inc9 multitile tail,40961,9,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 60 | +TC_negative_x,negative x,257,-2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 61 | +TC_negative_unit_y,negative unit y,257,2,-1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 62 | +TC_negative_unit_y_multitile,negative unit y multitile,4097,2,-1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 63 | +TC_negative_y,negative y,257,1,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 64 | +TC_negative_x_positive_y,negative x positive y,257,-2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 65 | +TC_positive_x_negative_y,positive x negative y,257,5,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 66 | +TC_negative_xy,negative xy,257,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 67 | +TC_negative_9_x,negative 9 x,257,-9,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 68 | +TC_negative_x_multicore,negative x multicore,4097,-2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 69 | +TC_negative_y_multicore,negative y multicore,4097,1,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 70 | +TC_opposite_sign_multicore,opposite sign multicore,4097,-2,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 71 | +TC_negative_xy_multicore,negative xy multicore,4097,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 72 | +TC_wide_stride_multitile,wide stride multitile,40961,10,11,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 73 | +TC_wide_stride_negative_multitile,wide stride negative multitile,40963,-10,-11,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 74 | +TC_dynamic_shared_incx16,dynamic shared incx16,40961,16,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 75 | +TC_dynamic_shared_incx32,dynamic shared incx32,40961,32,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 76 | +TC_dynamic_shared_incx41,dynamic shared incx41,40961,41,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 77 | +TC_dynamic_shared_boundary_usable,dynamic shared boundary usable,8000,60,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 78 | +TC_dynamic_shared_boundary_disjoint,dynamic shared boundary disjoint,8000,61,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 79 | +TC_strided_tile_plus_1,strided tile plus 1,194561,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 80 | +TC_multiwave_strided,multiwave strided,400000,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 81 | +TC_multiwave_negative,multiwave negative,400001,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 82 | +TC_multiwave_3_waves,multiwave 3 waves,700000,1,2,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 83 | +TC_pipeline_full_wave,pipeline full wave,327680,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 84 | +TC_hybrid_tail_1,hybrid tail 1,327681,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 85 | +TC_hybrid_tail_1_negative_y,hybrid tail 1 negative y,327681,2,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 86 | +TC_hybrid_tail_679_negative_x,hybrid tail 679 negative x,328359,-2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 87 | +TC_hybrid_tail_679_negative_xy,hybrid tail 679 negative xy,328359,-2,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 88 | +TC_pipeline_tail_680_negative_y,pipeline tail 680 negative y,328360,2,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 89 | +TC_regular,regular,16384,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 90 | +TC_tile_minus_1,tile minus 1,194559,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 91 | +TC_tile,tile,194560,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| 92 | +TC_tile_plus_1,tile plus 1,194561,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42 | ||
| @@ -0,0 +1,69 @@ | |||
| 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 | +inline aclblasStatus_t CaxpyValidateParams( | ||
| 18 | + aclblasHandle_t handle, int n, const aclblasComplex* alpha, const aclblasComplex* x, int incx, | ||
| 19 | + const aclblasComplex* y, int incy) | ||
| 20 | +{ | ||
| 21 | + if (handle == nullptr || alpha == nullptr) { | ||
| 22 | + return ACLBLAS_STATUS_HANDLE_IS_NULLPTR; | ||
| 23 | + } | ||
| 24 | + if (n < 0 || incx == 0 || incy == 0) { | ||
| 25 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
| 26 | + } | ||
| 27 | + if (n > 0 && (x == nullptr || y == nullptr)) { | ||
| 28 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
| 29 | + } | ||
| 30 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +inline aclblasComplex CaxpyComplexMac(aclblasComplex alpha, aclblasComplex x, aclblasComplex y) | ||
| 34 | +{ | ||
| 35 | + return {alpha.real * x.real - alpha.imag * x.imag + y.real, alpha.real * x.imag + alpha.imag * x.real + y.imag}; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +// Logical-index -> physical storage index for a strided complex vector. | ||
| 39 | +// Positive stride reads forward from the base; negative stride is BLAS | ||
| 40 | +// convention: the last logical element is physically first. | ||
| 41 | +inline int64_t CaxpyStorageIndex(int64_t logicalIndex, int64_t n, int64_t increment) | ||
| 42 | +{ | ||
| 43 | + const uint64_t absIncrement = | ||
| 44 | + increment > 0 ? static_cast<uint64_t>(increment) : static_cast<uint64_t>(-increment); | ||
| 45 | + return increment > 0 ? logicalIndex * static_cast<int64_t>(absIncrement) | ||
| 46 | + : (n - 1 - logicalIndex) * static_cast<int64_t>(absIncrement); | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +// y = alpha * x + y, complex, arbitrary (possibly negative) incx/incy. | ||
| 50 | +// x and y are the caller-owned base storage pointers (not offset by any guard region). | ||
| 51 | +inline aclblasStatus_t aclblasCaxpy_cpu( | ||
| 52 | + aclblasHandle_t handle, int n, const aclblasComplex* alpha, const aclblasComplex* x, int incx, | ||
| 53 | + aclblasComplex* y, int incy) | ||
| 54 | +{ | ||
| 55 | + aclblasStatus_t st = CaxpyValidateParams(handle, n, alpha, x, incx, y, incy); | ||
| 56 | + if (st != ACLBLAS_STATUS_SUCCESS) { | ||
| 57 | + return st; | ||
| 58 | + } | ||
| 59 | + if (n == 0) { | ||
| 60 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + for (int64_t i = 0; i < n; i++) { | ||
| 64 | + const int64_t xIdx = CaxpyStorageIndex(i, n, incx); | ||
| 65 | + const int64_t yIdx = CaxpyStorageIndex(i, n, incy); | ||
| 66 | + y[yIdx] = CaxpyComplexMac(*alpha, x[xIdx], y[yIdx]); | ||
| 67 | + } | ||
| 68 | + return ACLBLAS_STATUS_SUCCESS; | ||
| 69 | +} | ||
| @@ -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 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +struct CaxpyParam : public BlasTestParamBase { | ||
| 18 | + int n = 0; | ||
| 19 | + int incx = 1; | ||
| 20 | + int incy = 1; | ||
| 21 | + float alphaReal = 1.0f; | ||
| 22 | + float alphaImag = 0.0f; | ||
| 23 | + int nullx = 0; | ||
| 24 | + int nully = 0; | ||
| 25 | + | ||
| 26 | + CaxpyParam(const csv_map& csv) : BlasTestParamBase(csv) | ||
| 27 | + { | ||
| 28 | + n = parseInt(ReadMap(csv, "n", "0")); | ||
| 29 | + incx = parseInt(ReadMap(csv, "incx", "1")); | ||
| 30 | + incy = parseInt(ReadMap(csv, "incy", "1")); | ||
| 31 | + alphaReal = parseFloat(ReadMap(csv, "alpha_real", "1.0")); | ||
| 32 | + alphaImag = parseFloat(ReadMap(csv, "alpha_imag", "0.0")); | ||
| 33 | + nullx = parseInt(ReadMap(csv, "nullx", "0")); | ||
| 34 | + nully = parseInt(ReadMap(csv, "nully", "0")); | ||
| 35 | + } | ||
| 36 | +}; | ||


【编码规范 R10】禁止在 Host 里用前向声明引用 kernel_do。
请新增对应的
*_kernel.h,把kernel_do声明放到头文件,Host 通过#include引入。R10 例外只覆盖 kernel.cpp 里 Ascend C 要求的extern "C"入口。