LCOV - code coverage report
Current view: top level - aicpu_sharder - aicpu_sharder.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 91.6 % 167 153
Test Date: 2026-08-12 11:05:02 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /**
       2              :  * 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              :  * 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              : #include "aicpu_sharder.h"
      12              : 
      13              : #include <atomic>
      14              : #include <semaphore.h>
      15              : #include <unistd.h>
      16              : #ifndef _AOSCORE_
      17              : #include <error.h>
      18              : #endif
      19              : #include <cstring>
      20              : #include <algorithm>
      21              : #include <cerrno>
      22              : 
      23              : #include "aicpu_sharder_log.h"
      24              : #include "aicpu_context.h"
      25              : 
      26              : namespace aicpu {
      27              : constexpr uint32_t GET_EVENT_LIMITED_NUM = 1000U;
      28              : 
      29            3 : SharderNonBlock::SharderNonBlock()
      30            3 :     : cpuCoreNum_(0U),
      31            3 :       randomKernelScheduler_(nullptr),
      32            3 :       splitKernelScheduler_(nullptr),
      33            3 :       splitKernelGetProcesser_(nullptr),
      34            3 :       parallelId_(0U)
      35            3 : {}
      36              : 
      37           48 : void SharderNonBlock::Register(
      38              :     const uint32_t cpuCoreNum, const RandomKernelScheduler& randomKernelScheduler,
      39              :     const SplitKernelScheduler& splitKernelScheduler, const SplitKernelGetProcesser& splitKernelGetProcesser)
      40              : {
      41           48 :     cpuCoreNum_ = cpuCoreNum;
      42           48 :     randomKernelScheduler_ = randomKernelScheduler;
      43           48 :     splitKernelScheduler_ = splitKernelScheduler;
      44           48 :     splitKernelGetProcesser_ = splitKernelGetProcesser;
      45           48 : }
      46              : 
      47            3 : void SharderNonBlock::Schedule(const Closure& aicpuClosure)
      48              : {
      49            3 :     if (randomKernelScheduler_ == nullptr) {
      50            1 :         aicpuClosure();
      51            1 :         return;
      52              :     }
      53              : 
      54            2 :     const uint32_t ret = randomKernelScheduler_(aicpuClosure);
      55            2 :     if (ret != 0U) {
      56            1 :         aicpuClosure();
      57            1 :         AICPUE_LOGE("Schedule random kernel event failed, do it by self. ret=%u", ret);
      58              :     }
      59              : 
      60            2 :     return;
      61              : }
      62              : 
      63            1 : uint32_t SharderNonBlock::GetCPUNum() { return cpuCoreNum_; }
      64              : 
      65          113 : SharderNonBlock& SharderNonBlock::GetInstance()
      66              : {
      67          113 :     static SharderNonBlock sharderNonBlock;
      68          113 :     return sharderNonBlock;
      69              : }
      70              : 
      71          114 : inline int64_t SharderNonBlock::CeilMultiple(const int64_t x, const int64_t base) const
      72              : {
      73          114 :     int64_t ret = x / base;
      74          114 :     if ((x % base) != 0) {
      75            0 :         ret++;
      76              :     }
      77              : 
      78          114 :     return ret;
      79              : }
      80              : 
      81           37 : void SharderNonBlock::ParallelFor(const int64_t total, const int64_t perUnitSize, const SharderWork& work)
      82              : {
      83           37 :     uint32_t parallelId = 0U;
      84              :     {
      85           37 :         const std::lock_guard<std::mutex> lk(parallelIdMutex_);
      86           38 :         ++parallelId_;
      87           38 :         parallelId = parallelId_.load();
      88           38 :     }
      89              : 
      90           38 :     AICPUE_LOGI("In parallel for. parallelId=%u, total=%ld, perUnitSize=%ld", parallelId, total, perUnitSize);
      91           38 :     if ((total <= 0) || (work == nullptr)) {
      92            0 :         AICPUE_LOGE("Invalid param. parallelId=%u, total=%ld", parallelId, total);
      93            0 :         return;
      94              :     }
      95              : 
      96           38 :     if ((splitKernelScheduler_ == nullptr) || (splitKernelGetProcesser_ == nullptr) || (cpuCoreNum_ <= 1U)) {
      97            0 :         AICPUE_LOGI("Work itself all. parallelId=%u, cpuCoreNum=%u", parallelId, cpuCoreNum_);
      98            0 :         work(0, total);
      99            0 :         return;
     100              :     }
     101              : 
     102              :     // In order to ensure a smaller scheduling delay, the maximum number of slices is twice the number of CPU cores
     103           38 :     const int64_t maxShardNum = static_cast<int64_t>(cpuCoreNum_) * 2;
     104              : 
     105              :     // calculate shard number and block size
     106              :     // i.e., if total is 118, perUintSize is 2, and cpuCoreNum_ is 13
     107              :     // then shardNum is 24, blockSize is 5
     108           38 :     int64_t blockSize = std::max(int64_t{1}, std::min(total, perUnitSize));
     109           38 :     int64_t shardNum = CeilMultiple(total, blockSize);
     110           38 :     shardNum = std::min(maxShardNum, shardNum);
     111           38 :     blockSize = CeilMultiple(total, shardNum);
     112           38 :     shardNum = CeilMultiple(total, blockSize);
     113              :     // There is no need to submit an event if shardNum is 1
     114           38 :     if (shardNum == 1) {
     115            1 :         AICPUE_LOGI(
     116              :             "Executes on the current thread by shardNum is 1. parallelId=%u, total=%ld, perUnitSize=%ld", parallelId,
     117              :             total, perUnitSize);
     118            1 :         work(0, total);
     119            1 :         return;
     120              :     }
     121              : 
     122           37 :     ExecuteParallelFor(total, shardNum, blockSize, work, parallelId);
     123           37 :     return;
     124              : }
     125              : 
     126           37 : void SharderNonBlock::ExecuteParallelFor(
     127              :     const int64_t total, const int64_t shardNum, const int64_t blockSize, const SharderWork& work,
     128              :     const uint32_t parallelId)
     129              : {
     130           37 :     AICPUE_LOGI(
     131              :         "Op parallel process start. parallelId=%u, shardNum=%ld, blockSize=%ld", parallelId, shardNum, blockSize);
     132              : 
     133           37 :     std::atomic<int64_t> cpuNumCounter(shardNum);
     134              :     sem_t aicpuSem;
     135           37 :     const int32_t semInitRet = sem_init(&aicpuSem, 0, 0U);
     136           37 :     if (semInitRet == -1) {
     137            0 :         AICPUE_LOGE("sem_init error with message: %s", strerror(errno));
     138            0 :         work(0, total);
     139            0 :         return;
     140              :     }
     141              : 
     142           37 :     uint32_t taskIndex = 0U;
     143           37 :     std::queue<aicpu::Closure> taskQueue;
     144          174 :     for (int64_t start = 0; start < total; start += blockSize) {
     145          141 :         const auto limit = std::min(start + blockSize, total);
     146            0 :         const Closure aicpuClosure = [&aicpuSem, &work, &cpuNumCounter, start, limit, parallelId, shardNum,
     147              :                                       taskIndex]() {
     148          141 :             cpuNumCounter--;
     149              :             // In order to ensure that user's work function exception does not affect multithread services,
     150              :             // exception capture is needed. Exception type is not cared here, and error log is printed.
     151          142 :             AICPUE_LOGI(
     152              :                 "Start call work func. parallelId=%u, shardNum=%ld, taskIndex=%u", parallelId, shardNum, taskIndex);
     153              :             try {
     154          142 :                 work(start, limit);
     155            4 :             } catch (std::exception& e) {
     156            4 :                 AICPUE_LOGE(
     157              :                     "Exception occurred in work func. parallelId=%u, shardNum=%ld, taskIndex=%u, "
     158              :                     "exception=%s",
     159              :                     parallelId, shardNum, taskIndex, e.what());
     160            4 :             }
     161              : 
     162          142 :             const int32_t semPostRet = sem_post(&aicpuSem);
     163          142 :             if (semPostRet == -1) {
     164            0 :                 AICPUE_LOGE("sem_post error with message: %s", strerror(errno));
     165              :             }
     166          142 :             AICPUE_LOGI(
     167              :                 "End call work func. parallelId=%u, shardNum=%ld, taskIndex=%u", parallelId, shardNum, taskIndex);
     168          283 :         };
     169              : 
     170          139 :         taskQueue.push(aicpuClosure);
     171          140 :         ++taskIndex;
     172          140 :     }
     173              : 
     174           33 :     const uint32_t ret = splitKernelScheduler_(parallelId, shardNum, taskQueue);
     175           37 :     if (ret != 0U) {
     176            1 :         AICPUE_LOGE("Submit split kernel task failed, ret=%u, parallelId=%u", ret, parallelId);
     177            1 :         (void)sem_destroy(&aicpuSem);
     178            1 :         return;
     179              :     }
     180              : 
     181           36 :     DoTaskItself(parallelId, cpuNumCounter, shardNum);
     182              : 
     183          178 :     for (int64_t i = 0; i < shardNum; ++i) {
     184          142 :         const int32_t semWaitRet = sem_wait(&aicpuSem);
     185          142 :         if (semWaitRet == -1) {
     186            4 :             AICPUE_LOGE("sem_wait error with message: %s", strerror(errno));
     187              :         }
     188              :     }
     189           36 :     const int32_t semDesRet = sem_destroy(&aicpuSem);
     190           36 :     if (semDesRet == -1) {
     191            1 :         AICPUE_LOGE("sem_destroy error with message: %s", strerror(errno));
     192              :     }
     193              : 
     194           36 :     AICPUE_LOGI("Op parallel process finished. parallelId=%u", parallelId);
     195              : 
     196           36 :     return;
     197           37 : }
     198              : 
     199           40 : void SharderNonBlock::DoTaskItself(
     200              :     const uint32_t parallelId, std::atomic<int64_t>& cpuNumCounter, const int64_t shardNum) const
     201              : {
     202           40 :     uint32_t getEventCnt = 0U;
     203           40 :     bool ret = true;
     204           40 :     bool logPrintFlag = true;
     205         1195 :     while ((cpuNumCounter > 0) && ret) {
     206         1155 :         ret = splitKernelGetProcesser_();
     207         1155 :         ++getEventCnt;
     208         1155 :         if ((getEventCnt >= GET_EVENT_LIMITED_NUM) && (logPrintFlag)) {
     209            1 :             logPrintFlag = false;
     210            1 :             std::string opname("");
     211            1 :             (void)GetOpname(GetAicpuThreadIndex(), opname);
     212            2 :             AICPUE_RUN_LOGW(
     213              :                 "Get event num has exceeded %u. parallelId=%u, cpuNumCounter=%ld, shardNum=%ld, "
     214              :                 "opName=%s",
     215              :                 GET_EVENT_LIMITED_NUM, parallelId, cpuNumCounter.load(), shardNum, opname.c_str());
     216            1 :         }
     217              :     }
     218           40 : }
     219              : 
     220            6 : void SharderNonBlock::ExecuteParallelForHash(
     221              :     const int64_t total, const int64_t cpuNums, const SharderWork& work, const uint32_t parallelId)
     222              : {
     223            6 :     AICPUE_LOGI("Op hash parallel process start. parallelId=%u, total=%ld, cpuNums=%ld", parallelId, total, cpuNums);
     224              : 
     225            6 :     std::atomic<int64_t> cpuNumCounter(cpuNums);
     226              :     sem_t aicpuSem;
     227            6 :     const int32_t semInitRet = sem_init(&aicpuSem, 0, 0U);
     228            6 :     if (semInitRet == -1) {
     229            1 :         AICPUE_LOGE("sem_init error with message: %s", strerror(errno));
     230            1 :         return;
     231              :     }
     232              : 
     233            5 :     std::queue<aicpu::Closure> taskQueue;
     234           15 :     for (int64_t cur = 0; cur < cpuNums; cur++) {
     235            0 :         const Closure aicpuClosure = [&aicpuSem, &work, &cpuNumCounter, total, cur, parallelId]() {
     236            8 :             cpuNumCounter--;
     237           16 :             AICPUE_LOGI(
     238              :                 "Start call work func. parallelId=%u, cur=%ld, cpuNumCounter=%ld", parallelId, cur,
     239              :                 cpuNumCounter.load());
     240            8 :             work(total, cur);
     241              : 
     242            8 :             const int32_t semPostRet = sem_post(&aicpuSem);
     243            8 :             if (semPostRet == -1) {
     244            2 :                 AICPUE_LOGE("sem_post error with message: %s", strerror(errno));
     245              :             }
     246           10 :         };
     247              : 
     248           10 :         taskQueue.push(aicpuClosure);
     249           10 :     }
     250              : 
     251            5 :     const uint32_t ret = splitKernelScheduler_(parallelId, cpuNums, taskQueue);
     252            5 :     if (ret != 0U) {
     253            1 :         AICPUE_LOGE("Submit hash split kernel task failed, ret=%u, parallelId=%u", ret, parallelId);
     254            1 :         (void)sem_destroy(&aicpuSem);
     255            1 :         return;
     256              :     }
     257              : 
     258            4 :     DoTaskItself(parallelId, cpuNumCounter, cpuNums);
     259              : 
     260           12 :     for (int64_t i = 0; i < cpuNums; i++) {
     261            8 :         const int32_t semWaitRet = sem_wait(&aicpuSem);
     262            8 :         if (semWaitRet == -1) {
     263            4 :             AICPUE_LOGE("sem_wait error with message: %s", strerror(errno));
     264              :         }
     265              :     }
     266            4 :     const int32_t semDesRet = sem_destroy(&aicpuSem);
     267            4 :     if (semDesRet == -1) {
     268            1 :         AICPUE_LOGE("sem_destroy error with message: %s", strerror(errno));
     269              :     }
     270              : 
     271            4 :     AICPUE_LOGI("Op hash parallel process finished. parallelId=%u", parallelId);
     272            4 :     return;
     273            5 : }
     274              : 
     275            7 : void SharderNonBlock::ParallelForHash(const int64_t total, const int64_t cpuNums, const SharderWork& work)
     276              : {
     277            7 :     uint32_t parallelId = 0U;
     278              :     {
     279            7 :         const std::lock_guard<std::mutex> lk(parallelIdMutex_);
     280            7 :         ++parallelId_;
     281            7 :         parallelId = parallelId_.load();
     282            7 :     }
     283              : 
     284            7 :     if ((total <= 0) || (work == nullptr)) {
     285            0 :         AICPUE_LOGE("invalid param: total<=0 or work is nullptr");
     286            0 :         return;
     287              :     }
     288              : 
     289            7 :     if ((splitKernelScheduler_ == nullptr) || (splitKernelGetProcesser_ == nullptr) || (cpuCoreNum_ <= 1U)) {
     290            1 :         AICPUE_LOGE("schedule is nullptr or cpu core num is not enough");
     291            1 :         return;
     292              :     }
     293              : 
     294            6 :     ExecuteParallelForHash(total, cpuNums, work, parallelId);
     295            6 :     return;
     296              : }
     297              : } // namespace aicpu
     298              : 
     299              : /**
     300              :  * Shards the "total" unit of work refer "perUintSize"
     301              :  */
     302           38 : void ParallelFor(int64_t total, int64_t perUnitSize, const aicpu::SharderWork& work)
     303              : {
     304           38 :     aicpu::SharderNonBlock::GetInstance().ParallelFor(total, perUnitSize, work);
     305           38 : }
     306              : 
     307              : /**
     308              :  * Get CPU number
     309              :  */
     310            1 : uint32_t GetCPUNum() { return aicpu::SharderNonBlock::GetInstance().GetCPUNum(); }
        

Generated by: LCOV version 2.0-1