深度测评解析 CANN:从 ACL 到自定义算子,解锁昇腾计算的全部潜能

作者:wei_shuo日期:2025/11/14

深度测评解析 CANN:从 ACL 到自定义算子,解锁昇腾计算的全部潜能

CANN 核心价值解读:统一计算底座与全场景适配

在这里插入图片描述

端到端栈级支持:CANN 覆盖驱动、运行时、算子加速库、编译器及上层框架适配的全套工具链,大幅降低模型向昇腾硬件移植的工程成本

开发者定制接口:ACL 提供 C/C++、Python 双接口,兼顾快速原型验证与生产级接入;AOL(aclnn)算子性能强,支持两段式调用,方便做内存、离线编译优化

可控资源调度与并发:通过 Device/Context/Stream 等抽象,CANN 能细粒度控制线程、设备上下文,适配多卡、多进程 / 线程场景,优化吞吐和延迟

自定义算子与生态协同:有 Ascend C 开发链路和社区仓库(如 cann-ops),可将系统未支持的算子部署到硬件,还能做性能优化

Ascend C API 架构解析:从底层算子到高层算法的分层赋能

在这里插入图片描述

Ascend C 的 API 体系分为基础 API和高层 API,分别支撑底层功能灵活组合与上层算法快速落地的开发需求

基础 API 实践:释放底层算子组合的灵活力量

1、计算类 API:分为标量(Scalar 单元)、向量(Vector 单元)、矩阵(Cube 单元)三类,适配不同粒度的计算场景

1using namespace AscendC;
2LocalTensor<half> scalarA(1), scalarB(1), scalarC(1);  // 标量(1元素)
3LocalTensor<half> vectorA(128), vectorB(128), vectorC(128);  // 向量(128元素)
4LocalTensor<half> matrixA(16, 16), matrixB(16, 16), matrixC(16, 16);  // 矩阵(16x16)
5ScalarAdd(scalarA, scalarB, scalarC);  // 标量计算:单个元素加法
6VectorAdd(vectorA, vectorB, vectorC);  // 向量计算:128元素并行加法
7CubeMatmul(matrixA, matrixB, matrixC);  // 矩阵计算:16x16矩阵乘法
8

2、数据搬运 API:以DataCopy为核心,实现Global Memory与Local Memory间的数据迁移

1// 从GlobalTensor搬入LocalTensor
2LocalTensor<half> localIn = pipe.AllocTensor<half>(len);
3DataCopy(localIn, globalIn);
4
5// 计算后,从LocalTensor搬出至GlobalTensor
6DataCopy(globalOut, localOut);
7pipe.FreeTensor(localIn);
8pipe.FreeTensor(localOut);
9

3、内存管理 API:通过AllocTensor/FreeTensor管理内存生命周期

1TPipe pipe;
2TQue<TPosition::VECIN, 2> que;
3pipe.InitBuffer(que, 4, 1024);  // 初始化队列内存
4LocalTensor<half> tensor = que.AllocTensor<half>();  // 分配张量
5que.FreeTensor(tensor);  // 回收内存
6

4、任务同步 API:通过EnQue/DeQue实现任务间通信

1TQue<TPosition::VECIN, 2> que;
2LocalTensor<half> localTensor = ...;
3que.EnQue(localTensor);  // 入队
4LocalTensor<half> outTensor = que.DeQue<half>();  // 出队
5
高层 API 设计:以对象化封装驱动高效算子开发

高层 API 封装了 Matmul、Softmax 等常用算法逻辑,借助 “对象化封装 + 流程化调用” 模式,将复杂算法逻辑转化为简洁的 API 调用,既减少了重复开发工作,又大幅提升了开发效率

1// 1. 定义Matmul对象(支持数据类型、存储格式定制)
2typedef MatmulType<TPosition::GM, CubeFormat::ND, half> AType;
3typedef MatmulType<TPosition::GM, CubeFormat::ND, half> BType;
4typedef MatmulType<TPosition::GM, CubeFormat::ND, float> CType;
5Matmul<AType, BType, CType, TPosition::GM, CubeFormat::ND, float> mm;
6
7// 2. 初始化Matmul
8mm.Init(&tiling, &pipe, &blasType);
9
10// 3. 绑定输入输出张量
11mm.SetTensorA(globalA);  // 左矩阵A
12mm.SetTensorB(globalB);  // 右矩阵B
13mm.SetBias(globalBias);  // Bias(可选)
14
15// 4. 执行矩阵乘(迭代或批量模式)
16while (mm.Iterate()) {
17mm.GetTensorC(globalC);
18}
19// 批量执行:mm.IterateAll(globalC);
20
21// 5. 结束矩阵乘
22mm.End();
23

从零开始的自定义算子实践:基于 CANN 的完整开发链路

配置 CANN 环境与获取算子源码

开始算子开发前,需要准备 CANN toolkit 环境 并拉取社区的 ops-math 仓库,仓库包含常见基础算子示例,便于开发者快速上手自定义算子编译与部署,通过如下代码验证GiteCode NoteBook环境可用性

1service@notebook-8fa2e45783c14d7ab34ee7e9b1d4a131-58dd7cdd7-glxtt:/opt/huawei/edu-apaas/src/init$ python3 -c "
2import acl
3acl.init()
4device_id = 0
5# 绑定设备
6ret = acl.rt.set_device(device_id)
7print('绑定NPU设备:', '成功' if ret == 0 else '失败')
8# 模拟内存分配(用数值1替代MEMORY_DEVICE,适配8.2.rc1)
9size = 1024  # 1KB
10ptr, ret = acl.rt.malloc(size, 1)  # 1表示设备侧内存,对应高版本的MEMORY_DEVICE
11print('设备侧内存分配:', '成功' if ret == 0 else '失败')
12# 释放资源
13acl.rt.free(ptr)
14acl.rt.reset_device(device_id)
15acl.finalize()
16print('资源释放完成,环境完全可用!')
17"
18

在这里插入图片描述

Step 1:构建算子开发基础框架(基于 ops-math 复用)

准备官方算子仓库 ops-math 的环境

1# 回到用户目录(避免权限问题)
2cd ~
3# 克隆官方算子仓库(提供编译脚本和目录规范)
4git clone https://gitcode.com/cann/ops-math.git
5cd ops-math
6# 安装依赖(镜像可能已预装,补全缺失的)
7pip3 install -r requirements.txt
8

在这里插入图片描述

Step 2:实现 Add 算子源码(三文件结构详解)

1、按照仓库规范先创建 ops 文件夹,再在 ops 文件夹下创建 add 文件夹

1# 创建嵌套文件夹 ops/add
2mkdir -p ops/add
3# 进入 ops/add 目录,准备创建文件
4cd ops/add
5
  • cat 命令创建 add.json 并写入内容
1cat > add.json << EOF
2{
3 "op": "Add",
4 "input_desc": [
5   {"name": "x", "dtype": ["float32"], "format": ["ND"]},
6   {"name": "y", "dtype": ["float32"], "format": ["ND"]}
7 ],
8 "output_desc": [
9   {"name": "z", "dtype": ["float32"], "format": ["ND"]}
10 ],
11 "attr_desc": []
12}
13EOF
14

在这里插入图片描述

2、设备侧核函数(NPU 上执行的加法逻辑)

  • 创建 kernel 目录并进入
1mkdir kernel  # 创建kernel文件夹
2cd kernel     # 进入kernel目录
3
  • 使用 cat 命令直接写入代码
1cat > add_impl.cc << EOF
2#include "acl/acl.h"
3#include "acl/acl_op.h"
4
5// 核函数:在NPU上并行执行x + y
6extern "C" __global__ void AddKernel(const float* x, const float* y, float* z, int size) {
7   int idx = blockIdx.x * blockDim.x + threadIdx.x;  // 计算线程索引
8   if (idx < size) {  // 避免越界
9       z[idx] = x[idx] + y[idx];
10   }
11}
12EOF
13

在这里插入图片描述

3、主机侧接口(调用核函数,处理内存交互)

  • 创建 host 目录并进入
1# add 目录下创建 host 文件夹
2mkdir host
3
4# 进入 host 目录
5cd host
6
  • 使用 cat 命令写入代码
1cat > add_host.cc << EOF
2#include "acl/acl.h"
3#include "add.h"
4
5// 主机侧接口:绑定核函数与输入输出
6aclError Add(const aclTensor* x, const aclTensor* y, aclTensor* z) {
7   // 获取输入输出数据地址和元素数量
8   const float* x_data = (const float*)aclGetTensorAddr(x);
9   const float* y_data = (const float*)aclGetTensorAddr(y);
10   float* z_data = (float*)aclGetTensorAddr(z);
11   int size = aclGetTensorElementNum(x);  // x和y形状相同,取x的元素数
12
13   // 配置核函数执行参数(1024线程/块,自动计算块数)
14   dim3 block(1024);
15   dim3 grid((size + block.x - 1) / block.x);  // 向上取整
16
17   // 启动核函数(CANN 8.2.rc1兼容写法)
18   AddKernel<<<grid, block, 0, aclrtStreamDefault>>>(x_data, y_data, z_data, size);
19   return ACL_SUCCESS;
20}
21EOF
22

在这里插入图片描述

Step 3:编译与运行 Add 算子(验证全流程闭环)

1、生成包含完整 CANN 初始化、数据交互、资源释放逻辑的测试代码,为后续编译运行 Add 算子提供基础执行文件

1cat > add_op.cpp << EOF
2#include <acl/acl.h>
3#include <acl/acl_rt.h>
4#include <stdio.h>
5#include <stdlib.h>
6
7int main() {
8   // 1. 初始化CANN
9   int ret = aclInit(NULL);
10   if (ret != 0) {
11       fprintf(stderr, "[ERROR] CANN初始化失败!错误码:%d\n", ret);
12       return -1;
13   }
14   printf("[INFO] CANN初始化成功\n");
15
16   // 2. 绑定NPU设备
17   int device_id = 0;
18   ret = aclrtSetDevice(device_id);
19   if (ret != 0) {
20       fprintf(stderr, "[ERROR] 绑定设备%d失败!错误码:%d\n", device_id, ret);
21       aclFinalize();
22       return -1;
23   }
24   printf("[INFO] 成功绑定NPU设备 %d\n", device_id);
25
26   // 3. 准备主机数据
27   const int data_size = 4;
28   float x_host[data_size] = {1.0f, 2.0f, 3.0f, 4.0f};
29   float y_host[data_size] = {5.0f, 6.0f, 7.0f, 8.0f};
30   float z_host[data_size] = {0.0f};
31   size_t mem_bytes = data_size * sizeof(float);
32
33   // 4. 分配设备内存(用编译器推荐的 ACL_MEM_MALLOC_NORMAL_ONLY)
34   void* x_device = NULL;
35   void* y_device = NULL;
36   ret = aclrtMalloc(&x_device, mem_bytes, ACL_MEM_MALLOC_NORMAL_ONLY);
37   ret |= aclrtMalloc(&y_device, mem_bytes, ACL_MEM_MALLOC_NORMAL_ONLY);
38   if (ret != 0) {
39       fprintf(stderr, "[ERROR] 设备内存分配失败!错误码:%d\n", ret);
40       goto CLEAN;
41   }
42   printf("[INFO] 设备侧内存分配成功\n");
43
44   // 5. 主机→设备拷贝(用编译器推荐的 ACL_MEMCPY_HOST_TO_DEVICE)
45   ret = aclrtMemcpy(x_device, mem_bytes, x_host, mem_bytes, ACL_MEMCPY_HOST_TO_DEVICE);
46   ret |= aclrtMemcpy(y_device, mem_bytes, y_host, mem_bytes, ACL_MEMCPY_HOST_TO_DEVICE);
47   if (ret != 0) {
48       fprintf(stderr, "[ERROR] 主机→设备拷贝失败!错误码:%d\n", ret);
49       goto CLEAN;
50   }
51   printf("[INFO] 主机→设备数据传输成功\n");
52
53   // 6. 主机侧计算(验证流程)
54   for (int i = 0; i < data_size; i++) {
55       z_host[i] = x_host[i] + y_host[i];
56   }
57
58   // 7. 打印结果
59   printf("[INFO] 计算完成!结果:\n");
60   for (int i = 0; i < data_size; i++) {
61       printf("%.1f + %.1f = %.1f\n", x_host[i], y_host[i], z_host[i]);
62   }
63
64// 资源释放
65CLEAN:
66   if (x_device) aclrtFree(x_device);
67   if (y_device) aclrtFree(y_device);
68   aclrtResetDevice(device_id);
69   aclFinalize();
70   printf("[INFO] 所有资源已释放,程序退出\n");
71
72   return ret == 0 ? 0 : -1;
73}
74EOF
75

在这里插入图片描述

2、编译、运行

1# 编译(宏名完全匹配,类型无错误)
2g++ add_op.cpp -o add_op \
3-I$ASCEND_HOME_PATH/include \
4-L$ASCEND_HOME_PATH/lib64 \
5-lascendcl -lcce -std=c++11
6
7# 运行
8./add_op
9

在这里插入图片描述

3、完整验证了 CANN 环境的核心功能

  • CANN 初始化:aclInit成功,说明环境配置正确
  • 设备绑定:aclrtSetDevice成功,NPU 设备可正常访问
  • 内存操作:设备侧内存分配(aclrtMalloc)和数据传输(aclrtMemcpy)无错误,硬件交互链路通畅
  • 结果正确:加法计算结果符合预期,流程闭环验证通过

在这里插入图片描述

当前程序的计算部分在主机侧完成,若要利用 NPU 算力,可基于现有框架扩展

  • 调用内置 Add 算子(利用acl_op.h):之前提到的aclOpExecute接口,只需补充#include <acl/acl_op.h>,并按规则创建张量、调用"Add"算子,即可在 NPU 上执行计算
  • 自定义核函数:当环境包含acl_kernel.h和aicpu_kernel_runtime库后,可将加法逻辑写成 AICPU 核函数,用aclrtLaunchKernel启动,实现真正的设备侧并行计算

CANN 性能测试:设备状态与算力验证实测

查看设备基本信息

1、查看设备基本信息

  • npu-smi info的输出来看,我的 NPU 设备状态完全正常且空闲
1npu-smi info
2

在这里插入图片描述

创建性能测试代码

1、数据传输性能测试代码

1# 生成数据传输性能测试文件 cann_perf_memcpy.cpp
2cat > cann_perf_memcpy.cpp << EOF
3#include <acl/acl.h>
4#include <acl/acl_rt.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <time.h>
8#include <stdint.h>
9
10// 计时工具:获取当前时间(微秒,避免系统时间波动)
11static uint64_t get_current_us() {
12   struct timespec ts;
13   clock_gettime(CLOCK_MONOTONIC, &ts);  // CLOCK_MONOTONIC:单调递增时间,不受系统时间修改影响
14   return (uint64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
15}
16
17int main() {
18   // 1. 初始化CANN
19   int ret = aclInit(NULL);
20   if (ret != 0) {
21       fprintf(stderr, "[ERROR] CANN初始化失败!错误码:%d\n", ret);
22       return -1;
23   }
24   printf("[INFO] CANN初始化成功\n");
25
26   // 2. 绑定NPU设备(你的设备ID是0,和npu-smi输出一致)
27   int device_id = 0;
28   ret = aclrtSetDevice(device_id);
29   if (ret != 0) {
30       fprintf(stderr, "[ERROR] 绑定设备%d失败!错误码:%d\n", device_id, ret);
31       aclFinalize();
32       return -1;
33   }
34   printf("[INFO] 成功绑定NPU设备 %d(型号:910B3)\n\n", device_id);
35
36   // 3. 定义测试数据量(覆盖小/中/大场景,适配你的62GB空闲HBM)
37   // 单位:float元素数(1个float=4字节,如1048576元素=4MB)
38   size_t test_element_counts[] = {
39       1048576,        // 4MB
40       26214400,       // 100MB
41       262144000,      // 1GB
42       1310720000      // 5GB(不超过剩余HBM,避免内存不足)
43   };
44   int test_count = sizeof(test_element_counts) / sizeof(test_element_counts[0]);
45
46   // 4. 循环测试不同数据量的传输性能
47   for (int i = 0; i < test_count; i++) {
48       size_t element_count = test_element_counts[i];
49       size_t mem_bytes = element_count * sizeof(float);  // 总字节数
50       float data_mb = mem_bytes / 1024.0 / 1024.0;       // 转换为MB,方便显示
51
52       // 4.1 分配主机内存(用malloc,避免栈溢出)
53       float* host_data = (float*)malloc(mem_bytes);
54       if (host_data == NULL) {
55           fprintf(stderr, "[ERROR] 分配主机内存失败(数据量:%.2f MB)\n", data_mb);
56           continue;
57       }
58
59       // 4.2 分配NPU设备内存
60       void* dev_data = NULL;
61       ret = aclrtMalloc(&dev_data, mem_bytes, ACL_MEM_MALLOC_NORMAL_ONLY);
62       if (ret != 0) {
63           fprintf(stderr, "[ERROR] 分配设备内存失败(数据量:%.2f MB)!错误码:%d\n", data_mb, ret);
64           free(host_data);
65           continue;
66       }
67
68       // 4.3 初始化主机数据(填随机数,避免编译器“空数据优化”影响测试)
69       srand((unsigned int)time(NULL));
70       for (size_t j = 0; j < element_count; j++) {
71           host_data[j] = (float)rand() / RAND_MAX;  // 随机数范围:0~1
72       }
73
74       // -------------------------- 测试1:主机→NPU(H2D)传输 --------------------------
75       uint64_t start = get_current_us();
76       const int repeat = 10;  // 重复10次取平均,减少单次误差
77       for (int k = 0; k < repeat; k++) {
78           ret = aclrtMemcpy(dev_data, mem_bytes, host_data, mem_bytes, ACL_MEMCPY_HOST_TO_DEVICE);
79           if (ret != 0) {
80               fprintf(stderr, "[ERROR] H2D传输失败!错误码:%d\n", ret);
81               break;
82           }
83       }
84       uint64_t end = get_current_us();
85       float avg_time_ms = (end - start) / (repeat * 1000.0);  // 平均单次耗时(毫秒)
86       float bandwidth_mb_s = (mem_bytes * repeat) / (end - start) * 1000.0 / 1024.0 / 1024.0;  // 带宽(MB/s)
87
88       // 打印H2D结果
89       printf("【数据量:%.2f MB】\n", data_mb);
90       printf("  主机→NPU:平均耗时 %.2f ms,带宽 %.2f MB/s\n", avg_time_ms, bandwidth_mb_s);
91
92       // -------------------------- 测试2:NPU→主机(D2H)传输 --------------------------
93       start = get_current_us();
94       for (int k = 0; k < repeat; k++) {
95           ret = aclrtMemcpy(host_data, mem_bytes, dev_data, mem_bytes, ACL_MEMCPY_DEVICE_TO_HOST);
96           if (ret != 0) {
97               fprintf(stderr, "[ERROR] D2H传输失败!错误码:%d\n", ret);
98               break;
99           }
100       }
101       end = get_current_us();
102       avg_time_ms = (end - start) / (repeat * 1000.0);
103       bandwidth_mb_s = (mem_bytes * repeat) / (end - start) * 1000.0 / 1024.0 / 1024.0;
104
105       // 打印D2H结果
106       printf("  NPU→主机:平均耗时 %.2f ms,带宽 %.2f MB/s\n\n", avg_time_ms, bandwidth_mb_s);
107
108       // 4.4 释放内存(避免内存泄漏)
109       free(host_data);
110       aclrtFree(dev_data);
111   }
112
113   // 5. 资源释放
114   aclrtResetDevice(device_id);
115   aclFinalize();
116   printf("[INFO] 所有测试完成,资源已释放\n");
117
118   return 0;
119}
120EOF
121

在这里插入图片描述

编译
1# 编译:链接CANN库和计时依赖(-lpthread)
2g++ cann_perf_memcpy.cpp -o cann_perf_memcpy \
3-I$ASCEND_HOME_PATH/include \
4-L$ASCEND_HOME_PATH/lib64 \
5-lascendcl -lcce -lpthread -std=c++11
6

在这里插入图片描述

运行测试

数据来看,基于 Ascend 910B3 NPU 的内存拷贝性能表现出数据量越大、NPU→主机方向传输越稳定且带宽更高的特点:

  • 小数据量(4MB)时,因固定开销占比高,带宽仅 10 - 11MB/s,远低于硬件理论带宽(最高 1.07TB/s)
  • 中等数据量(100MB)时,带宽提升至 19 - 20MB/s,开始接近有效传输区间
  • 超大数据量(1000MB)时,NPU→主机带宽仍稳定在 20.95MB/s,而主机→NPU 带宽因内存调度压力降至 15.47MB/s
1# 运行性能测试
2./cann_perf_memcpy
3

在这里插入图片描述


深度测评解析 CANN:从 ACL 到自定义算子,解锁昇腾计算的全部潜能》 是转载文章,点击查看原文


相关推荐


Python 的内置函数 input
IMPYLH2025/11/13

Python 内建函数列表 > Python 的内置函数 input Python 的内置函数 input() 是一个用于获取用户输入的标准函数,它会暂停程序执行,等待用户在控制台输入内容并按回车键确认。这个函数在交互式程序和需要用户参与的脚本中非常有用。 基本用法 input() 函数的基本语法如下: user_input = input([prompt]) 其中: prompt 是一个可选参数,用于显示提示信息,告诉用户需要输入什么内容函数返回用户输入的内容,以字符串形式保存


C++笔记——STL list
报错小能手2025/11/11

1. list 基本概念 什么是list? 双向链表:每个元素包含指向前后元素的指针,形成链式结构 核心特性(与vector/deque对比): 特性vectordequelist随机访问✅ O(1)✅ O(1)❌ O(n)头部插入删除❌ O(n)✅ O(1)✅ O(1)尾部插入删除✅ O(1)✅ O(1)✅ O(1)中间插入删除❌ O(n)❌ O(n)✅ O(1)内存布局连续分段连续完全分散 2. list 的基本使用 头文件和声明: cpp #include <list


各个系统的 docker安装
Qayrup2025/11/9

docker简介 Docker 是一组平台即服务的产品,它基于操作系统层级的虚拟化技术,将软件与其依赖项打包为容器。本文将介绍在 CentOS 8 操作系统中安装 Docker 服务,并解决镜像源无法访问的问题。 centos8 安装 1 检查版本 Docker 要求 CentOS 的内核版本至少高于 3.10,可以用命令uname -r查看 2 安装 Docker 所需依赖 //执行命令yum install -y yum-utils device-mapper-persistent-dat


理解PostgreSQL中的数据块
WarriorTan2025/11/7

PG的数据块大小,默认是8KB,可以调整为16K或者 32K吗? PostgreSQL的数据块大小默认为8KB,可以将其调整为16KB或32KB。数据块大小需要在‌编译安装‌PostgreSQL时通过配置参数指定,例如使用configure.ac中的--with-blocksize选项进行设置 。需要注意的是,一旦数据库初始化完成,数据块大小就无法再修改 。 数据块的行指针都包括哪些信息? 具体来说,行指针是一个32位的数字,其结构被划分为三个部分: 行内容的偏移量‌:占用15位(bit


C#.NET Random 深入解析:随机数生成原理与最佳实践
唐青枫2025/11/4

简介 Random 是 .NET 中 System 命名空间提供的一个类,用于生成伪随机数。它广泛应用于需要随机化操作的场景,如生成随机数据、模拟、游戏开发或测试用例生成。 伪随机数生成 在计算机中,Random 类用于生成伪随机数,这些数值在一定程度上看起来是随机的,但它们实际上是通过数学公式从一个初始种子值计算得到的,因此称之为“伪随机数”。 广泛应用 Random 类常用于游戏开发、模拟、加密等场景。在许多应用中,生成随机数或随机选择某个元素是常见的需求。 注意: Random


设计模式的原则有哪些?
你的人类朋友2025/10/31

前言 温馨提示 对于原本不太熟悉设计模式的人来说(比如在下),这些内容是需要一定的时间消化的!慢慢来 😆 👋 你好啊,我是你的人类朋友! 今天说说设计模式的原则有哪些! 在开发用户权限系统时,你是否遇到过这样的问题: 当创建新的管理员用户类型时,发现它无法兼容普通用户的所有方法,导致系统中到处需要判断用户类型? 让我们了解设计模式的基本原则,构建更健壮的软件架构~ 健壮是啥意思? 健壮是指软件系统在面对变化和复杂性时,能够保持稳定运行的能力。也就是耐造的能力。 正文 SOLID 原则


Java的包装类
麦麦鸡腿堡2025/10/29

包装类(Wrapper)的分类: 1.针对八种基本数据类型相应的引用类型--包装类 2.有了类的特点,就可以调用类中的方法 *黄色框内都是number的子类,number是Ojbect子类,黑色框中的包装类是独立的,Ojbect子类 //boolean-Boolean-父类Object //char-Character-父类Object //byte-Byte-父类number-父类Object //int-Integer-父类number-父类Object //long-Long


17_AI智能体开发架构搭建之Flask集成swagger在线文档实践
腾飞开源2025/10/26

一、为什么需要Swagger集成? 在微服务架构和前后端分离的现代开发模式中,API文档承担着关键角色: 开发效率:前后端并行开发,减少沟通成本 接口契约:明确的请求/响应规范,避免歧义 测试便利:直接在文档界面测试API 团队协作:新成员快速理解接口设计 客户端生成:自动生成多种语言客户端代码 AI智能体系统设计相关文章: 👉《01_AI智能体系统设计之系统架构设计》 👉《02_AI智能体系统设计之钉钉消息处理流程设计》 👉《03_AI智能体系统设计之Ag


C#.NET SqlKata 使用详解:优雅构建动态 SQL 查询
唐青枫2025/10/23

简介 在复杂项目中,为了保持 SQL 灵活性与可读性,开发者往往需要手写大量拼接字符串或使用 ORMs 附带的 LINQ,但两者各有局限:手写拼接易出错、难以维护;LINQ 在某些场景下生成的 SQL 不够直观或性能不佳。 SqlKata 是一款轻量级、数据库无关的查询构建器(Query Builder),提供—— 流式 API,链式调用拼装 SQL 可切换编译器,支持多种数据库方言(SQL Server、PostgreSQL、MySQL、SQLite、Oracle 等) 语


深度解析 DNS服务:从基础原理到自建 DNS 服务器完整指南
coder4_2025/10/22

目录 一、DNS服务基础知识 1、DNS 2、域名 ①域名组成 ②域名管理与结构 3、DNS服务工作原理 ①主机中DNS客户端配置 ②DNS服务解析域名流程 4、DNS查询类型 ①递归查询 ②迭代查询 5、DNS解析类型 6、hosts文件 7、完整的域名查询请求流程 二、DNS检测工具 1、dig命令 2、host命令 3、nslookup命令 4、rndc命令 5、whois命令 三、自建DNS服务 1、ubuntu中的systemd-reso

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0