深度学习编译器的演进:从计算图到跨硬件部署的自动化之路
第一章 问题的诞生——深度学习部署的硬件困境
1.1 计算图的理想化抽象
什么是计算图?
想象你正在组装乐高积木。每个积木块代表一个数学运算(如加法、乘法),积木之间的连接代表数据流动。深度学习框架正是用这种"积木拼接"的方式构建神经网络,这种形式化表达称为计算图(Computational Graph)。
代码解析:一个真实的神经网络
# 导入PyTorch深度学习框架
import torch
import torch.nn as nn# 定义卷积神经网络类(继承自nn.Module基类)
class CNN(nn.Module):def __init__(self):super().__init__() # 初始化父类# 定义网络层(类似乐高积木的组件)self.conv1 = nn.Conv2d( # 2D卷积层in_channels=3, # 输入通道数(RGB图像为3通道)out_channels=64, # 输出通道数(即卷积核数量)kernel_size=3 # 卷积核尺寸3x3)self.relu = nn.ReLU() # 激活函数层(使网络具有非线性)self.fc = nn.Linear( # 全连接层(即Dense层)in_features=512, # 输入特征维度out_features=10 # 输出类别数(假设是10分类问题))# 定义数据前向传播路径(描述数据如何流过各层)def forward(self, x):x = self.conv1(x) # 输入数据经过卷积层x = self.relu(x) # 通过ReLU激活函数x = x.view(x.size(0), -1) # 展平操作(将4D张量转为2D)x = self.fc(x) # 通过全连接层return x # 返回最终输出
对应的数据流图如下(括号内为张量形状):
输入图像(batch,3,224,224)↓
Conv2D层 → 输出(batch,64,222,222) ↓
ReLU激活 → 输出(batch,64,222,222)↓
Flatten展平 → 输出(batch,64*222*222=3175072)↓
Dense全连接 → 输出(batch,10)
关键理解:计算图只描述"要做什么"(What to do),而不关心"如何做"(How to do)。
1.2 硬件现实的多样性挑战
硬件特性对比表(扩展解析)
硬件类型 | 计算单元 | 内存层次结构 | 适合场景 |
---|---|---|---|
GPU | 数千个流处理器 | 全局内存(大容量高延迟)+共享缓存(低延迟小容量) | 并行处理大批量数据 |
CPU | 多核(通常4-64核) | L1/L2/L3多级缓存(平衡延迟和容量) | 复杂逻辑控制流 |
NPU | 专用矩阵乘法单元 | 片上内存(超低延迟但容量受限) | 特定神经网络运算加速 |
FPGA | 可编程逻辑门阵列 | 分布式Block RAM(灵活但容量小) | 定制化计算加速 |
三大核心问题详解
问题1:算子实现差异
以卷积运算为例,不同硬件需要完全不同的实现方式:
GPU实现(CUDA伪代码):
__global__ void conv2d_gpu(float* input, float* kernel, float* output) {int bid = blockIdx.x; // 批量索引int tid = threadIdx.x; // 线程索引// 每个线程计算输出特征图的一个像素float sum = 0;for(int c=0; c<in_channels; c++){for(int i=0; i<3; i++){for(int j=0; j<3; j++){sum += input[bid][c][tid_x+i][tid_y+j] * kernel[c][i][j];}}}output[bid][tid] = sum;
}
CPU优化实现(C++ SIMD伪代码):
void conv2d_cpu(float* input, float* kernel, float* output) {#pragma omp parallel for // 多核并行for(int b=0; b<batch; b++){for(int c=0; c<channels; c++){__m256 simd_accum; // AVX256向量寄存器for(int i=0; i<height; i+=8){ // 8元素向量处理simd_accum = _mm256_load_ps(&input[b][c][i]);// 向量化乘加运算simd_accum = _mm256_fmadd_ps(simd_accum, kernel_vec, simd_accum);_mm256_store_ps(&output[b][c][i], simd_accum);}}}
}
问题2:内存访问模式冲突
不同硬件对内存访问的敏感性:
-
GPU内存墙:RTX 3090显存带宽936GB/s,但需要连续访问才能达到峰值性能。若数据布局不合理,实际带宽利用率可能不足30%。
# 不良内存布局(通道最后) tensor = torch.randn(224, 224, 3) # HWC格式 # GPU更偏好通道优先布局 tensor = tensor.permute(2, 0, 1) # CHW格式
-
CPU缓存行:现代CPU缓存行通常为64字节。假设处理float32数据(4字节/元素),每次缓存加载可获取16个连续元素。非连续访问会导致缓存频繁失效。
问题3:计算精度陷阱
常见精度问题案例:
# 训练时使用FP32精度
model.train()
output = model(fp32_input)# 部署时误用FP16
model.half() # 转换为FP16
output = model(fp16_input) # 可能溢出或精度不足
不同硬件的精度支持差异:
硬件类型 | FP32支持 | FP16支持 | INT8量化 |
---|---|---|---|
GPU | 完整 | TensorCore加速 | 需要特殊指令 |
NPU | 部分 | 主要支持 | 专用加速单元 |
手机SoC | 有限 | 可选 | 主流方案 |
1.3 早期解决方案的局限
人工优化库的工作流程
典型案例:卷积算子的多平台实现
假设需要新增一种Dilated Conv(空洞卷积):
平台 | 开发工作内容 | 耗时估算 |
---|---|---|
NVIDIA GPU | 编写CUDA内核并优化内存访问 | 3人周 |
Intel CPU | 实现AVX512向量化版本 | 2人周 |
ARM Mali | 适配OpenCL内核 | 2.5人周 |
华为NPU | 转换为AscendCL专用指令 | 3人周 |
总成本:约10人周(2.5个月),且需持续维护多个代码库。
人工优化的两大缺陷
缺陷1:算子实现碎片化
假设某框架有200个算子,支持5种硬件平台:
总维护量 = 200算子 × 5平台 = 1000个实现版本
当新增一个算子时:
新增工作量 = 1算子 × 5平台 = 5个实现版本
缺陷2:跨算子优化缺失
考虑如下计算图:
Conv → ReLU → Add
人工优化可能得到:
# 三个独立内核
conv_out = cudnn.conv(input)
relu_out = cudnn.relu(conv_out)
output = cudnn.add(relu_out, residual)
而编译器可进行算子融合优化:
# 单个融合内核
output = cudnn.fused_conv_relu_add(input, residual)
优化后的收益:
- 内核启动开销减少至1/3
- 中间结果内存占用降低1/2
- 数据访存次数减少至2/5
小结:编译器为何必须存在?
通过一个现实案例理解必要性:
场景:某公司开发人脸识别系统,需在以下设备部署:
- 云端:NVIDIA A100 GPU
- 边缘端:Intel i7-11800H CPU
- 终端:华为Mate 40手机NPU
传统方式:
编译器方式:
这种自动化转型带来的收益:
- 开发周期从3个月缩短至2周
- 硬件利用率平均提升40%
- 维护成本降低80%
至此我们理解了:为什么需要机器学习编译器,将统一的计算图描述转化为各硬件的高效实现。下一章将深入解析编译器的核心技术。
第二章 技术的演变——编译器架构的进化之路
2.1 第一代:模板化代码生成
2.1.1 Halide的核心思想
Halide将算法描述与调度策略分离,其核心思想可用以下公式表达:
程序 = 算法 + 调度 \text{程序} = \text{算法} + \text{调度} 程序=算法+调度
// 算法定义:3x3均值滤波
Func blur;
Var x, y; // 定义二维迭代变量
blur(x, y) = (input(x-1, y) + input(x, y) + input(x+1, y)) / 3;// 调度策略配置
blur.tile(x, y, xo, yo, xi, yi, 256, 256) // 分块:外层循环256x256.vectorize(xi, 8) // 内层循环向量化,每次处理8个元素.parallel(yo); // 外层循环并行执行
代码解析
代码片段 | 功能解释 | 优化目标 |
---|---|---|
tile(256, 256) | 将计算划分为256x256的块 | 提高缓存利用率 |
vectorize(xi, 8) | 对内层循环进行8元素向量化 | 利用SIMD指令 |
parallel(yo) | 外层循环并行执行 | 多核并行加速 |
2.1.2 工作流程解析
性能测试案例
对3x3卷积进行不同调度策略的测试:
调度策略 | 执行时间(ms) | 加速比 |
---|---|---|
基线顺序执行 | 152 | 1.0x |
分块+向量化 | 68 | 2.24x |
分块+向量化+并行 | 23 | 6.6x |
2.1.3 局限性分析
问题1:策略搜索空间爆炸
假设有5个调度原语(分块、向量化、并行化、循环展开等),每个有3种参数选择:
组合总数 = 3 5 = 243 \text{组合总数} = 3^5 = 243 组合总数=35=243
人工遍历所有组合需要至少243次实验。
问题2:硬件适配困难
同一调度在不同硬件的表现:
硬件平台 | 最佳分块尺寸 | 向量化宽度 |
---|---|---|
Intel i7 | 128x128 | 8 |
ARM A72 | 64x64 | 4 |
NVIDIA P100 | 256x256 | 16 |
问题3:跨算子优化缺失
// 两个独立算子
Func conv = ...;
Func relu = ...;// 无法进行融合优化
conv.compute_root();
relu.compute_root();
2.2 第二代:基于中间表示的优化
2.2.1 TVM的多级IR体系
关键IR解析
IR层级 | 抽象级别 | 主要功能 |
---|---|---|
Relay IR | 计算图级 | 全局优化、算子融合 |
TE | 张量运算级 | 算子实现定义 |
TIR | 低级循环优化 | 硬件特定优化 |
2.2.2 Relay优化实例
原始计算图:
def original():conv = relay.nn.conv2d(x, w1)relu = relay.nn.relu(conv)bn = relay.nn.batch_norm(relu)pool = relay.nn.max_pool2d(bn)return pool
优化过程:
# 应用优化规则序列
with relay.transform.PassContext(opt_level=3):mod = relay.transform.Sequential([relay.transform.FoldConstant(),relay.transform.FuseOps(fuse_opt_level=2),relay.transform.AlterOpLayout()])(mod)
优化后计算图:
FusedConvReLUBN → MaxPool2D
优化效果对比
优化阶段 | 算子数量 | 内存占用(MB) | 执行时间(ms) |
---|---|---|---|
原始计算图 | 4 | 1.2 | 15.3 |
常量折叠 | 3 | 1.1 | 14.7 |
算子融合 | 2 | 0.9 | 11.2 |
布局转换 | 2 | 0.8 | 9.1 |
2.2.3 TE调度系统
矩阵乘法优化示例:
def te_matmul(A, B):m, k = A.shapek, n = B.shaperc = te.reduce_axis((0, k))return te.compute((m, n), lambda i, j: te.sum(A[i, rc] * B[rc, j], axis=rc))s = te.create_schedule(matmul.op)# 分块策略
xo, xi = s[matmul].split(matmul.op.axis[0], factor=32)
yo, yi = s[matmul].split(matmul.op.axis[1], factor=32)# 循环重排序
s[matmul].reorder(xo, yo, xi, yi)# 线程绑定
s[matmul].bind(xo, te.thread_axis("blockIdx.x"))
s[matmul].bind(yo, te.thread_axis("blockIdx.y"))
s[matmul].bind(xi, te.thread_axis("threadIdx.x"))# 生成CUDA代码
target = tvm.target.Target("cuda")
mod = tvm.build(s, [A, B, matmul], target)
调度策略影响
优化策略 | 执行时间(ms) | 加速比 |
---|---|---|
基线顺序 | 12.5 | 1.0x |
分块+重排序 | 5.3 | 2.36x |
线程绑定 | 2.1 | 5.9x |
2.3 混合式调度系统
2.3.1 MLIR基础设施解析
MLIR采用模块化的方言(Dialect)设计,核心架构如下:
前端语言 → 高层方言(如TensorFlow Graph) ↓ 渐进式Lowering
中间方言(如Affine Dialect) ↓
底层方言(如LLVM IR) ↓
硬件指令
关键机制详解
-
可扩展方言体系:
// TensorFlow Lite方言示例 func @main(%input: tensor<1x224x224x3xf32>) -> tensor<1x1000xf32> {%0 = "tfl.conv_2d"(%input, %filter, %bias) {...} : (tensor<1x224x224x3xf32>, ...) -> tensor<1x112x112x64xf32>%1 = "tfl.relu"(%0) : (...) -> tensor<1x112x112x64xf32>return %1 : tensor<1x1000xf32> }
-
渐进式Lowering过程:
高层操作 → 循环嵌套 → 向量指令 → 硬件指令
-
多级IR共存机制:
module {func @mixed_ir(%arg: tensor<f32>) -> tensor<f32> {// 高层操作%0 = "tfl.custom_op"(%arg) : (...) -> tensor<f32>// 底层循环结构affine.for %i = 0 to 100 {%1 = load %0[%i] : memref<100xf32>%2 = "llvm.intr.fmul"(%1, %1) : (...) -> f32store %2, %0[%i] : memref<100xf32>}return %0 : tensor<f32>} }
2.3.2 架构对比分析
特性 | Halide | TVM | MLIR |
---|---|---|---|
优化抽象层次 | 循环级 | 计算图+循环级 | 全层次 |
硬件扩展性 | 需重写调度策略 | 新增目标后端 | 定义新方言 |
跨优化阶段集成 | 无 | 有限 | 原生支持 |
自动优化能力 | 无 | 基于搜索 | 基于规则+搜索 |
MLIR创新点:
- 统一编译基础设施:支持从算法到硬件的全栈表达
- 方言可组合性:不同抽象层次的IR可以共存交互
- 形式化语义:每个方言都有严格定义的Operation和Type系统
第三章 解决方案的实现——TVM编译器的技术剖析
3.1 计算图优化阶段:全局视野的优化艺术
3.1.1 优化规则分类
TVM的计算图优化器包含四大类优化策略:
# 优化器配置示例
seq = tvm.transform.Sequential([# 基本优化relay.transform.EliminateCommonSubexpr(),relay.transform.FoldConstant(),# 算子级优化relay.transform.FuseOps(fuse_opt_level=2),relay.transform.CombineParallelDense(min_num_branches=3),# 内存优化relay.transform.AlterOpLayout(),relay.transform.CanonicalizeCast(),# 硬件特定优化relay.transform.ConvertLayout({"nn.conv2d": ["NHWC", "default"]})
])
典型优化案例:算子融合
原始计算图:
Conv2D → ReLU → BatchNorm → MaxPool2D
优化过程:
# 应用融合规则
mod = relay.transform.FuseOps(fuse_opt_level=3)(mod)
优化后计算图:
FusedConv2D_ReLU_BN → MaxPool2D
3.1.2 优化效果量化分析
在ResNet-50上的优化效果:
优化策略 | 算子数量 | 内存占用(MB) | 推理时延(ms) |
---|---|---|---|
无优化 | 217 | 1245 | 152.3 |
常量折叠 | 203 | 1128 | 144.7 |
算子融合 | 98 | 876 | 112.4 |
布局转换 | 98 | 832 | 97.6 |
3.2 张量表达式系统:硬件无关的算子定义
3.2.1 表达式定义范式
卷积算子的完整定义示例:
def conv2d_nchw(data, kernel, stride, padding):"""NCHW布局的卷积计算定义Args:data: 输入张量,形状[N,C,H,W]kernel: 卷积核,形状[O,C,Kh,Kw]stride: 步长padding: 填充"""N, C, H, W = data.shapeO, _, Kh, Kw = kernel.shape# 计算输出尺寸out_h = (H + 2*padding - Kh) // stride + 1out_w = (W + 2*padding - Kw) // stride + 1# 定义规约轴rc = te.reduce_axis((0, C), name="rc")rh = te.reduce_axis((0, Kh), name="rh")rw = te.reduce_axis((0, Kw), name="rw")# 填充计算padded = te.compute((N, C, H+2*padding, W+2*padding),lambda n, c, h, w: te.if_then_else(te.any(h < padding, h >= H+padding, w < padding, w >= W+padding),0.0,data[n, c, h-padding, w-padding]),name="padded")# 卷积计算return te.compute((N, O, out_h, out_w),lambda n, o, h, w: te.sum(padded[n, rc, h*stride + rh, w*stride + rw] * kernel[o, rc, rh, rw],axis=[rc, rh, rw]),name="conv2d")
3.2.2 自动微分实现
TVM支持自动微分功能:
# 定义计算
x = te.var("x")
y = te.var("y")
z = te.sin(x) * te.log(y)# 创建微分计算
[dz_dx, dz_dy] = te.differentiate(z, [x, y])# 生成计算代码
s = te.create_schedule([dz_dx.op, dz_dy.op])
微分结果:
∂ z ∂ x = cos ( x ) ⋅ ln ( y ) ∂ z ∂ y = sin ( x ) y \frac{\partial z}{\partial x} = \cos(x) \cdot \ln(y) \\ \frac{\partial z}{\partial y} = \frac{\sin(x)}{y} ∂x∂z=cos(x)⋅ln(y)∂y∂z=ysin(x)
3.3 调度优化原语:性能调优的武器库
3.3.1 核心调度原语分类
类别 | 原语示例 | 优化目标 |
---|---|---|
循环变换 | split, reorder, tile | 数据局部性优化 |
并行化 | parallel, vectorize | 多级并行加速 |
内存管理 | cache_read, double_buffer | 减少内存访问延迟 |
硬件适配 | bind, storage_align | 硬件特性匹配 |
3.3.2 矩阵乘法的完整优化
def optimize_matmul(s, C):"""矩阵乘法调度优化"""# 分块策略x, y = s[C].op.axisxo, xi = s[C].split(x, factor=32)yo, yi = s[C].split(y, factor=32)s[C].reorder(xo, yo, xi, yi)# 缓存写入优化CC = s.cache_write(C, "local")# 自动线程绑定s[C].bind(xo, te.thread_axis("blockIdx.x"))s[C].bind(yo, te.thread_axis("blockIdx.y"))s[C].bind(xi, te.thread_axis("threadIdx.x"))s[C].bind(yi, te.thread_axis("threadIdx.y"))# 向量化优化s[CC].vectorize(CC.op.axis[-1])# 循环展开ko, ki = s[CC].split(s[CC].op.reduce_axis[0], factor=4)s[CC].unroll(ki)
3.3.3 优化效果对比
在1024x1024矩阵乘法上的测试结果:
优化阶段 | 执行时间(ms) | GFLOPS | 加速比 |
---|---|---|---|
基线实现 | 12.5 | 172.3 | 1.0x |
分块+重排序 | 5.3 | 406.7 | 2.36x |
线程绑定 | 2.1 | 1024.8 | 5.95x |
向量化+展开 | 1.4 | 1536.0 | 8.93x |
3.4 自动调优系统:机器学习的魔法
3.4.1 AutoTVM架构设计
组件详解:
-
搜索空间定义:描述可调节的调度参数
cfg = autotvm.get_config() cfg.define_knob("tile_size", [32, 64, 128]) cfg.define_knob("vectorize", [4, 8, 16])
-
成本模型:XGBoost预测性能
y ^ = ∑ i = 1 n f i ( x ) , f i ∈ F \hat{y} = \sum_{i=1}^n f_i(x), \quad f_i \in \mathcal{F} y^=i=1∑nfi(x),fi∈F -
参数搜索:采用并行贝叶斯优化
x n e x t = arg max x UCB ( x ) = μ ( x ) + κ σ ( x ) x_{next} = \arg\max_x \text{UCB}(x) = \mu(x) + \kappa \sigma(x) xnext=argxmaxUCB(x)=μ(x)+κσ(x)
3.4.2 调优收敛过程
- 随机采样阶段:探索全局空间
- 模型引导阶段:快速逼近最优
- 局部优化阶段:精细调整参数
3.4.3 实际调优案例
在NVIDIA T4 GPU上调优ResNet-50:
阶段 | 时间消耗 | 最佳时延(ms) | 加速比 |
---|---|---|---|
初始实现 | 0 | 15.2 | 1.0x |
随机搜索100次 | 2h | 9.8 | 1.55x |
贝叶斯优化300次 | 6h | 6.7 | 2.27x |
专家手动优化 | 40h | 6.5 | 2.34x |
3.4.4 技术实现总结
TVM通过分层优化体系实现自动化编译:
关键创新点:
- 跨层优化:在不同抽象层级实施针对性优化
- 可组合性:调度原语可任意组合产生新策略
- 可移植性:同一优化策略适配多硬件后端
实验数据显示,TVM在典型工作负载上可获得:
- 相比手工优化库90%以上的性能
- 比原生框架3-10倍的加速比
- 跨硬件平台的一致优化效果
这种技术突破使得深度学习模型可以真正实现"一次开发,处处高效运行"。下一章将通过实际部署案例,展示TVM在不同场景中的实践效果。
第四部分:实践验证——跨硬件平台的性能对比
4.1 GPU平台测试
在NVIDIA T4上的ResNet-50推理:
方法 | 时延(ms) | 内存使用(MB) | 功耗(W) |
---|---|---|---|
PyTorch原生 | 15.2 | 1203 | 72 |
TVM自动优化 | 6.8 | 874 | 65 |
手工CUDA实现 | 6.5 | 832 | 63 |
4.2 CPU平台测试
Intel Xeon Gold 6248上的BERT推理:
优化方法 | 吞吐量(qps) | 时延(ms) | 加速比 |
---|---|---|---|
ONNX Runtime | 78 | 12.8 | 1.0x |
TVM自动调优 | 142 | 7.04 | 1.82x |
手工AVX512优化 | 155 | 6.45 | 1.99x |
4.3 边缘设备测试
树莓派4B上的MobileNetV2:
部署方式 | 时延(ms) | 内存峰值(MB) | 能耗(J) |
---|---|---|---|
TFLite | 143 | 82 | 5.3 |
TVM+AutoTVM | 97 | 64 | 3.8 |
手工NEON优化 | 89 | 58 | 3.5 |
第五部分:技术演进——编译器架构的未来方向
5.1 动态形状支持
传统静态编译与动态编译对比:
静态编译时间 = O ( 1 ) 动态编译开销 = O ( n ) \text{静态编译时间} = O(1) \\ \text{动态编译开销} = O(n) 静态编译时间=O(1)动态编译开销=O(n)
TVM的动态shape处理:
def dynamic_conv(data, weight):N = te.var("n")C = te.var("c")H = te.var("h")W = te.var("w")rc = te.reduce_axis((0, C))return te.compute((N, K, H-R+1, W-S+1),lambda n, k, h, w: te.sum(data[n, rc, h+rh, w+rw] * weight[k, rc, rh, rw],axis=[rc, rh, rw]))
5.2 异构计算协同
跨设备计算示例:
# GPU执行卷积
with tvm.target.cuda():conv_module = tvm.build(conv_sch, [data, weight, conv_out])# NPU执行全连接
with tvm.target.ascend():fc_module = tvm.build(fc_sch, [conv_out, fc_weight, output])# 数据流水线
gpu_stream = tvm.runtime.DeviceAPI.get_stream(0)
npudata = conv_module(data, weight)
gpu_stream.sync()
npuresult = fc_module(npudata)
5.3 自动化程度提升
未来编译器架构展望:
相关文章:

深度学习编译器的演进:从计算图到跨硬件部署的自动化之路
第一章 问题的诞生——深度学习部署的硬件困境 1.1 计算图的理想化抽象 什么是计算图? 想象你正在组装乐高积木。每个积木块代表一个数学运算(如加法、乘法),积木之间的连接代表数据流动。深度学习框架正是用这种"积木拼接…...

【数据结构】_顺序表经典算法OJ(力扣版)
目录 1. 移除元素 1.1 题目描述及链接 1.2 解题思路 1.3 程序 2. 合并两个有序数组 1.1 原题链接及题目描述 1.2 解题思路 1.3 程序 1. 移除元素 1.1 题目描述及链接 原题链接:27. 移除元素 - 力扣(LeetCode) 题目描述:…...

数据结构:队列篇
图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…...

第05章 17 Contour 过滤器介绍与例子
vtkContourFilter 是 VTK(Visualization Toolkit)中的一个关键类,用于从输入数据生成等值线或等值面。它是基于阈值的过滤器,可以从标量字段中提取等值线或等值面。vtkContourFilter 的核心功能是根据用户指定的值生成等值线或等值…...

【落羽的落羽 数据结构篇】顺序表
文章目录 一、线性表二、顺序表1. 概念与分类2. 准备工作3. 静态顺序表4. 动态顺序表4.1 定义顺序表结构4.2 顺序表的初始化4.3 检查空间是否足够4.3 尾部插入数据4.4 头部插入数据4.5 尾部删除数据4.6 头部删除数据4.7 在指定位置插入数据4.8 在指定位置删除数据4.9 顺序表的销…...

AI编程:如何编写提示词
这是小卷对AI编程工具学习的第2篇文章,今天讲讲如何编写AI编程的提示词,并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是:目标清晰明确,具有针对性,能引导模型理解问题 下面是两条提示词的对…...

DeepSeek-R1 论文解读 —— 强化学习大语言模型新时代来临?
近年来,人工智能(AI)领域发展迅猛,大语言模型(LLMs)为通用人工智能(AGI)的发展开辟了道路。OpenAI 的 o1 模型表现非凡,它引入的创新性推理时缩放技术显著提升了推理能力…...

高阶C语言|深入理解字符串函数和内存函数
文章目录 前言1.求字符串长度1.1 字符串长度函数:strlen模拟实现 2.长度不受限制的字符串函数2.1 字符串拷贝函数:strcpy模拟实现 2.2 字符串连接函数:strcat模拟实现 2.3 字符串比较函数:strcmp模拟实现 3.长度受限制的字符串函数…...

UE学习日志#17 C++笔记#3 基础复习3
19.2 [[maybe_unused]] 禁止编译器在未使用某些内容时发出警告 19.3 [[noreturn]] 永远不会把控制权返回给调用点 19.4 [[deprecated]] 标记为已弃用,仍然可以使用但是不鼓励使用 可以加参数表示弃用的原因[[deprecated("")]] 19.5 [[likely]]和[[un…...

团体程序设计天梯赛-练习集——L1-028 判断素数
前言 一道10分的题目,相对来说比较简单,思考的时候要仔细且活跃,有时候在写代码的时候一些代码的出现很多余,并且会影响最后的结果 L1-028 判断素数 本题的目标很简单,就是判断一个给定的正整数是否素数。 输入格式…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础图形库实现)
目录 基础图形库的抽象 抽象图形 抽象点 设计我们的抽象 实现我们的抽象 测试 抽象线 设计我们的抽象 实现我们的抽象 绘制垂直的和水平的线 使用Bresenham算法完成任意斜率的绘制 绘制三角形和矩形 矩形 三角形 实现 绘制圆,圆弧和椭圆 继续我们的…...

创新创业计划书|建筑垃圾资源化回收
目录 第1部分 公司概况........................................................................ 1 第2部分 产品/服务...................................................................... 3 第3部分 研究与开发.................................................…...

反射、枚举以及lambda表达式
一.反射 1.概念:Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么&am…...

ROS应用之IMU碰撞检测与接触事件识别
前言 碰撞检测与接触事件识别是机器人与环境交互中的重要任务,尤其是在复杂的动态环境中。IMU(惯性测量单元)作为一种高频率、低延迟的传感器,因其能够检测加速度、角速度等动态变化而成为实现碰撞检测的核心手段之一。结合先进的…...

docker安装MySQL8:docker离线安装MySQL、docker在线安装MySQL、MySQL镜像下载、MySQL配置、MySQL命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull mysql:8.0.41 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜…...

android安卓用Rime
之前 [1] 在 iOS 配置用上自改方案 [2],现想在安卓也用上。Rime 主页推荐了两个安卓平台支持 rime 的输入法 [3]: 同文 Tongwen Rime Input Method Editor,但在我的 Realme X2 Pro 上似乎有 bug:弹出的虚拟键盘只有几个 switcher…...

每日一博 - 三高系统架构设计:高性能、高并发、高可用性解析
文章目录 引言一、高性能篇1.1 高性能的核心意义 1.2 影响系统性能的因素1.3 高性能优化方法论1.3.1 读优化:缓存与数据库的结合1.3.2 写优化:异步化处理 1.4 高性能优化实践1.4.1 本地缓存 vs 分布式缓存1.4.2 数据库优化 二、高并发篇2.1 高并发的核心…...

C++ 中的引用(Reference)
在 C 中,引用(Reference)是一种特殊的变量类型,它提供了一个已存在变量的别名。引用在很多场景下都非常有用,比如函数参数传递、返回值等。下面将详细介绍 C 引用的相关知识。 1. 引用的基本概念和语法 引用是已存在…...

负荷预测算法模型
1. 时间序列分析方法 时间序列分析方法是最早被用来进行电力负荷预测的方法之一,它基于历史数据来构建数学模型,以描述时间与负荷值之间的关系。这种方法通常只考虑时间变量,不需要大量的输入数据,因此计算速度快。然而ÿ…...

【C语言】内存管理
【C语言】内存管理 文章目录 【C语言】内存管理1.概念2.库函数3.动态分配内存malloccalloc 4.重新调整内存的大小和释放内存reallocfree 1.概念 C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。 在 C 语言中,内存是通过…...

deepseek大模型本机部署
2024年1月20日晚,中国DeepSeek发布了最新推理模型DeepSeek-R1,引发广泛关注。这款模型不仅在性能上与OpenAI的GPT-4相媲美,更以开源和创新训练方法,为AI发展带来了新的可能性。 本文讲解如何在本地部署deepseek r1模型。deepseek官…...

动态规划DP 最长上升子序列模型 拦截导弹(题目分析+C++完整代码)
概览检索 动态规划DP 最长上升子序列模型 拦截导弹 原题链接 AcWiing 1010. 拦截导弹 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每…...

缩位求和——蓝桥杯
1.题目描述 在电子计算机普及以前,人们经常用一个粗略的方法来验算四则运算是否正确。 比如:248153720248153720 把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是 1 位数,得 24814>145 156 56 而…...

Baklib赋能企业实现高效数字化内容管理提升竞争力
内容概要 在数字经济的浪潮下,企业面临着前所未有的机遇与挑战。随着信息技术的迅猛发展,各行业都在加速推进数字化转型,以保持竞争力。在这个过程中,数字化内容管理成为不可或缺的一环。高效的内容管理不仅能够优化内部流程&…...

【视频+图文讲解】HTML基础2-html骨架与基本语法
图文教程 基本骨架 举个例子,下图所展示的为html的源代码 -!DOCTYPE:表示文档类型(后边写的html表示文档类型是html);其中“!”表示声明 只要是加这个声明标签的,浏览器就会把下边的源代码当…...

消息队列篇--原理篇--常见消息队列总结(RabbitMQ,Kafka,ActiveMQ,RocketMQ,Pulsar)
1、RabbitMQ 特点: AMQP协议:RabbitMQ是基于AMQP(高级消息队列协议)构建的,支持多种消息传递模式,如发布/订阅、路由、RPC等。多语言支持:支持多种编程语言的客户端库,包括Java、P…...

【力扣每日一题】存在重复元素 II 解题思路
219. 存在重复元素 II 解题思路 问题描述 给定一个整数数组 nums 和一个整数 k,要求判断数组中是否存在两个 不同的索引 i 和 j,使得: nums[i] nums[j]且满足 abs(i - j) < k 如果满足上述条件,返回 true,否则…...

React第二十八章(css modules)
css modules 什么是 css modules 因为 React 没有Vue的Scoped,但是React又是SPA(单页面应用),所以需要一种方式来解决css的样式冲突问题,也就是把每个组件的样式做成单独的作用域,实现样式隔离,而css modules就是一种…...

本地运行大模型效果及配置展示
电脑上用ollama安装了qwen2.5:32b,deepseek-r1:32b,deepseek-r1:14b,llama3.1:8b四个模型,都是Q4_K_M量化版。 运行过程中主要是cpu和内存负载比较大,qwen2.5:32b大概需要22g,deepseek-r1:32b类…...

愿景:做机器视觉行业的颠覆者
一个愿景,两场战斗,专注制胜。 一个愿景:做机器视觉行业的颠覆者。 我给自己创业,立一个大的愿景:做机器视觉行业的颠覆者。 两场战斗:无监督-大模型 上半场,无监督。2025-2030,共五…...