深度学习编译器的演进:从计算图到跨硬件部署的自动化之路

第一章 问题的诞生——深度学习部署的硬件困境

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 早期解决方案的局限

人工优化库的工作流程

GPU
CPU
算法工程师
编写Python模型
选择目标硬件
调用cuDNN
调用MKL
部署

典型案例:卷积算子的多平台实现

假设需要新增一种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

传统方式

同一PyTorch模型
为GPU重写cuDNN版本
为CPU优化OpenMP并行
为NPU转换OM模型
三个独立代码库

编译器方式

统一计算图描述
TVM编译器
自动生成GPU代码
自动生成CPU代码
自动生成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)加速比
基线顺序执行1521.0x
分块+向量化682.24x
分块+向量化+并行236.6x

2.1.3 局限性分析

问题1:策略搜索空间爆炸
假设有5个调度原语(分块、向量化、并行化、循环展开等),每个有3种参数选择:

组合总数 = 3 5 = 243 \text{组合总数} = 3^5 = 243 组合总数=35=243

人工遍历所有组合需要至少243次实验。

问题2:硬件适配困难
同一调度在不同硬件的表现:

硬件平台最佳分块尺寸向量化宽度
Intel i7128x1288
ARM A7264x644
NVIDIA P100256x25616

问题3:跨算子优化缺失

// 两个独立算子
Func conv = ...;
Func relu = ...;

// 无法进行融合优化
conv.compute_root();
relu.compute_root();

2.2 第二代:基于中间表示的优化

2.2.1 TVM的多级IR体系

图优化
算子拆分
调度优化
代码生成
Relay IR
优化后的Relay IR
TE表达式
TIR
目标代码
关键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)
原始计算图41.215.3
常量折叠31.114.7
算子融合20.911.2
布局转换20.89.1

2.2.3 TE调度系统

矩阵乘法优化示例:

def te_matmul(A, B):
    m, k = A.shape
    k, n = B.shape
    rc = 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.51.0x
分块+重排序5.32.36x
线程绑定2.15.9x

2.3 混合式调度系统

2.3.1 MLIR基础设施解析

MLIR采用模块化的方言(Dialect)设计,核心架构如下:

在这里插入图片描述

前端语言 → 高层方言(如TensorFlow Graph) 
  ↓ 渐进式Lowering
中间方言(如Affine Dialect) 
  ↓
底层方言(如LLVM IR) 
  ↓
硬件指令
关键机制详解
  1. 可扩展方言体系

    // 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>
    }
    
  2. 渐进式Lowering过程

    高层操作 → 循环嵌套 → 向量指令 → 硬件指令
    
  3. 多级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) : (...) -> f32
          store %2, %0[%i] : memref<100xf32>
        }
        return %0 : tensor<f32>
      }
    }
    

2.3.2 架构对比分析

特性HalideTVMMLIR
优化抽象层次循环级计算图+循环级全层次
硬件扩展性需重写调度策略新增目标后端定义新方言
跨优化阶段集成有限原生支持
自动优化能力基于搜索基于规则+搜索

MLIR创新点

  1. 统一编译基础设施:支持从算法到硬件的全栈表达
  2. 方言可组合性:不同抽象层次的IR可以共存交互
  3. 形式化语义:每个方言都有严格定义的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)
无优化2171245152.3
常量折叠2031128144.7
算子融合98876112.4
布局转换9883297.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.shape
    O, _, Kh, Kw = kernel.shape
    
    # 计算输出尺寸
    out_h = (H + 2*padding - Kh) // stride + 1
    out_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} xz=cos(x)ln(y)yz=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.axis
    xo, 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.5172.31.0x
分块+重排序5.3406.72.36x
线程绑定2.11024.85.95x
向量化+展开1.41536.08.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=1nfi(x),fiF

  • 参数搜索:采用并行贝叶斯优化
    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 调优收敛过程

  1. 随机采样阶段:探索全局空间
  2. 模型引导阶段:快速逼近最优
  3. 局部优化阶段:精细调整参数

3.4.3 实际调优案例

在NVIDIA T4 GPU上调优ResNet-50:

阶段时间消耗最佳时延(ms)加速比
初始实现015.21.0x
随机搜索100次2h9.81.55x
贝叶斯优化300次6h6.72.27x
专家手动优化40h6.52.34x

3.4.4 技术实现总结

TVM通过分层优化体系实现自动化编译:

计算图优化
全局内存优化
算子融合
张量表达式
调度优化
自动调优
高性能代码

关键创新点

  1. 跨层优化:在不同抽象层级实施针对性优化
  2. 可组合性:调度原语可任意组合产生新策略
  3. 可移植性:同一优化策略适配多硬件后端

实验数据显示,TVM在典型工作负载上可获得:

  • 相比手工优化库90%以上的性能
  • 比原生框架3-10倍的加速比
  • 跨硬件平台的一致优化效果

这种技术突破使得深度学习模型可以真正实现"一次开发,处处高效运行"。下一章将通过实际部署案例,展示TVM在不同场景中的实践效果。


第四部分:实践验证——跨硬件平台的性能对比

4.1 GPU平台测试

在NVIDIA T4上的ResNet-50推理:

方法时延(ms)内存使用(MB)功耗(W)
PyTorch原生15.2120372
TVM自动优化6.887465
手工CUDA实现6.583263

4.2 CPU平台测试

Intel Xeon Gold 6248上的BERT推理:

优化方法吞吐量(qps)时延(ms)加速比
ONNX Runtime7812.81.0x
TVM自动调优1427.041.82x
手工AVX512优化1556.451.99x

4.3 边缘设备测试

树莓派4B上的MobileNetV2:

部署方式时延(ms)内存峰值(MB)能耗(J)
TFLite143825.3
TVM+AutoTVM97643.8
手工NEON优化89583.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 自动化程度提升

未来编译器架构展望:

不达标
达标
计算图
自动算子切分
自动调度优化
自动代码生成
性能验证
部署

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/963036.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Agentic Automation:基于Agent的企业认知架构重构与数字化转型跃迁---我的AI经典战例

文章目录 Agent代理Agent组成 我在企业实战AI Agent企业痛点我构建的AI Agent App 项目开源 & 安装包下载 大家好&#xff0c;我是工程师令狐&#xff0c;今天想给大家讲解一下AI智能体&#xff0c;以及企业与AI智能体的结合&#xff0c;文章中我会列举自己在企业中Agent实…

论文阅读:Realistic Noise Synthesis with Diffusion Models

这篇文章是 2025 AAAI 的一篇工作&#xff0c;主要介绍的是用扩散模型实现对真实噪声的仿真模拟 Abstract 深度去噪模型需要大量来自现实世界的训练数据&#xff0c;而获取这些数据颇具挑战性。当前的噪声合成技术难以准确模拟复杂的噪声分布。我们提出一种新颖的逼真噪声合成…

Baklib揭示内容中台与人工智能技术的创新协同效应

内容概要 在当今信息爆炸的时代&#xff0c;内容的高效生产与分发已成为各行业竞争的关键。内容中台与人工智能技术的结合&#xff0c;为企业提供了一种新颖的解决方案&#xff0c;使得内容创造的流程更加智能化和高效化。 内容中台作为信息流动的核心&#xff0c;能够集中管…

Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)

目录 前言1. 基本知识2. Demo3. 实战代码 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 基本的Java知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&am…

DeepSeek 第二弹:Janus-Pro 文生图模型

最近&#xff0c;DeepSeek 可谓是科技圈的焦点&#xff0c;还火出了圈外&#xff0c;掀起了一场全民创作热潮。大家纷纷借助 DeepSeek R1 挥洒才情&#xff0c;实现诗人、小说家的梦想。然而&#xff0c;就在这场文字狂欢之际&#xff0c;DeepSeek 又悄然推出了一款重磅产品——…

leetcode 2563. 统计公平数对的数目

题目如下 数据范围 显然数组长度最大可以到10的5次方n方的复杂度必然超时&#xff0c;阅读题目实际上就是寻找两个位置不同的数满足不等式即可(实际上i j无所谓是哪个 我们只要把位置小的想成i就行)。 按照上面的思路我们只需要排序数组然后从前往后遍历数组然后利用二分查找…

2024第十五届蓝桥杯网安赛道省赛题目--cc(CyberChef)/crypto

打开链接后是&#xff1a; 通过题目界面可以知道是AES加密&#xff0c;并且告诉我们key是gamelabgamelab&#xff0c;IV是gamelabgamelab&#xff0c;Mode是CBC模式&#xff0c;输入是flag&#xff0c;输出为Hex十六进制4da72144967f1c25e6273950bf29342aae635e2396ae17c80b1b…

【视频+图文详解】HTML基础4-html标签的基本使用

图文教程 html标签的基本使用 无序列表 作用&#xff1a;定义一个没有顺序的列表结构 由两个标签组成&#xff1a;<ul>以及<li>&#xff08;两个标签都属于容器级标签&#xff0c;其中ul只能嵌套li标签&#xff0c;但li标签能嵌套任何标签&#xff0c;甚至ul标…

Python-基于PyQt5,wordcloud,pillow,numpy,os,sys等的智能词云生成器

前言&#xff1a;日常生活中&#xff0c;我们有时后就会遇见这样的情形&#xff1a;我们需要将给定的数据进行可视化处理&#xff0c;同时保证呈现比较良好的量化效果。这时候我们可能就会用到词云图。词云图&#xff08;Word cloud&#xff09;又称文字云&#xff0c;是一种文…

自制虚拟机(C/C++)(二、分析引导扇区,虚拟机读二进制文件img软盘)

先修复上一次的bug&#xff0c;添加新指令&#xff0c;并增加图形界面 #include <graphics.h> #include <conio.h> #include <windows.h> #include <commdlg.h> #include <iostream> #include <fstream> #include <sstream> #inclu…

工作流引擎Camunda

一&#xff0c;什么是Camunda&#xff1f; Camunda是一个开源的工作流引擎和业务流程管理平台&#xff0c;基于Java和Spring框架构建。它支持BPMN 2.0标准&#xff0c;允许用户通过图形界面或编程方式定义复杂的工作流和业务流程。Camunda可以嵌入到任何Java应用程序中&#x…

C++,STL,【目录篇】

文章目录 一、简介二、内容提纲第一部分&#xff1a;STL 概述第二部分&#xff1a;STL 容器第三部分&#xff1a;STL 迭代器第四部分&#xff1a;STL 算法第五部分&#xff1a;STL 函数对象第六部分&#xff1a;STL 高级主题第七部分&#xff1a;STL 实战应用 三、写作风格四、…

【已解决】黑马点评项目Redis版本替换过程的数据迁移

黑马点评项目Redis版本替换过程的数据迁移 【哭哭哭】附近商户中需要用到的GEO功能只在Redis 6.2以上版本生效 如果用的是老版本&#xff0c;美食/KTV的主页能正常返回&#xff0c;但无法显示内容 上次好不容易升到了5.0以上版本&#xff0c;现在又用不了了 Redis 6.2的windo…

文献阅读 250201-The carbon budget of China: 1980–2021

The carbon budget of China: 1980–2021 来自 <https://www.sciencedirect.com/science/article/pii/S2095927323007703> 中国碳预算&#xff1a;1980–2021 年 ## Abstract: Using state-of-the-art datasets and models, this study comprehensively estimated the an…

《OpenCV》——图像透视转换

图像透视转换简介 在 OpenCV 里&#xff0c;图像透视转换属于重要的几何变换&#xff0c;也被叫做投影变换。下面从原理、实现步骤、相关函数和应用场景几个方面为你详细介绍。 原理 实现步骤 选取对应点&#xff1a;要在源图像和目标图像上分别找出至少四个对应的点。这些对…

条件变量 实现2生产者2消费者模型

1个生产者在生产的时候&#xff0c;另个生产者不能生产(生产者之间互斥) 条件变量用于线程同步&#xff0c;线程挂起/被唤醒。 条件变量和互斥锁共同保证生产者之间互斥生产者和消费者的同步。 思路&#xff1a; 1 定义、初始化共享资源 a 缓冲区&#xff1a;存储物品…

一个开源 GenBI AI 本地代理(确保本地数据安全),使数据驱动型团队能够与其数据进行互动,生成文本到 SQL、图表、电子表格、报告和 BI

一、GenBI AI 代理介绍&#xff08;文末提供下载&#xff09; github地址&#xff1a;https://github.com/Canner/WrenAI 本文信息图片均来源于github作者主页 在 Wren AI&#xff0c;我们的使命是通过生成式商业智能 &#xff08;GenBI&#xff09; 使组织能够无缝访问数据&…

使用C#开发一款通用数据库管理工具

由于经常使用各种数据库&#xff0c;笔者自己动手丰衣足食&#xff0c;使用C#开发了一款通用数据库管理工具&#xff0c;支持Mysql、Oracle、Sqlite、SQL Server等数据库的表、视图、存储过程、函数管理功能&#xff0c;并支持导入导出、数据字典生成、拖拽式跨机器跨库数据一键…

sqli-labs靶场通关

sqli-las通关 mysql数据库5.0以上版本有一个自带的数据库叫做information_schema,该数据库下面有两个表一个是tables和columns。tables这个表的table_name字段下面是所有数据库存在的表名。table_schema字段下是所有表名对应的数据库名。columns这个表的colum_name字段下是所有…

DNS缓存详解(DNS Cache Detailed Explanation)

DNS缓存详解 清空DNS缓存可以让网页访问更快捷。本文将从什么是DNS缓存、为什么清空DNS缓存、如何清空DNS缓存、清空DNS缓存存在的问题四个方面详细阐述DNS缓存清空的相关知识。 一、什么是DNS缓存 1、DNS缓存的定义&#xff1a; DNS缓存是域名系统服务在遇到DNS查询时自动…