已开启
Feat: 新增面向arch22的aclblasCaxpy任意incx/incy支持 #334
guodong54_创建于 8月13日
Feat: 新增面向arch22的aclblasCaxpy任意incx/incy支持 #334
已开启
共 16 个文件变更+2234-394
| @@ -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 | + | ||
| @@ -1,3 +1,4 @@ | |||
| 1 | + | ||
| 1 | /** | 2 | /** |
| 2 | * Copyright (c) 2025 Huawei Technologies Co., Ltd. | 3 | * Copyright (c) 2025 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 | * This program is free software, you can redistribute it and/or modify it under the terms and conditions of |
| @@ -18,10 +19,49 @@ | |||
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | 21 | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 21 | 25 | ||
| 22 | 26 | ||
| 23 | -__aicore__ __inline__ __attribute__((always_inline)) void copy_vec_gm2ub_uint32(__ubuf__ uint32_t *dst, | 27 | +// Unified Buffer capacity on 910B3 (192 KiB). All *_ub_layout() functions below |
| 24 | - __gm__ uint32_t *src, uint32_t len) | 28 | +// must fit their working set within this budget; the static_asserts next to |
| 29 | +// each layout enforce it at compile time instead of relying on manual review. | ||
| 30 | +constexpr uint32_t CAXPY_UB_BUDGET_BYTES = 192 * 1024; | ||
| 31 | + | ||
| 32 | +// --- Dense (contiguous incx=1, incy=1) kernel UB layout ------------------- | ||
| 33 | +// Two ping-pong slots for the load buffer (ubX) and the output buffer (ubOut), | ||
| 34 | +// each sized to one full tile (38 KiB), followed by a full-tile offset table | ||
| 35 | +// (ubOffset) and a small 8-float coefficient table (ubCoefficient). | ||
| 36 | +constexpr uint32_t CAXPY_DENSE_TILE_FLOATS = 38 * 1024 / sizeof(float); | ||
| 37 | +constexpr uint32_t CAXPY_DENSE_TILE_BYTES = 38 * 1024; | ||
| 38 | +constexpr uint32_t CAXPY_DENSE_UB_OUT0_BYTES = 0 * CAXPY_DENSE_TILE_BYTES; | ||
| 39 | +constexpr uint32_t CAXPY_DENSE_UB_OUT1_BYTES = 1 * CAXPY_DENSE_TILE_BYTES; | ||
| 40 | +constexpr uint32_t CAXPY_DENSE_UB_X0_BYTES = 2 * CAXPY_DENSE_TILE_BYTES; | ||
| 41 | +constexpr uint32_t CAXPY_DENSE_UB_X1_BYTES = 3 * CAXPY_DENSE_TILE_BYTES; | ||
| 42 | +constexpr uint32_t CAXPY_DENSE_UB_OFFSET_BYTES = 4 * CAXPY_DENSE_TILE_BYTES; | ||
| 43 | +constexpr uint32_t CAXPY_DENSE_UB_COEFFICIENT_BYTES = 5 * CAXPY_DENSE_TILE_BYTES; | ||
| 44 | +constexpr uint32_t CAXPY_DENSE_UB_COEFFICIENT_COUNT = 8; | ||
| 45 | +// Offset table region: one uint32 pair-swap offset per float in a full tile. | ||
| 46 | +// The trailing terms mirror the strided-kernel GM mask layout below this | ||
| 47 | +// function (CAXPY_X_OFFSET_COUNT and 2*CAXPY_SPAN_OFFSET_COUNT, both defined | ||
| 48 | +// later in this file for the strided kernels' own UB tables): the host lays | ||
| 49 | +// out gmAug as [compute offsets][x-normalize offsets][store offsets] | ||
| 50 | +// [y-gather offsets][dense swap offsets], so this dense-kernel offset into | ||
| 51 | +// gmAug must skip past the strided sections that precede it in that layout, | ||
| 52 | +// even though the dense and strided kernels are otherwise independent code | ||
| 53 | +// paths. See caxpy_host.cpp's DENSE_SWAP_OFFSET_START for the authoritative | ||
| 54 | +// host-side computation this mirrors. | ||
| 55 | +constexpr uint32_t CAXPY_DENSE_SWAP_SKIP_X_OFFSET_WORDS = 512 * 8 + 64; // == CAXPY_X_OFFSET_COUNT | ||
| 56 | +constexpr uint32_t CAXPY_DENSE_SWAP_SKIP_SPAN_OFFSET_WORDS = 2 * (512 * 2 + 64); // == 2 * CAXPY_SPAN_OFFSET_COUNT | ||
| 57 | +constexpr uint32_t CAXPY_DENSE_SWAP_OFFSET_START = | ||
| 58 | + CAXPY_DENSE_TILE_FLOATS + CAXPY_DENSE_SWAP_SKIP_X_OFFSET_WORDS + CAXPY_DENSE_SWAP_SKIP_SPAN_OFFSET_WORDS; | ||
| 59 | +static_assert( | ||
| 60 | + CAXPY_DENSE_UB_COEFFICIENT_BYTES + CAXPY_DENSE_UB_COEFFICIENT_COUNT * sizeof(float) <= CAXPY_UB_BUDGET_BYTES, | ||
| 61 | + "CAXPY dense UB layout exceeds the 910B3 Unified Buffer budget"); | ||
| 62 | + | ||
| 63 | +__aicore__ __inline__ __attribute__((always_inline)) void copy_vec_gm2ub_uint32( | ||
| 64 | + __ubuf__ uint32_t* dst, __gm__ uint32_t* src, uint32_t len) | ||
| 25 | { | 65 | { |
| 26 | uint16_t nBurst = 1; | 66 | uint16_t nBurst = 1; |
| 27 | uint32_t lenBurst = len * sizeof(uint32_t); | 67 | uint32_t lenBurst = len * sizeof(uint32_t); |
| @@ -29,13 +69,11 @@ __aicore__ __inline__ __attribute__((always_inline)) void copy_vec_gm2ub_uint32( | |||
| 29 | uint8_t rightPaddingNum = 0; | 69 | uint8_t rightPaddingNum = 0; |
| 30 | uint32_t srcGap = 0; | 70 | uint32_t srcGap = 0; |
| 31 | uint32_t dstGap = 0; | 71 | uint32_t dstGap = 0; |
| 32 | - copy_gm_to_ubuf_align_b32(dst, src, | 72 | + copy_gm_to_ubuf_align_b32(dst, src, 0, nBurst, lenBurst, leftPaddingNum, rightPaddingNum, srcGap, dstGap); |
| 33 | - 0, | ||
| 34 | - nBurst, lenBurst, leftPaddingNum, rightPaddingNum, srcGap, dstGap); | ||
| 35 | } | 73 | } |
| 36 | 74 | ||
| 37 | -__aicore__ __inline__ __attribute__((always_inline)) void copy_vec_gm2ub(__ubuf__ float *dst, __gm__ float *src, | 75 | +__aicore__ __inline__ __attribute__((always_inline)) void copy_vec_gm2ub( |
| 38 | - uint32_t len) | 76 | + __ubuf__ float* dst, __gm__ float* src, uint32_t len) |
| 39 | { | 77 | { |
| 40 | uint16_t nBurst = 1; | 78 | uint16_t nBurst = 1; |
| 41 | uint32_t lenBurst = len * sizeof(float); | 79 | uint32_t lenBurst = len * sizeof(float); |
| @@ -43,13 +81,11 @@ __aicore__ __inline__ __attribute__((always_inline)) void copy_vec_gm2ub(__ubuf_ | |||
| 43 | uint8_t rightPaddingNum = 0; | 81 | uint8_t rightPaddingNum = 0; |
| 44 | uint32_t srcGap = 0; | 82 | uint32_t srcGap = 0; |
| 45 | uint32_t dstGap = 0; | 83 | uint32_t dstGap = 0; |
| 46 | - copy_gm_to_ubuf_align_b32(dst, src, | 84 | + copy_gm_to_ubuf_align_b32(dst, src, 0, nBurst, lenBurst, leftPaddingNum, rightPaddingNum, srcGap, dstGap); |
| 47 | - 0, | ||
| 48 | - nBurst, lenBurst, leftPaddingNum, rightPaddingNum, srcGap, dstGap); | ||
| 49 | } | 85 | } |
| 50 | 86 | ||
| 51 | -__aicore__ __inline__ __attribute__((always_inline)) void copy_vec_ub2gm(__gm__ float *dst, __ubuf__ float *src, | 87 | +__aicore__ __inline__ __attribute__((always_inline)) void copy_vec_ub2gm( |
| 52 | - uint32_t len) | 88 | + __gm__ float* dst, __ubuf__ float* src, uint32_t len) |
| 53 | { | 89 | { |
| 54 | uint16_t nBurst = 1; | 90 | uint16_t nBurst = 1; |
| 55 | uint32_t lenBurst = len * sizeof(float); | 91 | uint32_t lenBurst = len * sizeof(float); |
| @@ -57,145 +93,677 @@ __aicore__ __inline__ __attribute__((always_inline)) void copy_vec_ub2gm(__gm__ | |||
| 57 | uint8_t rightPaddingNum = 0; | 93 | uint8_t rightPaddingNum = 0; |
| 58 | uint32_t srcGap = 0; | 94 | uint32_t srcGap = 0; |
| 59 | uint32_t dstGap = 0; | 95 | uint32_t dstGap = 0; |
| 60 | - copy_ubuf_to_gm_align_b32(dst, src, | 96 | + copy_ubuf_to_gm_align_b32(dst, src, 0, nBurst, lenBurst, leftPaddingNum, rightPaddingNum, srcGap, dstGap); |
| 61 | - 0, | ||
| 62 | - nBurst, lenBurst, leftPaddingNum, rightPaddingNum, srcGap, dstGap); | ||
| 63 | } | 97 | } |
| 64 | 98 | ||
| 65 | -__aicore__ __inline__ __attribute__((always_inline)) void caxpy_compute_aiv( | 99 | +__aicore__ __inline__ __attribute__((always_inline)) uint32_t abs_increment(int64_t increment) |
| 66 | - __gm__ float *gm_in, __gm__ float *gm_out, __ubuf__ float *ub_in, __ubuf__ float *ub_out, | ||
| 67 | - __ubuf__ uint32_t *ub_offset, float s_real, float s_imag, uint32_t copy_len, uint32_t len, uint32_t event_id) | ||
| 68 | { | 100 | { |
| 69 | - uint32_t repeatTime = (len + 63) / 64; | 101 | + return increment > 0 ? static_cast<uint32_t>(increment) : static_cast<uint32_t>(-increment); |
| 70 | - uint32_t computeRepeat = (len / 2 + 63) / 64; | ||
| 71 | - | ||
| 72 | - uint32_t real_offset = 0; | ||
| 73 | - uint32_t imag_offset = 38 * 1024 / sizeof(float) / 2; | ||
| 74 | - | ||
| 75 | - auto ub_out_real = ub_out; | ||
| 76 | - auto ub_out_imag = ub_out + imag_offset; | ||
| 77 | - | ||
| 78 | - copy_vec_gm2ub(ub_in, gm_in, copy_len); | ||
| 79 | - | ||
| 80 | - set_flag(PIPE_MTE2, PIPE_V, event_id); | ||
| 81 | - wait_flag(PIPE_MTE2, PIPE_V, event_id); | ||
| 82 | - | ||
| 83 | - vreducev2(reinterpret_cast<__ubuf__ uint32_t *>(ub_out_real), | ||
| 84 | - reinterpret_cast<__ubuf__ uint32_t *>(ub_in), | ||
| 85 | - nullptr, repeatTime, | ||
| 86 | - 1, | ||
| 87 | - 1, | ||
| 88 | - 8, 8); | ||
| 89 | - | ||
| 90 | - vreducev2(reinterpret_cast<__ubuf__ uint32_t *>(ub_out_imag), | ||
| 91 | - reinterpret_cast<__ubuf__ uint32_t *>(ub_in), | ||
| 92 | - nullptr, repeatTime, | ||
| 93 | - 1, | ||
| 94 | - 2, | ||
| 95 | - 8, 8); | ||
| 96 | - | ||
| 97 | - pipe_barrier(PIPE_V); | ||
| 98 | - | ||
| 99 | - vmuls(ub_in, ub_out_real, s_real, computeRepeat, 1, 1, 8, 8); | ||
| 100 | - vmuls(ub_in + imag_offset, ub_out_real, s_imag, computeRepeat, 1, 1, 8, 8); | ||
| 101 | - | ||
| 102 | - vmuls(ub_out_real, ub_out_imag, s_imag, computeRepeat, 1, 1, 8, 8); | ||
| 103 | - | ||
| 104 | - pipe_barrier(PIPE_V); | ||
| 105 | - vsub(ub_in, ub_in, ub_out_real, computeRepeat, 1, 1, 1, 8, 8, 8); | ||
| 106 | - | ||
| 107 | - vmuls(ub_out_imag, ub_out_imag, s_real, computeRepeat, 1, 1, 8, 8); | ||
| 108 | - | ||
| 109 | - pipe_barrier(PIPE_V); | ||
| 110 | - vadd(ub_in + imag_offset, ub_out_imag, ub_in + imag_offset, computeRepeat, 1, 1, 1, 8, 8, 8); | ||
| 111 | - | ||
| 112 | - pipe_barrier(PIPE_V); | ||
| 113 | - | ||
| 114 | - vgather(reinterpret_cast<__ubuf__ uint32_t *>(ub_out), reinterpret_cast<__ubuf__ uint32_t *>(ub_offset), | ||
| 115 | - (uintptr_t)(ub_in), | ||
| 116 | - 8, | ||
| 117 | - repeatTime | ||
| 118 | - ); | ||
| 119 | - | ||
| 120 | - set_flag(PIPE_V, PIPE_MTE3, event_id); | ||
| 121 | - wait_flag(PIPE_V, PIPE_MTE3, event_id); | ||
| 122 | - | ||
| 123 | - copy_vec_ub2gm(gm_out, ub_out, copy_len); | ||
| 124 | } | 102 | } |
| 125 | 103 | ||
| 126 | -__aicore__ __inline__ __attribute__((always_inline)) void caxpy_aiv(__gm__ float *gm_in, __gm__ uint32_t *gm_aug, | 104 | +__aicore__ __inline__ __attribute__((always_inline)) bool copy_complex_gm2ub_signed( |
| 127 | - __gm__ float *gm_out, float s_real, float s_imag, | 105 | + __ubuf__ float* dst, __ubuf__ float* denseTemp, __gm__ float* source, uint32_t logicalOffset, |
| 128 | - uint32_t offset, uint32_t calNum) | 106 | + uint32_t totalComplexCount, uint32_t complexCount, int64_t signedIncrement) |
| 129 | { | 107 | { |
| 108 | + constexpr uint32_t maxBurstCount = 4088; | ||
| 109 | + const uint32_t increment = abs_increment(signedIncrement); | ||
| 110 | + const uint32_t physicalOffset = signedIncrement > 0 ? | ||
| 111 | + logicalOffset * increment : | ||
| 112 | + (totalComplexCount - logicalOffset - complexCount) * increment; | ||
| 113 | + auto src = source + physicalOffset * 2; | ||
| 114 | + if (increment == 1) { | ||
| 115 | + copy_gm_to_ubuf_align_b32(denseTemp, src, 0, 1, complexCount * 2 * sizeof(float), 0, 0, 0, 0); | ||
| 116 | + return true; | ||
| 117 | + } | ||
| 118 | + const uint32_t gapBytes = (increment - 1) * 2 * sizeof(float); | ||
| 119 | + uint32_t copied = 0; | ||
| 120 | + while (copied < complexCount) { | ||
| 121 | + const uint32_t batch = (complexCount - copied) > maxBurstCount ? maxBurstCount : complexCount - copied; | ||
| 122 | + asc_copy_gm2ub_align( | ||
| 123 | + dst + copied * 8, src + copied * increment * 2, static_cast<uint16_t>(batch), 2 * sizeof(float), 0, 0, | ||
| 124 | + gapBytes, 0); | ||
| 125 | + copied += batch; | ||
| 126 | + } | ||
| 127 | + return false; | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +__aicore__ __inline__ __attribute__((always_inline)) void normalize_loaded_complexes( | ||
| 131 | + __ubuf__ float* dst, __ubuf__ float* denseTemp, uint32_t complexCount, bool denseLoaded, bool reverse) | ||
| 132 | +{ | ||
| 133 | + if (denseLoaded) { | ||
| 134 | + for (uint32_t i = 0; i < complexCount; ++i) { | ||
| 135 | + const uint32_t sourceIndex = reverse ? complexCount - 1 - i : i; | ||
| 136 | + dst[i * 8] = denseTemp[sourceIndex * 2]; | ||
| 137 | + dst[i * 8 + 1] = denseTemp[sourceIndex * 2 + 1]; | ||
| 138 | + } | ||
| 139 | + return; | ||
| 140 | + } | ||
| 141 | + if (reverse) { | ||
| 142 | + for (uint32_t left = 0; left < complexCount / 2; ++left) { | ||
| 143 | + const uint32_t right = complexCount - 1 - left; | ||
| 144 | + const float leftReal = dst[left * 8]; | ||
| 145 | + const float leftImag = dst[left * 8 + 1]; | ||
| 146 | + dst[left * 8] = dst[right * 8]; | ||
| 147 | + dst[left * 8 + 1] = dst[right * 8 + 1]; | ||
| 148 | + dst[right * 8] = leftReal; | ||
| 149 | + dst[right * 8 + 1] = leftImag; | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +__aicore__ __inline__ __attribute__((always_inline)) void copy_complex_ub2gm_signed( | ||
| 155 | + __gm__ float* destination, __ubuf__ float* src, uint32_t logicalOffset, uint32_t totalComplexCount, | ||
| 156 | + uint32_t complexCount, int64_t signedIncrement) | ||
| 157 | +{ | ||
| 158 | + constexpr uint32_t maxBurstCount = 4088; | ||
| 159 | + const uint32_t increment = abs_increment(signedIncrement); | ||
| 160 | + const uint32_t physicalOffset = signedIncrement > 0 ? | ||
| 161 | + logicalOffset * increment : | ||
| 162 | + (totalComplexCount - logicalOffset - complexCount) * increment; | ||
| 163 | + auto dst = destination + physicalOffset * 2; | ||
| 164 | + if (increment == 1) { | ||
| 165 | + copy_ubuf_to_gm_align_b32(dst, src, 0, 1, complexCount * 2 * sizeof(float), 0, 0, 0, 0); | ||
| 166 | + return; | ||
| 167 | + } | ||
| 168 | + const uint32_t gapBytes = (increment - 1) * 2 * sizeof(float); | ||
| 169 | + uint32_t copied = 0; | ||
| 170 | + while (copied < complexCount) { | ||
| 171 | + const uint32_t batch = (complexCount - copied) > maxBurstCount ? maxBurstCount : complexCount - copied; | ||
| 172 | + asc_copy_ub2gm_align( | ||
| 173 | + dst + copied * increment * 2, src + copied * 8, static_cast<uint16_t>(batch), 2 * sizeof(float), 0, 0, 0, | ||
| 174 | + gapBytes); | ||
| 175 | + copied += batch; | ||
| 176 | + } | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_compute_planar( | ||
| 180 | + __ubuf__ float* resultReal, __ubuf__ float* resultImag, __ubuf__ float* sourceReal, | ||
| 181 | + __ubuf__ float* sourceImag, float alphaReal, float alphaImag, uint32_t repeat) | ||
| 182 | +{ | ||
| 183 | + // Keep every vector address 32-byte aligned. An interleaved implementation using | ||
| 184 | + // source + 1 is mathematically equivalent, but faults on C220 vector instructions. | ||
| 185 | + vmuls(resultReal, sourceReal, alphaReal, repeat, 1, 1, 8, 8); | ||
| 186 | + vmuls(resultImag, sourceImag, alphaReal, repeat, 1, 1, 8, 8); | ||
| 187 | + pipe_barrier(PIPE_V); | ||
| 188 | + vaxpy(resultReal, sourceImag, -alphaImag, repeat, 1, 1, 8, 8); | ||
| 189 | + vaxpy(resultImag, sourceReal, alphaImag, repeat, 1, 1, 8, 8); | ||
| 190 | + pipe_barrier(PIPE_V); | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | + | ||
| 194 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_dense_load( | ||
| 195 | + __gm__ float* gmX, __ubuf__ float* ubX, uint32_t copyLength, uint32_t eventId) | ||
| 196 | +{ | ||
| 197 | + copy_vec_gm2ub(ubX, gmX, copyLength); | ||
| 198 | + set_flag(PIPE_MTE2, PIPE_V, eventId); | ||
| 199 | +} | ||
| 200 | + | ||
| 201 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_dense_compute( | ||
| 202 | + __ubuf__ float* ubX, __ubuf__ float* ubOut, __ubuf__ uint32_t* ubOffset, | ||
| 203 | + __ubuf__ float* ubCoefficient, float alphaReal, uint32_t computeLength, uint32_t eventId) | ||
| 204 | +{ | ||
| 205 | + const uint32_t repeat = (computeLength + 63) / 64; | ||
| 206 | + | ||
| 207 | + wait_flag(PIPE_MTE2, PIPE_V, eventId); | ||
| 208 | + // Turn [real, imag] into [imag, real]. The alternating coefficient then | ||
| 209 | + // applies [-alphaImag, +alphaImag] without splitting into planar buffers. | ||
| 210 | + vgather( | ||
| 211 | + reinterpret_cast<__ubuf__ uint32_t*>(ubOut), ubOffset, (uintptr_t)ubX, 8, repeat); | ||
| 212 | + pipe_barrier(PIPE_V); | ||
| 213 | + vmuls(ubX, ubX, alphaReal, repeat, 1, 1, 8, 8); | ||
| 214 | + vmul(ubOut, ubOut, ubCoefficient, repeat, 1, 1, 0, 8, 8, 0); | ||
| 215 | + pipe_barrier(PIPE_V); | ||
| 216 | + vadd(ubOut, ubX, ubOut, repeat, 1, 1, 1, 8, 8, 8); | ||
| 217 | + set_flag(PIPE_V, PIPE_MTE3, eventId); | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_dense_store( | ||
| 221 | + __gm__ float* gmY, __ubuf__ float* ubOut, uint32_t copyLength, uint32_t eventId) | ||
| 222 | +{ | ||
| 223 | + wait_flag(PIPE_V, PIPE_MTE3, eventId); | ||
| 224 | + copy_vec_ub2gm(gmY, ubOut, copyLength); | ||
| 225 | + set_flag(PIPE_MTE3, PIPE_MTE2, eventId); | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_dense_prepare( | ||
| 229 | + __gm__ uint32_t* gmAug, __ubuf__ uint32_t*& ubOffset, __ubuf__ float*& ubCoefficient, float alphaImag) | ||
| 230 | +{ | ||
| 231 | + constexpr uint32_t tileFloats = CAXPY_DENSE_TILE_FLOATS; | ||
| 232 | + ubOffset = reinterpret_cast<__ubuf__ uint32_t*>((uintptr_t)CAXPY_DENSE_UB_OFFSET_BYTES); | ||
| 233 | + ubCoefficient = reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_DENSE_UB_COEFFICIENT_BYTES); | ||
| 234 | + copy_vec_gm2ub_uint32(ubOffset, gmAug + CAXPY_DENSE_SWAP_OFFSET_START, tileFloats); | ||
| 235 | + for (uint32_t index = 0; index < CAXPY_DENSE_UB_COEFFICIENT_COUNT; ++index) | ||
| 236 | + ubCoefficient[index] = (index & 1) == 0 ? -alphaImag : alphaImag; | ||
| 237 | + set_flag(PIPE_S, PIPE_V, EVENT_ID2); | ||
| 238 | + wait_flag(PIPE_S, PIPE_V, EVENT_ID2); | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_dense_load_next_if_any( | ||
| 242 | + __gm__ float* gmX, __ubuf__ float* ubX[2], uint32_t offset, uint32_t calNum, uint32_t tileFloats, | ||
| 243 | + uint32_t tile, uint32_t tileCount, uint32_t current) | ||
| 244 | +{ | ||
| 245 | + if (tile + 1 >= tileCount) | ||
| 246 | + return; | ||
| 247 | + const uint32_t next = 1 - current; | ||
| 248 | + const uint32_t nextEvent = next == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 249 | + const uint32_t nextOffset = (tile + 1) * tileFloats; | ||
| 250 | + const uint32_t nextCount = calNum - nextOffset > tileFloats ? tileFloats : calNum - nextOffset; | ||
| 251 | + wait_flag(PIPE_MTE3, PIPE_MTE2, nextEvent); | ||
| 252 | + caxpy_dense_load(gmX + offset + nextOffset, ubX[next], nextCount, nextEvent); | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_dense_run( | ||
Z 【编码规范 R7】函数非空非注释行数 ≤ 50。
![]() ![]() | |||
| 256 | + __gm__ float* gmX, __gm__ uint32_t* gmAug, __gm__ float* gmY, float alphaReal, float alphaImag, | ||
| 257 | + uint32_t offset, uint32_t calNum) | ||
| 258 | +{ | ||
| 259 | + constexpr uint32_t tileFloats = CAXPY_DENSE_TILE_FLOATS; | ||
| 260 | + __ubuf__ float* ubOut[2] = { | ||
| 261 | + reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_DENSE_UB_OUT0_BYTES), | ||
| 262 | + reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_DENSE_UB_OUT1_BYTES)}; | ||
| 263 | + __ubuf__ float* ubX[2] = { | ||
| 264 | + reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_DENSE_UB_X0_BYTES), | ||
| 265 | + reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_DENSE_UB_X1_BYTES)}; | ||
| 266 | + __ubuf__ uint32_t* ubOffset = nullptr; | ||
| 267 | + __ubuf__ float* ubCoefficient = nullptr; | ||
| 268 | + caxpy_dense_prepare(gmAug, ubOffset, ubCoefficient, alphaImag); | ||
| 269 | + | ||
| 130 | set_atomic_add(); | 270 | set_atomic_add(); |
| 131 | set_atomic_f32(); | 271 | set_atomic_f32(); |
| 272 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 273 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 132 | 274 | ||
| 133 | - auto ub_out_ping = reinterpret_cast<__ubuf__ float *>((uintptr_t)0 * 1024); | 275 | + const uint32_t tileCount = (calNum + tileFloats - 1) / tileFloats; |
| 134 | - auto ub_out_pong = reinterpret_cast<__ubuf__ float *>((uintptr_t)38 * 1024); | 276 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); |
| 135 | - auto ub_in_ping = reinterpret_cast<__ubuf__ float *>((uintptr_t)76 * 1024); | 277 | + const uint32_t firstCount = calNum > tileFloats ? tileFloats : calNum; |
| 136 | - auto ub_in_pong = reinterpret_cast<__ubuf__ float *>((uintptr_t)114 * 1024); | 278 | + caxpy_dense_load(gmX + offset, ubX[0], firstCount, EVENT_ID0); |
| 137 | - auto ub_offset = reinterpret_cast<__ubuf__ uint32_t *>((uintptr_t)152 * 1024); | ||
| 138 | 279 | ||
| 139 | - uint32_t ping_flag = 1; | 280 | + for (uint32_t tile = 0; tile < tileCount; ++tile) { |
| 281 | + const uint32_t current = tile & 1; | ||
| 282 | + const uint32_t currentEvent = current == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 283 | + const uint32_t currentOffset = tile * tileFloats; | ||
| 284 | + const uint32_t currentCount = | ||
| 285 | + calNum - currentOffset > tileFloats ? tileFloats : calNum - currentOffset; | ||
| 140 | 286 | ||
| 141 | - uint32_t maxDataCount = 38 * 1024 / sizeof(float); | 287 | + caxpy_dense_load_next_if_any(gmX, ubX, offset, calNum, tileFloats, tile, tileCount, current); |
| 142 | 288 | ||
| 143 | - uint32_t repeatTime = calNum / maxDataCount; | 289 | + caxpy_dense_compute( |
| 144 | - uint32_t remainNum = calNum % maxDataCount; | 290 | + ubX[current], ubOut[current], ubOffset, ubCoefficient, alphaReal, tileFloats, currentEvent); |
| 145 | - | 291 | + caxpy_dense_store(gmY + offset + currentOffset, ubOut[current], currentCount, currentEvent); |
| 146 | - copy_vec_gm2ub_uint32(ub_offset, gm_aug, maxDataCount); | ||
| 147 | - | ||
| 148 | - uint32_t curr_offset = offset; | ||
| 149 | - | ||
| 150 | - if (repeatTime > 0) { | ||
| 151 | - set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 152 | - set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 153 | - for (uint32_t i = 0; i < repeatTime; i++) { | ||
| 154 | - auto ub_in = ping_flag ? ub_in_ping : ub_in_pong; | ||
| 155 | - auto ub_out = ping_flag ? ub_out_ping : ub_out_pong; | ||
| 156 | - | ||
| 157 | - auto event_id = ping_flag ? EVENT_ID0 : EVENT_ID1; | ||
| 158 | - | ||
| 159 | - wait_flag(PIPE_MTE3, PIPE_MTE2, event_id); | ||
| 160 | - | ||
| 161 | - caxpy_compute_aiv(gm_in + curr_offset, gm_out + curr_offset, ub_in, ub_out, ub_offset, s_real, s_imag, | ||
| 162 | - maxDataCount, maxDataCount, event_id); | ||
| 163 | - | ||
| 164 | - set_flag(PIPE_MTE3, PIPE_MTE2, event_id); | ||
| 165 | - | ||
| 166 | - curr_offset += maxDataCount; | ||
| 167 | - ping_flag = 1 - ping_flag; | ||
| 168 | - } | ||
| 169 | - wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 170 | - wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 171 | - } | ||
| 172 | - | ||
| 173 | - if (remainNum > 0) { | ||
| 174 | - set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 175 | - set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 176 | - auto ub_in = ping_flag ? ub_in_ping : ub_in_pong; | ||
| 177 | - auto ub_out = ping_flag ? ub_out_ping : ub_out_pong; | ||
| 178 | - | ||
| 179 | - auto event_id = ping_flag ? EVENT_ID0 : EVENT_ID1; | ||
| 180 | - | ||
| 181 | - wait_flag(PIPE_MTE3, PIPE_MTE2, event_id); | ||
| 182 | - | ||
| 183 | - caxpy_compute_aiv(gm_in + curr_offset, gm_out + curr_offset, ub_in, ub_out, ub_offset, s_real, s_imag, | ||
| 184 | - remainNum, maxDataCount, event_id); | ||
| 185 | - | ||
| 186 | - set_flag(PIPE_MTE3, PIPE_MTE2, event_id); | ||
| 187 | - | ||
| 188 | - wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 189 | - wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 190 | } | 292 | } |
| 191 | 293 | ||
| 294 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 295 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 192 | pipe_barrier(PIPE_ALL); | 296 | pipe_barrier(PIPE_ALL); |
| 193 | set_atomic_none(); | 297 | set_atomic_none(); |
| 194 | } | 298 | } |
| 299 | + | ||
| 300 | + | ||
| 301 | +constexpr uint32_t CAXPY_MAX_DATA_COUNT = 38 * 1024 / sizeof(float); | ||
| 302 | +constexpr uint32_t CAXPY_UB_COMPLEX_CAPACITY = CAXPY_MAX_DATA_COUNT / 2; | ||
| 303 | +// CAXPY_STRIDED_TILE_COUNT is defined once in caxpy_dispatch_policy.h (shared | ||
| 304 | +// with the host's shared-tile-capacity computation); this file must not | ||
| 305 | +// redefine it, or the two could silently drift apart the way the host's | ||
| 306 | +// GenXNormalizeOffsets 16-element compute-offset table drifted from the | ||
| 307 | +// device's copy size before that was fixed. | ||
| 308 | +static_assert(CAXPY_STRIDED_TILE_COUNT == 512, "CAXPY_STRIDED_TILE_COUNT definition moved or changed unexpectedly"); | ||
| 309 | +constexpr uint32_t CAXPY_INTERLEAVE_COUNT = 16; | ||
| 310 | +constexpr uint32_t CAXPY_BLOCKED_FLOATS = 8; | ||
| 311 | +constexpr uint32_t CAXPY_GATHER_PADDING = 64; | ||
| 312 | +constexpr uint32_t CAXPY_X_OFFSET_COUNT = CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS + CAXPY_GATHER_PADDING; | ||
| 313 | +constexpr uint32_t CAXPY_SPAN_OFFSET_COUNT = CAXPY_STRIDED_TILE_COUNT * 2 + CAXPY_GATHER_PADDING; | ||
| 314 | +static_assert( | ||
| 315 | + CAXPY_DENSE_SWAP_SKIP_X_OFFSET_WORDS == CAXPY_X_OFFSET_COUNT, | ||
| 316 | + "CAXPY_DENSE_SWAP_OFFSET_START's skip term no longer matches CAXPY_X_OFFSET_COUNT"); | ||
| 317 | +static_assert( | ||
| 318 | + CAXPY_DENSE_SWAP_SKIP_SPAN_OFFSET_WORDS == 2 * CAXPY_SPAN_OFFSET_COUNT, | ||
| 319 | + "CAXPY_DENSE_SWAP_OFFSET_START's skip term no longer matches CAXPY_SPAN_OFFSET_COUNT"); | ||
| 320 | +constexpr uint32_t CAXPY_STORE_OFFSET_START = CAXPY_MAX_DATA_COUNT + CAXPY_X_OFFSET_COUNT; | ||
| 321 | +constexpr uint32_t CAXPY_Y_GATHER_OFFSET_START = CAXPY_STORE_OFFSET_START + CAXPY_SPAN_OFFSET_COUNT; | ||
| 322 | + | ||
| 323 | +// --- Strided kernel UB byte layout ----------------------------------------- | ||
| 324 | +// Named byte offsets for the "shared" ping-pong buffer pair (strided_ub_layout, | ||
| 325 | +// used directly by run_shared_pipeline slot 0, and by disjoint/shared variants | ||
| 326 | +// via the overrides below). Each of x/work/y/lowScratch gets a 38 KiB region | ||
| 327 | +// (CAXPY_DENSE_TILE_BYTES, reused here rather than duplicated: the strided | ||
| 328 | +// blocked layout only needs CAXPY_STRIDED_TILE_COUNT*CAXPY_BLOCKED_FLOATS=16 KiB | ||
| 329 | +// per region, but the historical layout budgets a full 38 KiB slot per region | ||
| 330 | +// to match the dense kernel's tile size, leaving headroom after each xDense/ | ||
| 331 | +// workDense sub-offset). The offset-table region follows after 4 such slots. | ||
| 332 | +constexpr uint32_t CAXPY_STRIDED_UB_X_BYTES = 0 * CAXPY_DENSE_TILE_BYTES; | ||
| 333 | +constexpr uint32_t CAXPY_STRIDED_UB_WORK_BYTES = 1 * CAXPY_DENSE_TILE_BYTES; | ||
| 334 | +constexpr uint32_t CAXPY_STRIDED_UB_Y_BYTES = 2 * CAXPY_DENSE_TILE_BYTES; | ||
| 335 | +constexpr uint32_t CAXPY_STRIDED_UB_LOW_SCRATCH_BYTES = 3 * CAXPY_DENSE_TILE_BYTES; | ||
| 336 | +constexpr uint32_t CAXPY_STRIDED_UB_OFFSET_TABLES_BYTES = 4 * CAXPY_DENSE_TILE_BYTES; | ||
| 337 | +// Offset table sub-regions, laid out consecutively starting at | ||
| 338 | +// CAXPY_STRIDED_UB_OFFSET_TABLES_BYTES: xNormalizeOffset, then computeOffset | ||
| 339 | +// (sized for one CAXPY_INTERLEAVE_COUNT sub-chunk: convertRepeat<=2, one | ||
| 340 | +// offset pair per element -> 2*8=16 words, rounded up to the 2*64 historical | ||
| 341 | +// allocation for headroom), then yGatherOffset, then storeOffset. | ||
| 342 | +constexpr uint32_t CAXPY_COMPUTE_OFFSET_COUNT = 2 * 64; | ||
| 343 | +constexpr uint32_t CAXPY_STRIDED_UB_COMPUTE_OFFSET_BYTES = | ||
| 344 | + CAXPY_STRIDED_UB_OFFSET_TABLES_BYTES + CAXPY_X_OFFSET_COUNT * sizeof(uint32_t); | ||
| 345 | +constexpr uint32_t CAXPY_STRIDED_UB_Y_GATHER_OFFSET_BYTES = | ||
| 346 | + CAXPY_STRIDED_UB_COMPUTE_OFFSET_BYTES + CAXPY_COMPUTE_OFFSET_COUNT * sizeof(uint32_t); | ||
| 347 | +constexpr uint32_t CAXPY_STRIDED_UB_STORE_OFFSET_BYTES = | ||
| 348 | + CAXPY_STRIDED_UB_Y_GATHER_OFFSET_BYTES + CAXPY_SPAN_OFFSET_COUNT * sizeof(uint32_t); | ||
| 349 | +constexpr uint32_t CAXPY_STRIDED_UB_Y_COMPACT_BYTES = | ||
| 350 | + CAXPY_STRIDED_UB_STORE_OFFSET_BYTES + CAXPY_SPAN_OFFSET_COUNT * sizeof(uint32_t); | ||
| 351 | +static_assert( | ||
| 352 | + CAXPY_STRIDED_UB_OFFSET_TABLES_BYTES == 152 * 1024, | ||
| 353 | + "CAXPY strided offset-table region start moved; update disjoint/shared_ub_layout overrides"); | ||
| 354 | +static_assert( | ||
| 355 | + CAXPY_STRIDED_UB_Y_COMPACT_BYTES + CAXPY_STRIDED_TILE_COUNT * 2 * sizeof(float) <= CAXPY_UB_BUDGET_BYTES, | ||
| 356 | + "CAXPY strided UB layout exceeds the 910B3 Unified Buffer budget"); | ||
| 357 | + | ||
| 358 | +struct CaxpyStridedUb { | ||
| 359 | + __ubuf__ float* x; | ||
| 360 | + __ubuf__ float* work; | ||
| 361 | + __ubuf__ float* y; | ||
| 362 | + __ubuf__ uint32_t* lowScratch; | ||
| 363 | + __ubuf__ float* xDense; | ||
| 364 | + __ubuf__ float* yDense; | ||
| 365 | + __ubuf__ float* workDense; | ||
| 366 | + __ubuf__ uint32_t* xNormalizeOffset; | ||
| 367 | + __ubuf__ uint32_t* computeOffset; | ||
| 368 | + __ubuf__ uint32_t* yGatherOffset; | ||
| 369 | + __ubuf__ uint32_t* storeOffset; | ||
| 370 | + __ubuf__ float* yCompact; | ||
| 371 | +}; | ||
| 372 | + | ||
| 373 | +__aicore__ __inline__ __attribute__((always_inline)) CaxpyStridedUb strided_ub_layout() | ||
| 374 | +{ | ||
| 375 | + CaxpyStridedUb ub; | ||
| 376 | + ub.x = reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_STRIDED_UB_X_BYTES); | ||
| 377 | + ub.work = reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_STRIDED_UB_WORK_BYTES); | ||
| 378 | + ub.y = reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_STRIDED_UB_Y_BYTES); | ||
| 379 | + ub.lowScratch = reinterpret_cast<__ubuf__ uint32_t*>((uintptr_t)CAXPY_STRIDED_UB_LOW_SCRATCH_BYTES); | ||
| 380 | + ub.xDense = ub.x + CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS; | ||
| 381 | + ub.yDense = ub.y + CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS; | ||
| 382 | + ub.workDense = ub.work + CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS; | ||
| 383 | + ub.xNormalizeOffset = reinterpret_cast<__ubuf__ uint32_t*>((uintptr_t)CAXPY_STRIDED_UB_OFFSET_TABLES_BYTES); | ||
| 384 | + ub.computeOffset = reinterpret_cast<__ubuf__ uint32_t*>((uintptr_t)CAXPY_STRIDED_UB_COMPUTE_OFFSET_BYTES); | ||
| 385 | + ub.yGatherOffset = reinterpret_cast<__ubuf__ uint32_t*>((uintptr_t)CAXPY_STRIDED_UB_Y_GATHER_OFFSET_BYTES); | ||
| 386 | + ub.storeOffset = reinterpret_cast<__ubuf__ uint32_t*>((uintptr_t)CAXPY_STRIDED_UB_STORE_OFFSET_BYTES); | ||
| 387 | + ub.yCompact = reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_STRIDED_UB_Y_COMPACT_BYTES); | ||
| 388 | + return ub; | ||
| 389 | +} | ||
| 390 | + | ||
| 391 | +__aicore__ __inline__ __attribute__((always_inline)) void strided_preload_offsets( | ||
| 392 | + CaxpyStridedUb& ub, __gm__ uint32_t* gmAug, bool useXSpanGather, bool useYSpanGather) | ||
| 393 | +{ | ||
| 394 | + copy_vec_gm2ub_uint32(ub.computeOffset, gmAug, CAXPY_COMPUTE_OFFSET_COUNT); | ||
| 395 | + if (useXSpanGather) { | ||
| 396 | + copy_vec_gm2ub_uint32(ub.xNormalizeOffset, gmAug + CAXPY_MAX_DATA_COUNT, CAXPY_X_OFFSET_COUNT); | ||
| 397 | + } | ||
| 398 | + if (useYSpanGather) { | ||
| 399 | + copy_vec_gm2ub_uint32(ub.yGatherOffset, gmAug + CAXPY_Y_GATHER_OFFSET_START, CAXPY_SPAN_OFFSET_COUNT); | ||
| 400 | + copy_vec_gm2ub_uint32(ub.storeOffset, gmAug + CAXPY_STORE_OFFSET_START, CAXPY_SPAN_OFFSET_COUNT); | ||
| 401 | + } | ||
| 402 | +} | ||
| 403 | + | ||
| 404 | +__aicore__ __inline__ __attribute__((always_inline)) bool strided_load_x_span( | ||
| 405 | + CaxpyStridedUb& ub, __gm__ float* gmX, uint32_t logicalOffset, uint32_t totalCount, uint32_t count, int64_t incx, | ||
| 406 | + bool useSpanGather) | ||
| 407 | +{ | ||
| 408 | + if (!useSpanGather) { | ||
| 409 | + return copy_complex_gm2ub_signed(ub.x, ub.xDense, gmX, logicalOffset, totalCount, count, incx); | ||
| 410 | + } | ||
| 411 | + const uint32_t increment = abs_increment(incx); | ||
| 412 | + const uint32_t physicalOffset = | ||
| 413 | + incx > 0 ? logicalOffset * increment : (totalCount - logicalOffset - count) * increment; | ||
| 414 | + copy_vec_gm2ub( | ||
| 415 | + reinterpret_cast<__ubuf__ float*>(ub.lowScratch), gmX + physicalOffset * 2, (count - 1) * increment * 2 + 2); | ||
| 416 | + return false; | ||
| 417 | +} | ||
| 418 | + | ||
| 419 | +__aicore__ __inline__ __attribute__((always_inline)) void strided_normalize_inputs( | ||
| 420 | + CaxpyStridedUb& ub, uint32_t count, int64_t incx, int64_t incy, bool xDense, bool yDense, bool useXSpanGather, | ||
| 421 | + bool useYSpanGather) | ||
| 422 | +{ | ||
| 423 | + if (useXSpanGather) { | ||
| 424 | + vgather( | ||
| 425 | + reinterpret_cast<__ubuf__ uint32_t*>(ub.x), ub.xNormalizeOffset, (uintptr_t)ub.lowScratch, 8, | ||
| 426 | + (count + 7) / 8); | ||
| 427 | + } | ||
| 428 | + if (useYSpanGather) { | ||
| 429 | + vgather( | ||
| 430 | + reinterpret_cast<__ubuf__ uint32_t*>(ub.yCompact), ub.yGatherOffset, (uintptr_t)ub.y, 8, | ||
| 431 | + (count * 2 + 63) / 64); | ||
| 432 | + } | ||
| 433 | + const bool normalizeX = xDense || incx < 0; | ||
| 434 | + const bool normalizeY = incy != 1 && !useYSpanGather && (yDense || incy < 0); | ||
| 435 | + if (!normalizeX && !normalizeY) | ||
| 436 | + return; | ||
| 437 | + set_flag(PIPE_V, PIPE_S, EVENT_ID0); | ||
| 438 | + wait_flag(PIPE_V, PIPE_S, EVENT_ID0); | ||
| 439 | + if (normalizeX) | ||
| 440 | + normalize_loaded_complexes(ub.x, ub.xDense, count, xDense, incx < 0); | ||
| 441 | + if (normalizeY) | ||
| 442 | + normalize_loaded_complexes(ub.y, ub.yDense, count, yDense, incy < 0); | ||
| 443 | + set_flag(PIPE_S, PIPE_V, EVENT_ID0); | ||
| 444 | + wait_flag(PIPE_S, PIPE_V, EVENT_ID0); | ||
| 445 | +} | ||
| 446 | + | ||
| 447 | +__aicore__ __inline__ __attribute__((always_inline)) void strided_compute_tile( | ||
| 448 | + CaxpyStridedUb& ub, float real, float imag, uint32_t count, int64_t incy, bool useYSpanGather) | ||
| 449 | +{ | ||
| 450 | + auto workReal = ub.workDense; | ||
| 451 | + auto workImag = workReal + CAXPY_UB_COMPLEX_CAPACITY; | ||
| 452 | + for (uint32_t converted = 0; converted < count; converted += CAXPY_INTERLEAVE_COUNT) { | ||
| 453 | + const uint32_t convertCount = | ||
| 454 | + count - converted > CAXPY_INTERLEAVE_COUNT ? CAXPY_INTERLEAVE_COUNT : count - converted; | ||
| 455 | + const uint32_t convertRepeat = (convertCount + 7) / 8; | ||
| 456 | + const uint32_t computeRepeat = (convertCount + 63) / 64; | ||
| 457 | + const uint32_t blockedOffset = converted * CAXPY_BLOCKED_FLOATS; | ||
| 458 | + auto xReal = ub.x + blockedOffset; | ||
| 459 | + auto xImag = xReal + CAXPY_UB_COMPLEX_CAPACITY; | ||
| 460 | + vreducev2( | ||
| 461 | + reinterpret_cast<__ubuf__ uint32_t*>(workReal), reinterpret_cast<__ubuf__ uint32_t*>(xReal), nullptr, | ||
| 462 | + convertRepeat, 1, 1, 8, 8); | ||
| 463 | + vreducev2( | ||
| 464 | + reinterpret_cast<__ubuf__ uint32_t*>(workImag), reinterpret_cast<__ubuf__ uint32_t*>(xReal), nullptr, | ||
| 465 | + convertRepeat, 1, 2, 8, 8); | ||
| 466 | + pipe_barrier(PIPE_V); | ||
| 467 | + caxpy_compute_planar(xReal, xImag, workReal, workImag, real, imag, computeRepeat); | ||
| 468 | + vgather( | ||
| 469 | + reinterpret_cast<__ubuf__ uint32_t*>(ub.work + blockedOffset), ub.computeOffset, (uintptr_t)xReal, 8, | ||
| 470 | + convertRepeat); | ||
| 471 | + pipe_barrier(PIPE_V); | ||
| 472 | + if (incy != 1 && !useYSpanGather) { | ||
| 473 | + vadd( | ||
| 474 | + ub.work + blockedOffset, ub.work + blockedOffset, ub.y + blockedOffset, convertRepeat, 1, 1, 1, 8, 8, | ||
| 475 | + 8); | ||
| 476 | + } | ||
| 477 | + } | ||
| 478 | +} | ||
| 479 | + | ||
| 480 | +__aicore__ __inline__ __attribute__((always_inline)) CaxpyStridedUb disjoint_ub_layout(uint32_t index) | ||
| 481 | +{ | ||
| 482 | + CaxpyStridedUb ub = strided_ub_layout(); | ||
| 483 | + ub.x = reinterpret_cast<__ubuf__ float*>( | ||
| 484 | + (uintptr_t)(index == 0 ? CAXPY_STRIDED_UB_X_BYTES : CAXPY_STRIDED_UB_WORK_BYTES)); | ||
| 485 | + ub.work = reinterpret_cast<__ubuf__ float*>( | ||
| 486 | + (uintptr_t)(index == 0 ? CAXPY_STRIDED_UB_Y_BYTES : CAXPY_STRIDED_UB_LOW_SCRATCH_BYTES)); | ||
| 487 | + ub.xDense = ub.x + CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS; | ||
| 488 | + ub.workDense = ub.work + CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS; | ||
| 489 | + return ub; | ||
| 490 | +} | ||
| 491 | + | ||
| 492 | +__aicore__ __inline__ __attribute__((always_inline)) void disjoint_load_tile( | ||
| 493 | + CaxpyStridedUb& ub, __gm__ float* gmX, uint32_t logicalOffset, uint32_t totalCount, uint32_t count, | ||
| 494 | + int64_t incx, uint32_t eventId) | ||
| 495 | +{ | ||
| 496 | + copy_complex_gm2ub_signed(ub.x, ub.xDense, gmX, logicalOffset, totalCount, count, incx); | ||
| 497 | + set_flag(PIPE_MTE2, PIPE_V, eventId); | ||
| 498 | +} | ||
| 499 | + | ||
| 500 | +__aicore__ __inline__ __attribute__((always_inline)) void disjoint_compute_tile( | ||
| 501 | + CaxpyStridedUb& ub, float alphaReal, float alphaImag, uint32_t count, int64_t incx, uint32_t eventId) | ||
| 502 | +{ | ||
| 503 | + wait_flag(PIPE_MTE2, PIPE_V, eventId); | ||
| 504 | + strided_normalize_inputs(ub, count, incx, 1, false, false, false, false); | ||
| 505 | + strided_compute_tile(ub, alphaReal, alphaImag, count, 1, false); | ||
| 506 | +} | ||
| 507 | + | ||
| 508 | +__aicore__ __inline__ __attribute__((always_inline)) void strided_store_tile( | ||
| 509 | + CaxpyStridedUb& ub, __gm__ float* gmY, uint32_t logicalOffset, uint32_t totalCount, uint32_t count, | ||
| 510 | + int64_t incy, uint32_t eventId) | ||
| 511 | +{ | ||
| 512 | + __ubuf__ float* source = ub.work; | ||
| 513 | + if (incy == 1) { | ||
| 514 | + const uint32_t repeat = (count * 2 + 63) / 64; | ||
| 515 | + vgather( | ||
| 516 | + reinterpret_cast<__ubuf__ uint32_t*>(ub.workDense), ub.storeOffset, (uintptr_t)ub.work, 8, repeat); | ||
| 517 | + pipe_barrier(PIPE_V); | ||
| 518 | + source = ub.workDense; | ||
| 519 | + } | ||
| 520 | + set_flag(PIPE_V, PIPE_MTE3, eventId); | ||
| 521 | + wait_flag(PIPE_V, PIPE_MTE3, eventId); | ||
| 522 | + copy_complex_ub2gm_signed(gmY, source, logicalOffset, totalCount, count, incy); | ||
| 523 | + set_flag(PIPE_MTE3, PIPE_MTE2, eventId); | ||
| 524 | +} | ||
| 525 | + | ||
| 526 | +__aicore__ __inline__ __attribute__((always_inline)) void disjoint_preload_offsets( | ||
| 527 | + CaxpyStridedUb& ping, __gm__ uint32_t* gmAug, int64_t incy) | ||
| 528 | +{ | ||
| 529 | + copy_vec_gm2ub_uint32(ping.computeOffset, gmAug, CAXPY_COMPUTE_OFFSET_COUNT); | ||
| 530 | + if (incy == 1) | ||
| 531 | + copy_vec_gm2ub_uint32( | ||
| 532 | + ping.storeOffset, gmAug + CAXPY_STORE_OFFSET_START, CAXPY_SPAN_OFFSET_COUNT); | ||
| 533 | +} | ||
| 534 | + | ||
| 535 | +__aicore__ __inline__ __attribute__((always_inline)) void disjoint_load_next_if_any( | ||
| 536 | + CaxpyStridedUb& ping, CaxpyStridedUb& pong, __gm__ float* gmX, uint32_t logicalOffset, | ||
| 537 | + uint32_t totalComplexCount, uint32_t complexCount, uint32_t tileCapacity, int64_t incx, uint32_t tile, | ||
| 538 | + uint32_t tileCount, uint32_t current) | ||
| 539 | +{ | ||
| 540 | + if (tile + 1 >= tileCount) | ||
| 541 | + return; | ||
| 542 | + const uint32_t next = 1 - current; | ||
| 543 | + const uint32_t nextEvent = next == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 544 | + CaxpyStridedUb& nextUb = next == 0 ? ping : pong; | ||
| 545 | + const uint32_t nextOffset = (tile + 1) * tileCapacity; | ||
| 546 | + const uint32_t nextCount = | ||
| 547 | + complexCount - nextOffset > tileCapacity ? tileCapacity : complexCount - nextOffset; | ||
| 548 | + wait_flag(PIPE_MTE3, PIPE_MTE2, nextEvent); | ||
| 549 | + disjoint_load_tile( | ||
| 550 | + nextUb, gmX, logicalOffset + nextOffset, totalComplexCount, nextCount, incx, nextEvent); | ||
| 551 | +} | ||
| 552 | + | ||
| 553 | +__aicore__ __inline__ __attribute__((always_inline)) void run_disjoint_pipeline( | ||
| 554 | + __gm__ float* gmX, __gm__ uint32_t* gmAug, __gm__ float* gmY, float alphaReal, float alphaImag, | ||
| 555 | + uint32_t logicalOffset, uint32_t totalComplexCount, uint32_t complexCount, int64_t incx, int64_t incy) | ||
| 556 | +{ | ||
| 557 | + CaxpyStridedUb ping = disjoint_ub_layout(0); | ||
| 558 | + CaxpyStridedUb pong = disjoint_ub_layout(1); | ||
| 559 | + disjoint_preload_offsets(ping, gmAug, incy); | ||
| 560 | + | ||
| 561 | + set_atomic_add(); | ||
| 562 | + set_atomic_f32(); | ||
| 563 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 564 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 565 | + | ||
| 566 | + constexpr uint32_t tileCapacity = CAXPY_STRIDED_TILE_COUNT; | ||
| 567 | + const uint32_t tileCount = (complexCount + tileCapacity - 1) / tileCapacity; | ||
| 568 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 569 | + const uint32_t firstCount = complexCount > tileCapacity ? tileCapacity : complexCount; | ||
| 570 | + disjoint_load_tile(ping, gmX, logicalOffset, totalComplexCount, firstCount, incx, EVENT_ID0); | ||
| 571 | + | ||
| 572 | + for (uint32_t tile = 0; tile < tileCount; ++tile) { | ||
| 573 | + const uint32_t current = tile & 1; | ||
| 574 | + const uint32_t currentEvent = current == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 575 | + CaxpyStridedUb& currentUb = current == 0 ? ping : pong; | ||
| 576 | + const uint32_t currentOffset = tile * tileCapacity; | ||
| 577 | + const uint32_t currentCount = | ||
| 578 | + complexCount - currentOffset > tileCapacity ? tileCapacity : complexCount - currentOffset; | ||
| 579 | + | ||
| 580 | + disjoint_load_next_if_any( | ||
| 581 | + ping, pong, gmX, logicalOffset, totalComplexCount, complexCount, tileCapacity, incx, tile, tileCount, | ||
| 582 | + current); | ||
| 583 | + | ||
| 584 | + disjoint_compute_tile(currentUb, alphaReal, alphaImag, currentCount, incx, currentEvent); | ||
| 585 | + strided_store_tile( | ||
| 586 | + currentUb, gmY, logicalOffset + currentOffset, totalComplexCount, currentCount, incy, currentEvent); | ||
| 587 | + } | ||
| 588 | + | ||
| 589 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 590 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 591 | + pipe_barrier(PIPE_ALL); | ||
| 592 | + set_atomic_none(); | ||
| 593 | +} | ||
| 594 | + | ||
| 595 | +__aicore__ __inline__ __attribute__((always_inline)) CaxpyStridedUb shared_ub_layout(uint32_t index) | ||
| 596 | +{ | ||
| 597 | + CaxpyStridedUb ub = strided_ub_layout(); | ||
| 598 | + if (index != 0) { | ||
| 599 | + ub.work = reinterpret_cast<__ubuf__ float*>((uintptr_t)CAXPY_STRIDED_UB_Y_BYTES); | ||
| 600 | + ub.workDense = ub.work + CAXPY_STRIDED_TILE_COUNT * CAXPY_BLOCKED_FLOATS; | ||
| 601 | + } | ||
| 602 | + return ub; | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | +__aicore__ __inline__ __attribute__((always_inline)) void shared_load_tile( | ||
| 606 | + CaxpyStridedUb& ub, __gm__ float* gmX, uint32_t logicalOffset, uint32_t totalCount, uint32_t count, | ||
| 607 | + int64_t incx, uint32_t waitEvent, uint32_t readyEvent, bool waitForRaw) | ||
| 608 | +{ | ||
| 609 | + // MTE3 owns each work buffer after a store. Hand it back through MTE2, | ||
| 610 | + // which is the event direction used by the stable dense pipeline. | ||
| 611 | + wait_flag(PIPE_MTE3, PIPE_MTE2, readyEvent); | ||
| 612 | + if (waitForRaw) | ||
| 613 | + wait_flag(PIPE_V, PIPE_MTE2, waitEvent); | ||
| 614 | + strided_load_x_span(ub, gmX, logicalOffset, totalCount, count, incx, true); | ||
| 615 | + set_flag(PIPE_MTE2, PIPE_V, readyEvent); | ||
| 616 | +} | ||
| 617 | + | ||
| 618 | +__aicore__ __inline__ __attribute__((always_inline)) void shared_compute_tile( | ||
| 619 | + CaxpyStridedUb& ub, float alphaReal, float alphaImag, uint32_t count, int64_t incx, uint32_t eventId) | ||
| 620 | +{ | ||
| 621 | + wait_flag(PIPE_MTE2, PIPE_V, eventId); | ||
| 622 | + strided_normalize_inputs(ub, count, incx, 1, false, false, true, false); | ||
| 623 | + // raw span is dead immediately after gather/normalization. Release it to | ||
| 624 | + // MTE2 before the remaining complex arithmetic finishes on PIPE_V. | ||
| 625 | + set_flag(PIPE_V, PIPE_MTE2, eventId); | ||
| 626 | + strided_compute_tile(ub, alphaReal, alphaImag, count, 1, false); | ||
| 627 | +} | ||
| 628 | + | ||
| 629 | +__aicore__ __inline__ __attribute__((always_inline)) void shared_tile_bounds( | ||
| 630 | + uint32_t complexCount, uint32_t tileCount, uint32_t tile, uint32_t tileCapacity, uint32_t& tileOffset, | ||
| 631 | + uint32_t& tileLength) | ||
| 632 | +{ | ||
| 633 | + // A final tile shorter than the interleave width is mis-staged by the span-read | ||
| 634 | + // path, so avoid producing one: when the natural schedule would leave such a | ||
| 635 | + // remainder, split the last two tiles evenly instead. Coverage stays exact and | ||
| 636 | + // contiguous and no tile exceeds the capacity, so buffer sizing is unchanged. | ||
| 637 | + // tileCapacity is a runtime value (see run_shared_pipeline) that is always | ||
| 638 | + // >= CAXPY_SHARED_MIN_USABLE_TILE_CAPACITY (CAXPY_INTERLEAVE_COUNT + 1), so | ||
| 639 | + // this rebalancing always has room to produce two tiles of at least | ||
| 640 | + // CAXPY_INTERLEAVE_COUNT/2 elements each. | ||
| 641 | + const uint32_t remainder = complexCount - (tileCount - 1) * tileCapacity; | ||
| 642 | + if (tileCount >= 2 && remainder < CAXPY_INTERLEAVE_COUNT + 1) { | ||
| 643 | + const uint32_t splitBase = (tileCount - 2) * tileCapacity; | ||
| 644 | + if (tile < tileCount - 2) { | ||
| 645 | + tileOffset = tile * tileCapacity; | ||
| 646 | + tileLength = tileCapacity; | ||
| 647 | + return; | ||
| 648 | + } | ||
| 649 | + const uint32_t splitTotal = complexCount - splitBase; | ||
| 650 | + const uint32_t firstHalf = (splitTotal + 1) / 2; | ||
| 651 | + if (tile == tileCount - 2) { | ||
| 652 | + tileOffset = splitBase; | ||
| 653 | + tileLength = firstHalf; | ||
| 654 | + } else { | ||
| 655 | + tileOffset = splitBase + firstHalf; | ||
| 656 | + tileLength = splitTotal - firstHalf; | ||
| 657 | + } | ||
| 658 | + return; | ||
| 659 | + } | ||
| 660 | + tileOffset = tile * tileCapacity; | ||
| 661 | + tileLength = complexCount - tileOffset > tileCapacity ? tileCapacity : complexCount - tileOffset; | ||
| 662 | +} | ||
| 663 | + | ||
| 664 | +__aicore__ __inline__ __attribute__((always_inline)) void shared_preload_offsets( | ||
| 665 | + CaxpyStridedUb& ping, __gm__ uint32_t* gmAug, int64_t incy) | ||
| 666 | +{ | ||
| 667 | + strided_preload_offsets(ping, gmAug, true, false); | ||
| 668 | + if (incy == 1) | ||
| 669 | + copy_vec_gm2ub_uint32( | ||
| 670 | + ping.storeOffset, gmAug + CAXPY_STORE_OFFSET_START, CAXPY_SPAN_OFFSET_COUNT); | ||
| 671 | +} | ||
| 672 | + | ||
| 673 | +__aicore__ __inline__ __attribute__((always_inline)) void shared_load_next_if_any( | ||
| 674 | + CaxpyStridedUb& ping, CaxpyStridedUb& pong, __gm__ float* gmX, uint32_t logicalOffset, | ||
| 675 | + uint32_t totalComplexCount, uint32_t complexCount, uint32_t tileCapacity, int64_t incx, uint32_t tile, | ||
| 676 | + uint32_t tileCount, uint32_t current) | ||
| 677 | +{ | ||
| 678 | + if (tile + 1 >= tileCount) | ||
| 679 | + return; | ||
| 680 | + const uint32_t next = 1 - current; | ||
| 681 | + const uint32_t nextEvent = next == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 682 | + const uint32_t currentEvent = current == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 683 | + CaxpyStridedUb& nextUb = next == 0 ? ping : pong; | ||
| 684 | + uint32_t nextOffset = 0; | ||
| 685 | + uint32_t nextCount = 0; | ||
| 686 | + shared_tile_bounds(complexCount, tileCount, tile + 1, tileCapacity, nextOffset, nextCount); | ||
| 687 | + shared_load_tile( | ||
| 688 | + nextUb, gmX, logicalOffset + nextOffset, totalComplexCount, nextCount, incx, currentEvent, nextEvent, true); | ||
| 689 | +} | ||
| 690 | + | ||
| 691 | +__aicore__ __inline__ __attribute__((always_inline)) void run_shared_pipeline( | ||
| 692 | + __gm__ float* gmX, __gm__ uint32_t* gmAug, __gm__ float* gmY, float alphaReal, float alphaImag, | ||
| 693 | + uint32_t logicalOffset, uint32_t totalComplexCount, uint32_t complexCount, int64_t incx, int64_t incy, | ||
| 694 | + uint32_t tileCapacity) | ||
| 695 | +{ | ||
| 696 | + CaxpyStridedUb ping = shared_ub_layout(0); | ||
| 697 | + CaxpyStridedUb pong = shared_ub_layout(1); | ||
| 698 | + shared_preload_offsets(ping, gmAug, incy); | ||
| 699 | + | ||
| 700 | + set_atomic_add(); | ||
| 701 | + set_atomic_f32(); | ||
| 702 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 703 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 704 | + | ||
| 705 | + const uint32_t tileCount = (complexCount + tileCapacity - 1) / tileCapacity; | ||
| 706 | + uint32_t firstOffset = 0; | ||
| 707 | + uint32_t firstCount = 0; | ||
| 708 | + shared_tile_bounds(complexCount, tileCount, 0, tileCapacity, firstOffset, firstCount); | ||
| 709 | + shared_load_tile( | ||
| 710 | + ping, gmX, logicalOffset + firstOffset, totalComplexCount, firstCount, incx, EVENT_ID0, EVENT_ID0, false); | ||
| 711 | + | ||
| 712 | + for (uint32_t tile = 0; tile < tileCount; ++tile) { | ||
| 713 | + const uint32_t current = tile & 1; | ||
| 714 | + const uint32_t currentEvent = current == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 715 | + CaxpyStridedUb& currentUb = current == 0 ? ping : pong; | ||
| 716 | + uint32_t currentOffset = 0; | ||
| 717 | + uint32_t currentCount = 0; | ||
| 718 | + shared_tile_bounds(complexCount, tileCount, tile, tileCapacity, currentOffset, currentCount); | ||
| 719 | + | ||
| 720 | + shared_compute_tile(currentUb, alphaReal, alphaImag, currentCount, incx, currentEvent); | ||
| 721 | + | ||
| 722 | + shared_load_next_if_any( | ||
| 723 | + ping, pong, gmX, logicalOffset, totalComplexCount, complexCount, tileCapacity, incx, tile, tileCount, | ||
| 724 | + current); | ||
| 725 | + | ||
| 726 | + strided_store_tile( | ||
| 727 | + currentUb, gmY, logicalOffset + currentOffset, totalComplexCount, currentCount, incy, currentEvent); | ||
| 728 | + } | ||
| 729 | + | ||
| 730 | + const uint32_t lastRawEvent = ((tileCount - 1) & 1) == 0 ? EVENT_ID0 : EVENT_ID1; | ||
| 731 | + wait_flag(PIPE_V, PIPE_MTE2, lastRawEvent); | ||
| 732 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 733 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID1); | ||
| 734 | + pipe_barrier(PIPE_ALL); | ||
| 735 | + set_atomic_none(); | ||
| 736 | +} | ||
| 737 | + | ||
| 738 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_strided_shared_run( | ||
| 739 | + __gm__ float* gmX, __gm__ uint32_t* gmAug, __gm__ float* gmY, float alphaReal, float alphaImag, | ||
| 740 | + uint32_t logicalOffset, uint32_t totalComplexCount, uint32_t complexCount, int64_t incx, int64_t incy, | ||
| 741 | + uint32_t tileCapacity) | ||
| 742 | +{ | ||
| 743 | + run_shared_pipeline( | ||
| 744 | + gmX, gmAug, gmY, alphaReal, alphaImag, logicalOffset, totalComplexCount, complexCount, incx, incy, | ||
| 745 | + tileCapacity); | ||
| 746 | +} | ||
| 747 | + | ||
| 748 | +__aicore__ __inline__ __attribute__((always_inline)) void caxpy_strided_disjoint_run( | ||
| 749 | + __gm__ float* gmX, __gm__ uint32_t* gmAug, __gm__ float* gmY, float alphaReal, float alphaImag, | ||
| 750 | + uint32_t logicalOffset, uint32_t totalComplexCount, uint32_t complexCount, int64_t incx, int64_t incy, | ||
| 751 | + uint32_t tileCapacity) | ||
| 752 | +{ | ||
| 753 | + // The disjoint pipeline never span-reads, so it has no UB-buffer-fit | ||
| 754 | + // constraint to shrink its tile for; it always uses the fixed | ||
| 755 | + // CAXPY_STRIDED_TILE_COUNT capacity and ignores the runtime tileCapacity | ||
| 756 | + // value threaded through only so both kernel entries share one signature. | ||
| 757 | + (void)tileCapacity; | ||
| 758 | + run_disjoint_pipeline( | ||
| 759 | + gmX, gmAug, gmY, alphaReal, alphaImag, logicalOffset, totalComplexCount, complexCount, incx, incy); | ||
| 760 | +} | ||
| 761 | + | ||
| 195 | 762 | ||
| 196 | 763 | ||
| 197 | -extern "C" __global__ __aicore__ __vector__ void caxpy(__gm__ float *__restrict__ x, __gm__ uint32_t *__restrict__ aug, | 764 | +extern "C" __global__ __aicore__ __vector__ void caxpy( |
| 198 | - __gm__ float *__restrict__ y, __gm__ float *__restrict__ tiling_gm) | 765 | + __gm__ float* __restrict__ x, __gm__ uint32_t* __restrict__ aug, __gm__ float* __restrict__ y, |
| 766 | + __gm__ float* __restrict__ tiling_gm) | ||
| 199 | { | 767 | { |
| 200 | 768 | ||
| 201 | auto vec_idx = AscendC::GetBlockIdx(); | 769 | auto vec_idx = AscendC::GetBlockIdx(); |
| @@ -203,25 +771,156 @@ extern "C" __global__ __aicore__ __vector__ void caxpy(__gm__ float *__restrict_ | |||
| 203 | set_mask_norm(); | 771 | set_mask_norm(); |
| 204 | set_vector_mask((uint64_t)-1, (uint64_t)-1); | 772 | set_vector_mask((uint64_t)-1, (uint64_t)-1); |
| 205 | 773 | ||
| 206 | - auto tiling_buf = reinterpret_cast<__gm__ uint8_t *>(tiling_gm); | 774 | + auto tiling = reinterpret_cast<__gm__ CaxpyTilingData*>(tiling_gm); |
| 207 | 775 | ||
| 208 | - uint32_t n = (*(__gm__ uint32_t *)((__gm__ uint8_t *)tiling_buf)); | 776 | + const uint32_t numBlocks = tiling->activeBlocks; |
| 209 | - float alphaReal = (*(__gm__ float *)((__gm__ uint8_t *)tiling_buf + 4)); | 777 | + const uint32_t totalN = tiling->totalN; |
| 210 | - float alphaImag = (*(__gm__ float *)((__gm__ uint8_t *)tiling_buf + 8)); | 778 | + if (numBlocks == 0 || vec_idx >= numBlocks) { |
| 211 | - | 779 | + return; |
| 212 | - uint32_t offset = (*(__gm__ uint32_t *)((__gm__ uint8_t *)tiling_buf + 12 + 4 * vec_idx)); | ||
| 213 | - uint32_t calNum = (*(__gm__ uint32_t *)((__gm__ uint8_t *)tiling_buf + 12 + 40 * 4 + 4 * vec_idx)); | ||
| 214 | - | ||
| 215 | - if (calNum > 0) { | ||
| 216 | - caxpy_aiv(x, aug, y, alphaReal, alphaImag, offset, calNum); | ||
| 217 | } | 780 | } |
| 781 | + const uint32_t perBlock = totalN / numBlocks; | ||
| 782 | + const uint32_t remainder = totalN % numBlocks; | ||
| 783 | + const uint32_t complexCount = perBlock + (vec_idx < remainder ? 1 : 0); | ||
| 784 | + const uint32_t complexOffset = vec_idx * perBlock + (vec_idx < remainder ? vec_idx : remainder); | ||
| 785 | + caxpy_dense_run( | ||
| 786 | + x, aug, y, tiling->alphaReal, tiling->alphaImag, complexOffset * 2, complexCount * 2); | ||
| 218 | 787 | ||
| 219 | } | 788 | } |
| 220 | 789 | ||
| 221 | -void caxpy_kernel_do(GM_ADDR x, GM_ADDR maskBuf, GM_ADDR y, GM_ADDR workSpace, GM_ADDR tilingGm, | 790 | +#if __DAV_C220_VEC__ |
| 222 | - uint32_t numBlocks, void *stream) | 791 | +#define DEFINE_CAXPY_STRIDED_KERNEL(KERNEL, PIPELINE) \ |
| 792 | +extern "C" __global__ __aicore__ __vector__ void KERNEL( \ | ||
| 793 | + __gm__ float* __restrict__ x, __gm__ uint32_t* __restrict__ aug, __gm__ float* __restrict__ y, \ | ||
| 794 | + __gm__ float* __restrict__ tilingGm) \ | ||
| 795 | +{ \ | ||
| 796 | + const uint32_t vecIdx = AscendC::GetBlockIdx(); \ | ||
| 797 | + set_mask_norm(); set_vector_mask((uint64_t)-1, (uint64_t)-1); \ | ||
| 798 | + auto tiling = reinterpret_cast<__gm__ CaxpyTilingData*>(tilingGm); \ | ||
| 799 | + const uint32_t numBlocks = tiling->activeBlocks; \ | ||
| 800 | + const float alphaReal = tiling->alphaReal; \ | ||
| 801 | + const float alphaImag = tiling->alphaImag; \ | ||
| 802 | + const uint32_t totalN = tiling->totalN; \ | ||
| 803 | + const int64_t incx = tiling->incx; \ | ||
| 804 | + const int64_t incy = tiling->incy; \ | ||
| 805 | + const uint32_t tileCapacity = tiling->sharedTileCapacity; \ | ||
| 806 | + if (numBlocks == 0) { return; } \ | ||
| 807 | + const uint32_t waveCapacity = numBlocks * CAXPY_STRIDED_MAX_PER_BLOCK_PER_WAVE; \ | ||
| 808 | + for (uint32_t logicalBase = 0; logicalBase < totalN; logicalBase += waveCapacity) { \ | ||
| 809 | + const uint32_t waveN = totalN - logicalBase > waveCapacity ? waveCapacity : totalN - logicalBase; \ | ||
| 810 | + const uint32_t waveBlocks = numBlocks < waveN ? numBlocks : waveN; \ | ||
| 811 | + if (vecIdx >= waveBlocks) { continue; } \ | ||
| 812 | + const uint32_t rowNum = waveN / waveBlocks; const uint32_t remain = waveN % waveBlocks; \ | ||
| 813 | + const uint32_t myCount = rowNum + (vecIdx < remain ? 1 : 0); \ | ||
| 814 | + const uint32_t myStart = logicalBase + vecIdx * rowNum + (vecIdx < remain ? vecIdx : remain); \ | ||
| 815 | + if (incy > 0) { PIPELINE(x, aug, y, alphaReal, alphaImag, myStart, totalN, myCount, incx, incy, tileCapacity); } \ | ||
| 816 | + else { const uint32_t mirroredStart = totalN - myStart - myCount; PIPELINE(x, aug, y, alphaReal, alphaImag, mirroredStart, totalN, myCount, -incx, -incy, tileCapacity); } \ | ||
| 817 | + } \ | ||
| 818 | +} | ||
| 819 | + | ||
| 820 | + | ||
| 821 | +extern "C" __global__ __aicore__ __vector__ void KERNEL( \ | ||
| 822 | + __gm__ float*, __gm__ uint32_t*, __gm__ float*, __gm__ float*) {} | ||
| 823 | + | ||
| 824 | +DEFINE_CAXPY_STRIDED_KERNEL(caxpy_strided_shared, caxpy_strided_shared_run) | ||
| 825 | +DEFINE_CAXPY_STRIDED_KERNEL(caxpy_strided_disjoint, caxpy_strided_disjoint_run) | ||
| 826 | + | ||
| 827 | + | ||
| 828 | +void caxpy_kernel_do( | ||
| 829 | + GM_ADDR x, GM_ADDR maskBuf, GM_ADDR y, GM_ADDR workSpace, GM_ADDR tilingGm, uint32_t numBlocks, void* stream) | ||
| 223 | { | 830 | { |
| 224 | - caxpy<<<numBlocks, nullptr, stream>>>((float *)x, | 831 | + caxpy<<<numBlocks, nullptr, stream>>>((float*)x, (uint32_t*)maskBuf, (float*)y, (float*)tilingGm); |
| 225 | - (uint32_t *)maskBuf, | 832 | +} |
| 226 | - (float *)y, (float *)tilingGm); | 833 | + |
| 227 | -} | 834 | +void caxpy_strided_shared_kernel_do( |
| 835 | + GM_ADDR x, GM_ADDR maskBuf, GM_ADDR y, GM_ADDR workSpace, GM_ADDR tilingGm, uint32_t numBlocks, void* stream) | ||
| 836 | +{ | ||
| 837 | + caxpy_strided_shared<<<numBlocks, nullptr, stream>>>((float*)x, (uint32_t*)maskBuf, (float*)y, (float*)tilingGm); | ||
| 838 | +} | ||
| 839 | + | ||
| 840 | +void caxpy_strided_disjoint_kernel_do( | ||
| 841 | + GM_ADDR x, GM_ADDR maskBuf, GM_ADDR y, GM_ADDR workSpace, GM_ADDR tilingGm, uint32_t numBlocks, void* stream) | ||
| 842 | +{ | ||
| 843 | + caxpy_strided_disjoint<<<numBlocks, nullptr, stream>>>( | ||
| 844 | + (float*)x, (uint32_t*)maskBuf, (float*)y, (float*)tilingGm); | ||
| 845 | +} | ||
| 846 | + | ||
| 847 | + | ||
| 848 | +__aicore__ __inline__ __attribute__((always_inline)) void scalar_copy_complex_in( | ||
| 849 | + __ubuf__ float* dst, __gm__ float* src) | ||
| 850 | +{ | ||
| 851 | + copy_gm_to_ubuf_align_b32(dst, src, 0, 1, 2 * sizeof(float), 0, 0, 0, 0); | ||
| 852 | +} | ||
| 853 | + | ||
| 854 | +__aicore__ __inline__ __attribute__((always_inline)) void scalar_copy_complex_out( | ||
| 855 | + __gm__ float* dst, __ubuf__ float* src) | ||
| 856 | +{ | ||
| 857 | + copy_ubuf_to_gm_align_b32(dst, src, 0, 1, 2 * sizeof(float), 0, 0, 0, 0); | ||
| 858 | +} | ||
| 859 | + | ||
| 860 | +__aicore__ __inline__ __attribute__((always_inline)) void scalar_compute_all( | ||
| 861 | + __gm__ float* gmX, __gm__ float* gmY, float alphaReal, float alphaImag, uint32_t logicalOffset, | ||
| 862 | + uint32_t totalComplexCount, uint32_t complexCount, int64_t incx, int64_t incy) | ||
| 863 | +{ | ||
| 864 | + const uint32_t absIncx = abs_increment(incx); | ||
| 865 | + const uint32_t absIncy = abs_increment(incy); | ||
| 866 | + auto ubX = reinterpret_cast<__ubuf__ float*>((uintptr_t)0); | ||
| 867 | + auto ubY = reinterpret_cast<__ubuf__ float*>((uintptr_t)32); | ||
| 868 | + | ||
| 869 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 870 | + for (uint32_t local = 0; local < complexCount; ++local) { | ||
| 871 | + const uint32_t logical = logicalOffset + local; | ||
| 872 | + const uint32_t xPhysical = | ||
| 873 | + incx > 0 ? logical * absIncx : (totalComplexCount - 1 - logical) * absIncx; | ||
| 874 | + const uint32_t yPhysical = | ||
| 875 | + incy > 0 ? logical * absIncy : (totalComplexCount - 1 - logical) * absIncy; | ||
| 876 | + | ||
| 877 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 878 | + scalar_copy_complex_in(ubX, gmX + xPhysical * 2); | ||
| 879 | + scalar_copy_complex_in(ubY, gmY + yPhysical * 2); | ||
| 880 | + set_flag(PIPE_MTE2, PIPE_S, EVENT_ID0); | ||
| 881 | + wait_flag(PIPE_MTE2, PIPE_S, EVENT_ID0); | ||
| 882 | + | ||
| 883 | + const float xReal = ubX[0]; | ||
| 884 | + const float xImag = ubX[1]; | ||
| 885 | + const float yReal = ubY[0]; | ||
| 886 | + const float yImag = ubY[1]; | ||
| 887 | + ubY[0] = yReal + alphaReal * xReal - alphaImag * xImag; | ||
| 888 | + ubY[1] = yImag + alphaReal * xImag + alphaImag * xReal; | ||
| 889 | + set_flag(PIPE_S, PIPE_MTE3, EVENT_ID0); | ||
| 890 | + wait_flag(PIPE_S, PIPE_MTE3, EVENT_ID0); | ||
| 891 | + scalar_copy_complex_out(gmY + yPhysical * 2, ubY); | ||
| 892 | + set_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 893 | + } | ||
| 894 | + wait_flag(PIPE_MTE3, PIPE_MTE2, EVENT_ID0); | ||
| 895 | +} | ||
| 896 | + | ||
| 897 | + | ||
| 898 | +extern "C" __global__ __aicore__ __vector__ void caxpy_strided_scalar( | ||
| 899 | + __gm__ float* __restrict__ x, __gm__ float* __restrict__ y, __gm__ float* __restrict__ tilingGm) | ||
| 900 | +{ | ||
| 901 | + | ||
| 902 | + const uint32_t vecIdx = AscendC::GetBlockIdx(); | ||
| 903 | + auto tiling = reinterpret_cast<__gm__ CaxpyTilingData*>(tilingGm); | ||
| 904 | + const uint32_t numBlocks = tiling->activeBlocks; | ||
| 905 | + const float alphaReal = tiling->alphaReal; | ||
| 906 | + const float alphaImag = tiling->alphaImag; | ||
| 907 | + const uint32_t totalN = tiling->totalN; | ||
| 908 | + const int64_t incx = tiling->incx; | ||
| 909 | + const int64_t incy = tiling->incy; | ||
| 910 | + if (numBlocks == 0 || vecIdx >= numBlocks) { | ||
| 911 | + return; | ||
| 912 | + } | ||
| 913 | + | ||
| 914 | + const uint32_t rowNum = totalN / numBlocks; | ||
| 915 | + const uint32_t remain = totalN % numBlocks; | ||
| 916 | + const uint32_t count = rowNum + (vecIdx < remain ? 1 : 0); | ||
| 917 | + const uint32_t start = vecIdx * rowNum + (vecIdx < remain ? vecIdx : remain); | ||
| 918 | + scalar_compute_all(x, y, alphaReal, alphaImag, start, totalN, count, incx, incy); | ||
| 919 | + | ||
| 920 | +} | ||
| 921 | + | ||
| 922 | +void caxpy_strided_scalar_kernel_do( | ||
| 923 | + GM_ADDR x, GM_ADDR y, GM_ADDR tilingGm, uint32_t numBlocks, void* stream) | ||
| 924 | +{ | ||
| 925 | + caxpy_strided_scalar<<<numBlocks, nullptr, stream>>>((float*)x, (float*)y, (float*)tilingGm); | ||
| 926 | +} | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +void caxpy_kernel_do( | ||
| 16 | + uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, uint32_t numBlocks, | ||
| 17 | + void* stream); | ||
| 18 | + | ||
| 19 | +void caxpy_strided_shared_kernel_do( | ||
| 20 | + uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, uint32_t numBlocks, | ||
| 21 | + void* stream); | ||
| 22 | + | ||
| 23 | +void caxpy_strided_disjoint_kernel_do( | ||
| 24 | + uint8_t* x, uint8_t* maskBuf, uint8_t* y, uint8_t* workSpace, uint8_t* tilingGm, uint32_t numBlocks, | ||
| 25 | + void* stream); | ||
| 26 | + | ||
| 27 | +void caxpy_strided_scalar_kernel_do(uint8_t* x, uint8_t* y, uint8_t* tilingGm, uint32_t numBlocks, void* stream); | ||
| @@ -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 | +// Single source of truth for the CAXPY host->device tiling block. BOTH the host | ||
| 16 | +// (caxpy_host.cpp) and the device kernel (caxpy_kernel.cpp) include this header | ||
| 17 | +// and access fields by name. Do not re-declare this layout anywhere else and do | ||
| 18 | +// not read it via hard-coded byte offsets -- that is exactly the drift hazard | ||
| 19 | +// this shared definition exists to remove. | ||
| 20 | + | ||
| 21 | +struct CaxpyTilingData { | ||
| 22 | + uint32_t totalN; | ||
| 23 | + uint32_t activeBlocks; | ||
| 24 | + float alphaReal; | ||
| 25 | + float alphaImag; | ||
| 26 | + int64_t incx; | ||
| 27 | + int64_t incy; | ||
| 28 | + // Shared-pipeline span-read tile capacity, in complex elements. Computed by | ||
| 29 | + // the host from incx so that the span read | ||
| 30 | + // ((sharedTileCapacity-1)*|incx|+1) complex elements always fits in the | ||
| 31 | + // 38 KiB lowScratch UB buffer -- see CaxpySharedTileCapacity in | ||
| 32 | + // caxpy_dispatch_policy.h for the formula. Unused (left at its default of | ||
| 33 | + // CAXPY_STRIDED_TILE_COUNT) by the disjoint and scalar kernel variants, | ||
| 34 | + // which never span-read. | ||
| 35 | + uint32_t sharedTileCapacity; | ||
| 36 | +}; | ||
| @@ -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,117 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +static inline bool CaxpyNeedPassThrough(aclblasHandle_t handle, int n) | ||
| 22 | +{ | ||
| 23 | + return handle == nullptr || n <= 0; | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +static inline aclError CaxpyAllocCopyH2D(void*& dPtr, const void* hPtr, size_t bytes) | ||
| 27 | +{ | ||
| 28 | + dPtr = nullptr; | ||
| 29 | + if (hPtr == nullptr || bytes == 0) { | ||
| 30 | + return ACL_SUCCESS; | ||
| 31 | + } | ||
| 32 | + aclError ret = aclrtMalloc(&dPtr, bytes, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 33 | + if (ret != ACL_SUCCESS) { | ||
| 34 | + return ret; | ||
| 35 | + } | ||
| 36 | + ret = aclrtMemcpy(dPtr, bytes, hPtr, bytes, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 37 | + if (ret != ACL_SUCCESS) { | ||
| 38 | + aclrtFree(dPtr); | ||
| 39 | + dPtr = nullptr; | ||
| 40 | + } | ||
| 41 | + return ret; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +static inline void CaxpyFreeAll(void* dX, void* dY) | ||
| 45 | +{ | ||
| 46 | + if (dX) { | ||
| 47 | + aclrtFree(dX); | ||
| 48 | + } | ||
| 49 | + if (dY) { | ||
| 50 | + aclrtFree(dY); | ||
| 51 | + } | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +// Physical storage span (in complex elements) covered by a length-n vector | ||
| 55 | +// with the given (possibly negative) increment. | ||
| 56 | +static inline size_t CaxpySpanElements(int n, int increment) | ||
| 57 | +{ | ||
| 58 | + if (n <= 0) { | ||
| 59 | + return 0; | ||
| 60 | + } | ||
| 61 | + const uint64_t absIncrement = | ||
| 62 | + increment >= 0 ? static_cast<uint64_t>(increment) | ||
| 63 | + : static_cast<uint64_t>(-(static_cast<int64_t>(increment) + 1)) + 1; | ||
| 64 | + const uint64_t logicalDistance = static_cast<uint64_t>(n - 1); | ||
| 65 | + if (absIncrement != 0 && logicalDistance > | ||
| 66 | + (std::numeric_limits<size_t>::max() / sizeof(aclblasComplex) - 1) / | ||
| 67 | + absIncrement) { | ||
| 68 | + return 0; | ||
| 69 | + } | ||
| 70 | + return static_cast<size_t>(logicalDistance * absIncrement + 1); | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +inline aclblasStatus_t aclblasCaxpy_npu( | ||
| 74 | + aclblasHandle_t handle, int n, const aclblasComplex* alpha, const aclblasComplex* x, int incx, | ||
| 75 | + aclblasComplex* y, int incy) | ||
| 76 | +{ | ||
| 77 | + if (CaxpyNeedPassThrough(handle, n)) { | ||
| 78 | + return aclblasCaxpy(handle, n, alpha, x, incx, y, incy); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + const size_t xElements = CaxpySpanElements(n, incx); | ||
| 82 | + const size_t yElements = CaxpySpanElements(n, incy); | ||
| 83 | + if ((n > 0 && xElements == 0) || (n > 0 && yElements == 0)) { | ||
| 84 | + return ACLBLAS_STATUS_INVALID_VALUE; | ||
| 85 | + } | ||
| 86 | + const size_t xBytes = xElements * sizeof(aclblasComplex); | ||
| 87 | + const size_t yBytes = yElements * sizeof(aclblasComplex); | ||
| 88 | + | ||
| 89 | + void* dX = nullptr; | ||
| 90 | + void* dY = nullptr; | ||
| 91 | + | ||
| 92 | + if (CaxpyAllocCopyH2D(dX, x, xBytes) != ACL_SUCCESS) { | ||
| 93 | + return ACLBLAS_STATUS_ALLOC_FAILED; | ||
| 94 | + } | ||
| 95 | + if (CaxpyAllocCopyH2D(dY, y, yBytes) != ACL_SUCCESS) { | ||
| 96 | + CaxpyFreeAll(dX, dY); | ||
| 97 | + return ACLBLAS_STATUS_ALLOC_FAILED; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + aclblasStatus_t ret = aclblasCaxpy( | ||
| 101 | + handle, n, alpha, static_cast<const aclblasComplex*>(dX), incx, static_cast<aclblasComplex*>(dY), incy); | ||
| 102 | + | ||
| 103 | + if (aclrtSynchronizeDevice() != ACL_SUCCESS) { | ||
| 104 | + CaxpyFreeAll(dX, dY); | ||
| 105 | + return ACLBLAS_STATUS_INTERNAL_ERROR; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + if (ret == ACLBLAS_STATUS_SUCCESS && y != nullptr && dY != nullptr) { | ||
| 109 | + if (aclrtMemcpy(y, yBytes, dY, yBytes, ACL_MEMCPY_DEVICE_TO_HOST) != ACL_SUCCESS) { | ||
| 110 | + CaxpyFreeAll(dX, dY); | ||
| 111 | + return ACLBLAS_STATUS_INTERNAL_ERROR; | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + CaxpyFreeAll(dX, dY); | ||
| 116 | + return ret; | ||
| 117 | +} | ||
| @@ -8,158 +8,135 @@ | |||
| 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 | -#include <algorithm> | 12 | +#include <iostream> |
| 20 | -#include <iterator> | 13 | +#include <iomanip> |
| 21 | - | ||
| 22 | - | ||
| 23 | - | ||
| 24 | 14 | ||
| 25 | -#define CHECK_RET(cond, return_expr) \ | 15 | +#include "verify.h" |
| 26 | - do { \ | 16 | +#include "blas_test.h" |
| 27 | - if (!(cond)) { \ | 17 | +#include "csv_loader.h" |
| 28 | - return_expr; \ | 18 | +#include "fill.h" |
| 29 | - } \ | 19 | +#include "caxpy_param.h" |
| 30 | - } while (0) | 20 | +#include "caxpy_golden.h" |
| 21 | + | ||
| 22 | + | ||
| 31 | 23 | ||
| 32 | -#define LOG_PRINT(message, ...) \ | 24 | +namespace { |
| 33 | - do { \ | ||
| 34 | - printf(message, ##__VA_ARGS__); \ | ||
| 35 | - } while (0) | ||
| 36 | 25 | ||
| 37 | -uint32_t VerifyCaxpyResult(std::vector<aclblasComplex>& output, std::vector<aclblasComplex>& golden) | 26 | +constexpr aclblasComplex GUARD_VALUE{12345.25F, -54321.5F}; |
| 27 | +constexpr size_t GUARD_COUNT = 64; | ||
| 28 | + | ||
| 29 | +// Deterministic per-index complex value generator: independent of BlasFillMode | ||
| 30 | +// so cross-checking against the reference cblas-style AXPY is exact, matching | ||
| 31 | +// the layout the legacy hardcoded-array caxpy_test used before the CSV | ||
| 32 | +// migration. | ||
| 33 | +aclblasComplex MakeStridedValue(size_t index, int32_t salt) | ||
| 38 | { | 34 | { |
| 39 | - auto printTensor = [](std::vector<aclblasComplex>& tensor, const char* name) { | 35 | + const float real = 0.125F * static_cast<float>(static_cast<int32_t>(index % 29) - 14 + salt); |
| 40 | - constexpr size_t maxPrintSize = 10; | 36 | + const float imag = 0.0625F * static_cast<float>(static_cast<int32_t>(index % 23) - 11 - salt); |
| 41 | - std::cout << name << ": "; | 37 | + 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 | } | 38 | } |
| 64 | 39 | ||
| 65 | -int32_t TestCaxpy(aclblasHandle handle, aclrtStream stream) | 40 | +// Build a length-n complex vector (logical, stride-independent index order) |
| 41 | +// with GUARD_COUNT sentinel padding on both sides so any out-of-bounds | ||
| 42 | +// read/write by the operator under test corrupts a detectable guard region. | ||
| 43 | +std::vector<aclblasComplex> MakeGuardedVector(int n, int increment, int32_t salt) | ||
| 66 | { | 44 | { |
| 67 | - constexpr uint32_t totalLength = 8 * 2048; | 45 | + const size_t span = CaxpySpanElements(n, increment); |
| 68 | - constexpr aclblasComplex valueX{1.0f, 0.5f}; | 46 | + std::vector<aclblasComplex> data(GUARD_COUNT + span + GUARD_COUNT, GUARD_VALUE); |
| 69 | - constexpr aclblasComplex valueY{2.0f, 1.0f}; | 47 | + for (size_t i = 0; i < span; ++i) |
| 70 | - constexpr aclblasComplex alpha{2.0f, 1.0f}; | 48 | + data[GUARD_COUNT + i] = MakeStridedValue(i, salt); |
| 71 | - std::vector<aclblasComplex> x(totalLength, valueX); | 49 | + 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 | } | 50 | } |
| 124 | 51 | ||
| 125 | -int32_t main(int32_t argc, char* argv[]) | 52 | +} // namespace |
| 126 | -{ | ||
| 127 | - int32_t deviceId = 0; | ||
| 128 | 53 | ||
| 129 | - aclInit(nullptr); | 54 | +// ── Test fixture ────────────────────────────────────────────────────────────── |
| 130 | - aclrtSetDevice(deviceId); | 55 | +class CaxpyArch22Test : public BlasTest<CaxpyParam> { }; |
| 131 | 56 | ||
| 132 | - aclblasHandle_t handle = nullptr; | 57 | +// ── TEST_F: null handle / null alpha (not expressible via CSV) ─────────────── |
| 133 | - auto ret = aclblasCreate(&handle); | 58 | +TEST_F(CaxpyArch22Test, NullHandle) { |
| 134 | - CHECK_RET(ret == ACLBLAS_STATUS_SUCCESS, LOG_PRINT("aclblasCreate failed. ERROR: %d\n", ret); return ret); | 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_HANDLE_IS_NULLPTR)); | ||
| 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); | 81 | + |
| 148 | - aclrtResetDevice(deviceId); | 82 | + // Step 1: Generate host data (guarded x/y storage, deterministic values) |
| 149 | - aclFinalize(); | 83 | + std::vector<aclblasComplex> xHost; |
| 150 | - return result; | 84 | + if (p.nullx == 0 && p.n > 0) |
| 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); | ||
| 89 | + const std::vector<aclblasComplex> initialY = yHost; | ||
| 90 | + | ||
| 91 | + const aclblasComplex* xPtr = xHost.empty() ? nullptr : xHost.data() + GUARD_COUNT; | ||
| 92 | + aclblasComplex* yPtr = yHost.empty() ? nullptr : yHost.data() + GUARD_COUNT; | ||
| 93 | + | ||
| 94 | + // Step 2: Execute on NPU (wrapper handles device memory, nullptr passthrough) | ||
| 95 | + aclblasStatus_t ret = aclblasCaxpy_npu(CaxpyArch22Test::handle_, p.n, &alpha, xPtr, p.incx, yPtr, p.incy); | ||
| 96 | + | ||
| 97 | + // Step 3: Verify expected return code | ||
| 98 | + EXPECT_EQ(static_cast<int>(ret), static_cast<int>(p.expectResult)); | ||
| 99 | + if (p.expectResult != ACLBLAS_STATUS_SUCCESS) return; | ||
| 100 | + if (p.n == 0) return; | ||
| 101 | + | ||
| 102 | + // Step 4: Compute golden on CPU over the same guarded storage, so any | ||
| 103 | + // stray write into the guard region is caught by the full-buffer compare. | ||
| 104 | + std::vector<aclblasComplex> xGolden = p.nullx == 0 ? MakeGuardedVector(p.n, p.incx, 3) : std::vector<aclblasComplex>{}; | ||
| 105 | + std::vector<aclblasComplex> yGolden = p.nully == 0 ? MakeGuardedVector(p.n, p.incy, -5) : std::vector<aclblasComplex>{}; | ||
| 106 | + const aclblasComplex* xGoldenPtr = xGolden.empty() ? nullptr : xGolden.data() + GUARD_COUNT; | ||
| 107 | + aclblasComplex* yGoldenPtr = yGolden.empty() ? nullptr : yGolden.data() + GUARD_COUNT; | ||
| 108 | + aclblasStatus_t cpuRet = aclblasCaxpy_cpu(CaxpyArch22Test::handle_, p.n, &alpha, xGoldenPtr, p.incx, yGoldenPtr, p.incy); | ||
| 109 | + EXPECT_EQ(static_cast<int>(cpuRet), static_cast<int>(ACLBLAS_STATUS_SUCCESS)); | ||
| 110 | + | ||
| 111 | + // Step 5: Precision verification, real/imag components split (MERE/MARE). | ||
| 112 | + VerifyConfig cfg; | ||
| 113 | + cfg.mode = PrecisionMode::MERE_MARE; | ||
| 114 | + cfg.mereThreshold = p.mereThreshold; | ||
| 115 | + cfg.mareMultiplier = p.mareMultiplier; | ||
| 116 | + | ||
| 117 | + EXPECT_TRUE(Verifier::verifyMereMareComplexFloat( | ||
| 118 | + yHost.data(), yGolden.data(), yHost.size(), cfg.mereThreshold, cfg.mareMultiplier, 0.0, p.caseName)); | ||
| 119 | + | ||
| 120 | + if (p.measure != 0 && blas_test::MeasureRequested()) { | ||
| 121 | + std::vector<double> samples; | ||
| 122 | + const int measureRet = blas_test::MeasureNpuEvent( | ||
| 123 | + stream_, p.warmup, p.measurements, | ||
| 124 | + [&]() { | ||
| 125 | + yHost = initialY; | ||
| 126 | + return true; | ||
| 127 | + }, | ||
| 128 | + [&]() { | ||
| 129 | + const aclblasComplex* timedX = xHost.empty() ? nullptr : xHost.data() + GUARD_COUNT; | ||
| 130 | + aclblasComplex* timedY = yHost.empty() ? nullptr : yHost.data() + GUARD_COUNT; | ||
| 131 | + return aclblasCaxpy_npu( | ||
| 132 | + CaxpyArch22Test::handle_, p.n, &alpha, timedX, p.incx, timedY, p.incy) == | ||
| 133 | + ACLBLAS_STATUS_SUCCESS; | ||
| 134 | + }, | ||
| 135 | + samples); | ||
| 136 | + ASSERT_EQ(measureRet, 0); | ||
| 137 | + std::cout << std::setprecision(9) << "PERFORMANCE_SUMMARY_JSON={\"case\":\"" << p.caseName | ||
| 138 | + << "\",\"n\":" << p.n << ",\"p50_us\":" << blas_test::MedianUs(samples) | ||
| 139 | + << ",\"avg_us\":" << blas_test::MeanUs(samples) << ",\"warmups\":" << p.warmup | ||
| 140 | + << ",\"measurements\":" << p.measurements << ",\"timing\":\"npu_event\"}\n"; | ||
| 151 | } | 141 | } |
| 152 | - std::cout << "[PASS] Caxpy test passed" << std::endl; | 142 | +} |
| 153 | - | ||
| 154 | - aclblasDestroy(handle); | ||
| 155 | - aclrtDestroyStream(stream); | ||
| 156 | - aclrtResetDevice(deviceId); | ||
| 157 | - aclFinalize(); | ||
| 158 | - | ||
| 159 | - std::cout << "========================================" << std::endl; | ||
| 160 | - std::cout << "Test Summary:" << std::endl; | ||
| 161 | - std::cout << " Passed: 1 - Caxpy" << std::endl; | ||
| 162 | - std::cout << "========================================" << std::endl; | ||
| 163 | - | ||
| 164 | - return 0; | ||
| 165 | -} | ||
| @@ -0,0 +1,79 @@ | |||
| 1 | +case_name,description,n,incx,incy,alpha_real,alpha_imag,nullx,nully,expect_result,mere_threshold,mare_multiplier,random_seed,measure,warmup,measurements,classification,flops,profile | ||
| 2 | +TC_zero,zero,0,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 3 | +TC_one,one,1,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 4 | +TC_lane_2,lane 2,2,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 5 | +TC_lane_3,lane 3,3,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 6 | +TC_lane_31,lane 31,31,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 7 | +TC_lane_32,lane 32,32,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 8 | +TC_lane_33,lane 33,33,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 9 | +TC_lane_63,lane 63,63,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 10 | +TC_lane_64,lane 64,64,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 11 | +TC_lane_65,lane 65,65,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 12 | +TC_core_39,core 39,39,1,1,0.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 13 | +TC_core_40,core 40,40,1,1,0.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 14 | +TC_core_41,core 41,41,1,1,0.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 15 | +TC_strided_x,strided x,257,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 16 | +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,0,30,50,smoke,0,0 | ||
| 17 | +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,0,30,50,smoke,0,0 | ||
| 18 | +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,0,30,50,smoke,0,0 | ||
| 19 | +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,0,30,50,smoke,0,0 | ||
| 20 | +TC_vector_reorder_tile,vector reorder tile,512,2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 21 | +TC_scalar_block_15,scalar block 15,600,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 22 | +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,0,30,50,smoke,0,0 | ||
| 23 | +TC_scalar_block_16,scalar block 16,640,2,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 24 | +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,0,30,50,smoke,0,0 | ||
| 25 | +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,0,30,50,smoke,0,0 | ||
| 26 | +TC_scalar_wide_increment,scalar wide increment,17,65537,-32769,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 27 | +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,0,30,50,smoke,0,0 | ||
| 28 | +TC_strided_y,strided y,257,1,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 29 | +TC_strided_xy,strided xy,257,2,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 30 | +TC_stride_5_x,stride 5 x,257,5,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 31 | +TC_stride_5_y,stride 5 y,257,1,5,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 32 | +TC_stride_5_9,stride 5 9,257,5,9,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 33 | +TC_stride_9_x,stride 9 x,257,9,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 34 | +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,0,30,50,smoke,0,0 | ||
| 35 | +TC_stride_5_multicore,stride 5 multicore,4097,5,5,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 36 | +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,0,30,50,smoke,0,0 | ||
| 37 | +TC_shared_tail_1,shared tail 1,41000,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 38 | +TC_shared_tail_8,shared tail 8,41280,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 39 | +TC_shared_tail_9,shared tail 9,41320,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 40 | +TC_shared_tail_16,shared tail 16,41600,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 41 | +TC_shared_tail_17,shared tail 17,41640,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 42 | +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,0,30,50,smoke,0,0 | ||
| 43 | +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,0,30,50,smoke,0,0 | ||
| 44 | +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,0,30,50,smoke,0,0 | ||
| 45 | +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,0,30,50,smoke,0,0 | ||
| 46 | +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,0,30,50,smoke,0,0 | ||
| 47 | +TC_negative_x,negative x,257,-2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 48 | +TC_negative_unit_y,negative unit y,257,2,-1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 49 | +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,0,30,50,smoke,0,0 | ||
| 50 | +TC_negative_y,negative y,257,1,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 51 | +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,0,30,50,smoke,0,0 | ||
| 52 | +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,0,30,50,smoke,0,0 | ||
| 53 | +TC_negative_xy,negative xy,257,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 54 | +TC_negative_9_x,negative 9 x,257,-9,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 55 | +TC_negative_x_multicore,negative x multicore,4097,-2,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 56 | +TC_negative_y_multicore,negative y multicore,4097,1,-3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 57 | +TC_opposite_sign_multicore,opposite sign multicore,4097,-2,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 58 | +TC_negative_xy_multicore,negative xy multicore,4097,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 59 | +TC_wide_stride_multitile,wide stride multitile,40961,10,11,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 60 | +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,0,30,50,smoke,0,0 | ||
| 61 | +TC_dynamic_shared_incx16,dynamic shared incx16,40961,16,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 62 | +TC_dynamic_shared_incx32,dynamic shared incx32,40961,32,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 63 | +TC_dynamic_shared_incx41,dynamic shared incx41,40961,41,3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 64 | +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,0,30,50,smoke,0,0 | ||
| 65 | +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,0,30,50,smoke,0,0 | ||
| 66 | +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,0,30,50,smoke,0,0 | ||
| 67 | +TC_multiwave_strided,multiwave strided,400000,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 68 | +TC_multiwave_negative,multiwave negative,400001,-2,-3,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 69 | +TC_multiwave_3_waves,multiwave 3 waves,700000,1,2,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 70 | +TC_pipeline_full_wave,pipeline full wave,327680,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 71 | +TC_hybrid_tail_1,hybrid tail 1,327681,2,3,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 72 | +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,0,30,50,smoke,0,0 | ||
| 73 | +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,0,30,50,smoke,0,0 | ||
| 74 | +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,0,30,50,smoke,0,0 | ||
| 75 | +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,0,30,50,smoke,0,0 | ||
| 76 | +TC_regular,regular,16384,1,1,0.75,-0.25,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,1,100,200,comparison,131072,1 | ||
| 77 | +TC_tile_minus_1,tile minus 1,194559,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,0,30,50,smoke,0,0 | ||
| 78 | +TC_tile,tile,194560,1,1,2.0,1.0,0,0,ACLBLAS_STATUS_SUCCESS,0.0001220703125,10.0,42,1,100,200,comparison,1556480,0 | ||
| 79 | +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,30,50,smoke,0,0 | ||
| @@ -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,42 @@ | |||
| 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 | + int measure = 0; | ||
| 26 | + int warmup = 30; | ||
| 27 | + int measurements = 50; | ||
| 28 | + | ||
| 29 | + CaxpyParam(const csv_map& csv) : BlasTestParamBase(csv) | ||
| 30 | + { | ||
| 31 | + n = parseInt(ReadMap(csv, "n", "0")); | ||
| 32 | + incx = parseInt(ReadMap(csv, "incx", "1")); | ||
| 33 | + incy = parseInt(ReadMap(csv, "incy", "1")); | ||
| 34 | + alphaReal = parseFloat(ReadMap(csv, "alpha_real", "1.0")); | ||
| 35 | + alphaImag = parseFloat(ReadMap(csv, "alpha_imag", "0.0")); | ||
| 36 | + nullx = parseInt(ReadMap(csv, "nullx", "0")); | ||
| 37 | + nully = parseInt(ReadMap(csv, "nully", "0")); | ||
| 38 | + measure = parseInt(ReadMap(csv, "measure", "0")); | ||
| 39 | + warmup = parseInt(ReadMap(csv, "warmup", "30")); | ||
| 40 | + measurements = parseInt(ReadMap(csv, "measurements", "50")); | ||
| 41 | + } | ||
| 42 | +}; | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +name: caxpy | ||
| 10 | +soc: ascend910b3 | ||
| 11 | +arch: arch22 | ||
| 12 | +cases: arch22/caxpy_test.csv | ||
| 13 | +performance_metrics: | ||
| 14 | + flop_model: "8 floating-point operations per complex element for one complex multiply-add" | ||
| 15 | + measured_peak_tflops: 1.0 | ||
| @@ -0,0 +1,69 @@ | |||
| 1 | + | ||
| 2 | +/** | ||
| 3 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 5 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 6 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 8 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace blas_test { | ||
| 21 | + | ||
| 22 | +inline bool MeasureRequested() { return std::getenv("BLAS_MEASURE") != nullptr; } | ||
| 23 | + | ||
| 24 | +inline int MeasureNpuEvent( | ||
| 25 | + aclrtStream stream, int warmup, int measurements, const std::function<bool()>& resetA, | ||
| 26 | + const std::function<bool()>& launch, std::vector<double>& outUs) | ||
| 27 | +{ | ||
| 28 | + if (stream == nullptr || warmup <= 0 || measurements <= 0) return 1; | ||
| 29 | + aclrtEvent evStart = nullptr; | ||
| 30 | + aclrtEvent evEnd = nullptr; | ||
| 31 | + if (aclrtCreateEvent(&evStart) != ACL_SUCCESS || aclrtCreateEvent(&evEnd) != ACL_SUCCESS) return 2; | ||
| 32 | + const auto fail = [&](int code) { | ||
| 33 | + aclrtDestroyEvent(evStart); | ||
| 34 | + aclrtDestroyEvent(evEnd); | ||
| 35 | + return code; | ||
| 36 | + }; | ||
| 37 | + for (int i = 0; i < warmup; ++i) { | ||
| 38 | + if (!resetA() || !launch() || aclrtSynchronizeStream(stream) != ACL_SUCCESS) return fail(3); | ||
| 39 | + } | ||
| 40 | + outUs.clear(); | ||
| 41 | + outUs.reserve(static_cast<size_t>(measurements)); | ||
| 42 | + for (int i = 0; i < measurements; ++i) { | ||
| 43 | + if (!resetA()) return fail(4); | ||
| 44 | + if (aclrtRecordEvent(evStart, stream) != ACL_SUCCESS || !launch() || | ||
| 45 | + aclrtRecordEvent(evEnd, stream) != ACL_SUCCESS || aclrtSynchronizeStream(stream) != ACL_SUCCESS) | ||
| 46 | + return fail(5); | ||
| 47 | + float ms = 0.0F; | ||
| 48 | + if (aclrtEventElapsedTime(&ms, evStart, evEnd) != ACL_SUCCESS) return fail(6); | ||
| 49 | + outUs.push_back(static_cast<double>(ms) * 1000.0); | ||
| 50 | + } | ||
| 51 | + return fail(0); | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +inline double MedianUs(std::vector<double> values) | ||
| 55 | +{ | ||
| 56 | + if (values.empty()) return 0.0; | ||
| 57 | + std::sort(values.begin(), values.end()); | ||
| 58 | + return values[values.size() / 2]; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +inline double MeanUs(const std::vector<double>& values) | ||
| 62 | +{ | ||
| 63 | + if (values.empty()) return 0.0; | ||
| 64 | + double sum = 0.0; | ||
| 65 | + for (double value : values) sum += value; | ||
| 66 | + return sum / static_cast<double>(values.size()); | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +} // namespace blas_test | ||


[Major] Missing README update
问题
新增算子实现但未发现
README.md变更。README 应包含:函数原型、参数表、约束说明、产品支持表、调用示例。依据
修改建议
在算子目录下的
README.md中添加算子文档,包括函数原型、参数说明表、约束条件、产品支持表和调用示例代码。