CUDA C++核心库(CCCL)

文章目录

    • CUDA C++核心库(CCCL)
    • 核心库介绍
      • CUDA C++ 开发工具的层级范围
      • 各层级工具的具体内容
    • Thrust
      • 自动内存管理
      • 类型安全
      • 自定义分配器(页锁定内存)
      • 高级API替代底层操作
      • thrust::transform
        • 基本使用
        • 几种执行策略
      • iterator
      • load_cs
      • 高效索引mdspan
    • Libcu++
      • cuda::std::tuple
      • cuda::std::variant
      • cuda::std::pair

CUDA C++核心库(CCCL)

一、标准 C++ 组成

  1. C++ 语言核心
  2. 标准库(Standard Library)
    • 核心功能:
      ▶ 通用抽象(General Purpose Abstractions)
      ▶ 数据结构(Data Structures)
      ▶ 算法库(Algorithms)
    • 价值:简化开发流程,提升代码健壮性,避免底层重复实现

二、CUDA C++ 组成

  1. 基础组件:
    ▶ C++ 语言核心
    ▶ 主机标准库(Host Standard Library)
  2. 扩展组件:
    ▶ CUDA 语言扩展(CUDA Language Extensions)
    ▶ CUDA C++ 核心库(CUDA C++ Core Libraries, CCCL)
  3. CCCL 核心功能:
    • 异构 C++ 标准库(Heterogeneous C++ Standard Library)
      ✅ 支持 CPU/GPU 协同编程
      ✅ 提供统一内存模型抽象
    • CUDA 基础抽象(Fundamental CUDA Abstractions)
      ✅ 封装线程管理(Thread Hierarchy)
      ✅ 设备内存操作(Memory Management)
    • 高性能并行算法库(High-Performance Parallel Algorithms)
      ✅ 矩阵运算加速
      ✅ 数据并行处理原语

核心库介绍

CUDA C++ 开发工具的层级范围

  • 横轴意义:从左到右,工具呈现从 “高层且易用(High-Level & Productive)” 到 “底层且更具控制力(Low-Level & More Control)” 的变化。
  • 左侧起点(绿色箭头 “Start Here”):建议从高层工具开始,如Thrust,因其易用性高、能提升开发效率。
  • 右侧终点(红色箭头 “Don’t Start Here”):不建议直接从底层工具(如 PTX Wrappers)入手,这类工具复杂且维护难度大。

在这里插入图片描述

各层级工具的具体内容

  1. 高层工具(High-Level & Productive)
    • libcu++:提供 C++ 标准库扩展,例如cuda::std::variantcuda::std::optional,方便使用容器及抽象化功能。
    • Thrust:提供 CPU/GPU 并行算法,适用于快速开发高层算法和数据处理。
  2. 中间层工具(中等抽象层次)
    • 迭代器(Fancy Iterators):如cuda::std::spancuda::std::mdspan,用于处理复杂数据结构。
    • 设备范围算法(Device-wide Algorithms):用于对设备内数据进行全局操作。
    • 块范围算法(Block-Scope Algorithms):如cuda::memcpy_async,适合更精细的块级控制。
    • Warp 范围算法(Warp-Scope Algorithms):通过cuda::atomic实现线程束间的同步与控制。
  3. 底层工具(Low-Level & More Control)
    • PTX Wrappers:对 PTX 汇编代码进行封装,适用于需要极端性能优化的场景。
    • CUB:提供低级 GPU 并行算法的实现,灵活性更高,但使用复杂度也随之增加。

Thrust

自动内存管理

解释:Thrust容器使用RAII(资源获取即初始化)模式,在对象生命周期结束时自动释放内存,避免内存泄漏。传统CUDA编程需手动调用cudaFree,容易遗漏。

传统方法

int* d_data;
cudaMalloc(&d_data, N * sizeof(int));
// 使用d_data
cudaFree(d_data); // 必须手动释放,否则内存泄漏

Thrust方法

thrust::device_vector<int> d_data(N); // 自动分配内存
// 使用d_data
// 离开作用域时自动释放内存

类型安全

解释:Thrust容器在编译期进行类型检查,防止不匹配的数据操作。传统cudaMemcpy仅检查指针类型,不验证实际数据类型。

不安全示例(传统方法):

cuda::std::complex<float>* d_complex;
cudaMalloc(&d_complex, N * sizeof(cuda::std::complex<float>));
int* h_int = new int[N];
// 错误:类型不匹配,但cudaMemcpy不报错
cudaMemcpy(d_complex, h_int, N * sizeof(int), cudaMemcpyHostToDevice);

安全示例(Thrust方法):

thrust::device_vector<cuda::std::complex<float>> d_complex(N);
thrust::host_vector<int> h_int(N);
// 编译错误:无法将int赋值给complex<float>
// d_complex = h_int; // 此操作会触发编译错误

自定义分配器(页锁定内存)

解释:Thrust允许通过自定义分配器使用页锁定内存(pinned memory),提升主机与设备间的数据传输效率。

传统方法

float* h_data;
cudaHostAlloc(&h_data, N * sizeof(float), cudaHostAllocDefault); // 分配页锁定内存
// 使用h_data
cudaFreeHost(h_data); // 手动释放

Thrust方法

using pinned_allocator = thrust::cuda_cub::tagged_allocator<float, // 分配的元素类型thrust::cuda_cub::thread_safe_allocator_tag, // 线程安全标记thrust::cuda_cub::pinned_host_memory_resource_tag // 固定内存标记
>;// 使用页锁定内存的host_vector
thrust::host_vector<float, pinned_allocator> h_data(N);
// 使用h_data
// 自动释放

高级API替代底层操作

解释:Thrust提供高层算法(如fill_n)替代底层函数(如cudaMemset),提升代码可读性和安全性。

传统方法

float* d_data;
cudaMalloc(&d_data, N * sizeof(float));
cudaMemset(d_data, 0, N * sizeof(float)); // 字节级操作,需手动计算字节数
// 使用d_data
cudaFree(d_data);

Thrust方法

thrust::device_vector<float> d_data(N);
thrust::fill(d_data.begin(), d_data.end(), 0.0f); // 类型安全,自动处理内存

thrust::transform

基本使用

传统方法

__global__ void gelu_kernel(float* out, const float* inp, int N) {int i = blockIdx.x * blockDim.x + threadIdx.x;if (i < N) {float x = inp[i];float cube = x * x * x;float cdf = 0.5f * (1.0f + tanh(0.79788456f * (x + 0.044715f * cube)));out[i] = x * cdf;}
}void gelu_forward(float* out, const float* inp, int N) {int block_size = 128;int grid_size = CEIL_DIV(N, block_size);gelu_kernel<<<grid_size, block_size>>>(out, inp, N);
}  

thrust::transform方法:

  • 使用thrust::transform方法,只需要关心每个元素的计算逻辑,不用手动管线程分工,代码更加简洁,突出”算法意图“。
  • 可以直接使用多种多种执行策略,控制计算在设备上的执行,如thrust::device、thrust::cuda::par_nosync和thrust::cuda::par_on(stream)等。
struct gelu_functor {__host__ __device__float operator()(const float& x) const {  // 运算符重载operator(),将结构体转变为函数对象float cube = x * x * x;return x * 0.5f * (1.0f + tanh(0.79788456f * (x + 0.044715f * cube)));}
};void gelu_forward(float* out, const float* inp, int N) {thrust::device_ptr<const float> d_inp(inp);thrust::device_ptr<float> d_out(out);// d_inp输入数据的起始位置;d_inp+N输入数据的结束位置;d_out输出数据的起始位置thrust::transform(thrust::device, d_inp, d_inp + N, d_out, gelu_functor());
} 
几种执行策略

thrust::device

  • 它指定 Thrust 算法应该在 GPU 设备上执行。它是一种相对通用的设备端执行策略,使用默认的 CUDA 流
  • 在默认情况下,使用thrust::device执行完 Thrust 算法后,主机端代码如果继续访问与该操作相关的设备内存,会隐式地等待操作完成

thrust::cuda::par_nosync

  • 指示 Thrust 算法在 GPU 设备上运行,但它强调不进行隐式同步。这意味着 Thrust 操作提交到 GPU 后,主机端代码会立即继续执行,而不会等待 GPU 操作完成。
  • 利用这种策略可以实现主机端和设备端的重叠计算,提高整体的计算效率。例如,在一些复杂的计算流程中,主机端可能需要在设备端计算的同时进行一些其他的预处理或后处理操作,此时thrust::cuda::par_nosync就非常有用。
  • 由于没有隐式同步,如果主机端后续需要访问设备端操作的结果,必须手动调用 CUDA 的同步函数(如cudaDeviceSynchronize() )来确保 GPU 操作已经完成,否则会导致错误

thrust::cuda::par_on(stream)

  • 通过使用特定的 CUDA 流,可以更好地管理 GPU 资源,实现更细粒度的并行。比如,在一个应用中有多个不同优先级或者不同类型的计算任务,可以分别放在不同的 CUDA 流中,利用thrust::cuda::par_on(stream)将相应的 Thrust 操作分配到合适的流上。
  • 并发与同步管理:可以结合不同流之间的同步原语(如cudaStreamSynchronize() )来控制不同流中操作的执行顺序和依赖关系。同时,不同流中的操作在硬件支持下能够并发执行,进一步提高 GPU 利用率。

iterator

传统方法:

__global__ void unpermute_kernel(float* inp, float *out, int B, int N, int NH, int d) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < B * NH * N * d) {int b = idx / (NH * N * d);int rest = idx % (NH * N * d);int nh_ = rest / (N * d);rest = rest % (N * d);int n = rest / d;int d_ = rest % d;int other_idx = (b * NH * N * d) + (n * NH * d) + (nh_ * d) + d_;out[other_idx] = __ldcs(&inp[idx]);}
}// 核函数调用示例
num_blocks = CEIL_DIV(B * T * C, block_size);
unpermute_kernel<<<num_blocks, block_size>>>(vaccum, out, B, T, NH, HS);  

优化方法:

// Thrust库迭代器方式实现
auto map = thrust::make_transform_iterator(thrust::make_counting_iterator(0),  // 创建从 0 开始的计数迭代器,提供连续的索引值 idx[=] __host__ __device__ (int idx) {   // 通过 lambda 表达式作为变换操作,[=] 表示按值捕获外部变量auto [b, n, nh_, d_] = i2n(idx, NH, T, HS);return (b * NH * T * HS) + (n * NH * HS) + (nh_ * HS) + d_;}
);cub::CacheModifiedInputIterator<cub::LOAD_CS, float> vaccumcs(vaccum);
// 该操作会根据 map 提供的索引,把 vaccumcs 指向的数据按序分散到 out 数组对应位置
thrust::scatter(thrust::device, vaccumcs, vaccumcs + B * T * C, map, out); 

load_cs

thrust的高级抽象不会牺牲底层控制能力。可以通过cub::CacheModifiedInputIterator<cub::LOAD_CS, float>来等价cuda kernel中直接调用底层的__ldcs指令。

传统方法

// CUDA 核函数,实现两个数组对应元素相加,结果存入输出数组
__global__ void residual_forward_kernel(float* out, float* inp1, float* inp2, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) {out[idx] = __ldcs(&inp1[idx]) + __ldcs(&inp2[idx]);}
}// 主机端函数,用于调用上述 CUDA 核函数
void residual_forward(float* out, float* inp1, float* inp2, int N) {const int block_size = 256;const int grid_size = CEIL_DIV(N, block_size); residual_forward_kernel<<<grid_size, block_size>>>(out, inp1, inp2, N);cudaCheck(cudaGetLastError()); 
}

thrust

void residual_forward(float* out, float* inp1, float* inp2, int N) {cub::CacheModifiedInputIterator<cub::LOAD_CS, float> inp1cs(inp1);cub::CacheModifiedInputIterator<cub::LOAD_CS, float> inp2cs(inp2);thrust::transform(thrust::device,inp1cs, inp1cs + N, inp2cs, out, thrust::plus<float>());
}

高效索引mdspan

使用cuda::std::mdspan管理多维数据,从而将传统的按照一维数组访问的方式,转变为以多维方式进行访问。

传统方法

// permute_kernel函数实现矩阵重排列操作
__global__ void permute_kernel(float* q, float* k, float* v,const float* inp, int B, int N, int NH, int d) {// 计算当前线程的全局索引int idx = blockIdx.x * blockDim.x + threadIdx.x;// 原始代码中的矩阵重排列计算// dlb[nh][n][d_] = inp[b][n][nh][d_]// 计算输入张量的各个维度索引int b = idx / (NH * N * d);      // batch维度int rest = idx % (NH * N * d);   // 剩余部分int nh = rest / (N * d);         // head维度rest = rest % (N * d);           // 继续分解剩余部分int n = rest / d;                // 序列长度维度int d_ = rest % d;               // 特征维度// 计算输入张量的线性索引int inp_idx = (b * N * NH * d) +           // batch偏移(n * NH * d) +               // 序列长度偏移(nh * d) +                   // head偏移d_;                          // 特征维度偏移// 执行张量重排列操作q[idx] = __ldcs(&inp[inp_idx]); // 使用__ldcs进行缓存优化的内存读取k[idx] = __ldcs(&inp[inp_idx + NH * d]);v[idx] = __ldcs(&inp[inp_idx + 2 * NH * d]);
}// attention_forward函数实现注意力前向传播
void attention_forward(float* out, float* veccum, float* qkv, float* presft, float* att,int B, int T, int C, int NH) {const int block_size = 256;              // CUDA线程块大小const int softmax_block_size = 256;      // Softmax操作的线程块大小int HS = C / NH;                        // 每个head的维度大小// 计算每个head的维度大小float *q, *k, *v;q = qkv;                                 // 查询矩阵Q的起始位置k = qkv + B * T * C;                     // 键矩阵K的起始位置v = qkv + 2 * B * T * C;                 // 值矩阵V的起始位置// 计算需要的CUDA线程块数量int total_threads = B * NH * T * HS;int num_blocks = CEIL_DIV(total_threads, block_size);// 启动permute_kernel进行张量重排列permute_kernel<<<num_blocks, block_size>>>(q, k, v, qkv, B, T, NH, HS);
}

优化方法:

void attention_forward(float* out, float* vaccum, float* qkvr, float* prestt, float* att,float* inp, int B, int T, int C, int NH) {// 设置CUDA块大小常量const int block_size = 256;const int softmax_block_size = 256;// 计算每个注意力头的维度大小int HS = C / NH;  // head size// 设置Q、K、V矩阵的指针,它们在内存中是连续存储的float *q, *k, *v;q = qkvr + 0 * B * T * C;      // Q矩阵起始位置k = qkvr + 1 * B * T * C;      // K矩阵起始位置v = qkvr + 2 * B * T * C;      // V矩阵起始位置// 使用CUDA动态内存分配constexpr auto dyn = cuda::std::dynamic_extent;  // 这是一个编译时常量,表示数组维度的大小在运行时确定using ext_t = cuda::std::extent<int, dyn, dyn, 3, dyn, dyn>;  // 定义了一个 5 维数组的维度结构using mds_t = cuda::std::mdspan<const float, ext_t>;  // 不拥有内存,只是提供多维索引到一维内存的映射;提高代码可读性和维护性,避免手动计算偏移量// 创建多维数组视图,用于更方便地访问数据ext_t extents(B, T, NH, HS);   // 只传入动态维度,extents仍然是5维的mds_t inp_md(inp, extents);  // 将一维内存指针 inp 映射为多维视图/*** 示例访问方式:* 访问批次b、时间步t、第0个矩阵(对应Q)、头nh_、维度hs的数据* float value = inp_md(b, t, 0, nh_, hs);*/// 使用thrust库创建迭代器,用于并行处理auto begin = thrust::make_counting_iterator(0);auto end = begin + B * NH * T * T;// 原始重排列操作的注释:Q[b][nh][t][d_] = inp[b][t][nh][d_]// 使用thrust并行处理每个元素// [=]:捕获方式为值捕获(By Value),表示 Lambda 内部可以使用外部作用域的所有变量(如B, T, C, NH, q, k, v, inp_md等)thrust::for_each(thrust::cuda::par,begin, end,[=] __device__ (int idx) {// 计算当前处理位置的各个维度索引auto [b, t, nh_, hs] = idx2(idx, NH, T, HS);// 执行Q、K、V矩阵的数据重排列q[idx] = inp_md(b, t, 0, nh_, hs);  // Q矩阵赋值k[idx] = inp_md(b, t, 1, nh_, hs);  // K矩阵赋值v[idx] = inp_md(b, t, 2, nh_, hs);  // V矩阵赋值});
}

Libcu++

cuda::std::tuple

传统方法

__global__ void permute_kernel(float* q, float* k, float* v,const float* inp, int B, int N, int NH, int d) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < B * NH * N * d) {int b = idx / (NH * N * d);int rest = idx % (NH * N * d);int nh_ = rest / (N * d);rest = rest % (N * d);int n = rest / d;int d_ = rest % d;//...}
}__global__ void unpermute_kernel(float* inp, float *out, int B,int N, int NH, int d) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < B * NH * N * d) {int b = idx / (NH * N * d);int rest = idx % (NH * N * d);int nh_ = rest / (N * d);rest = rest % (N * d);int n = rest / d;int d_ = rest % d;//...}
}

优化方法

  • 使用cuda::std::tuple减少了代码冗余
__host__ __device__ 
cuda::std::tuple<int, int, int, int> 
idx2n(int idx, int E1, int E2, int E3) {int b = idx / (E1 * E2 * E3);int rest = idx % (E1 * E2 * E3);int nh_ = rest / (E2 * E3);rest = rest % (E2 * E3);int t = rest / E3;int hs = rest % E3;return {b, t, nh_, hs};
}__global__ void permute_kernel(float* q, float* k, float* v,const float* inp, int B, int N, int NH, int d) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < B * NH * N * d) {auto [b, n, nh_, d_] = idx2n(idx, NH, N, d);//...}
}__global__ void unpermute_kernel(float* inp, float *out, int B,int N, int NH, int d) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < B * NH * N * d) {auto [b, n, nh_, d_] = idx2n(idx, NH, N, d);//...}
}

cuda::std::variant

cuda::std::variant 是一种类型安全的联合(tagged union )。它可以在同一时间存储一个值,这个值的类型可以是在声明 variant 时指定的几种类型中的任意一种。与传统的 union 不同,variant 知道当前存储的值具体是哪种类型,并且提供了安全的访问方式。

__global__ void variant_kernel() {// 声明一个可以存储 int 或 float 类型的 variantcuda::std::variant<int, float> var; var = 42;  // 存储 int 类型的值if (auto *i = cuda::std::get_if<int>(&var)) {// 安全地获取 int 类型的值std::cout << "The value is an int: " << *i << std::endl; }var = 3.14f;  // 存储 float 类型的值if (auto *f = cuda::std::get_if<float>(&var)) {std::cout << "The value is a float: " << *f << std::endl; }
}

cuda::std::pair

cuda::std::pair 是一个简单的模板类,用于将两个不同类型的对象组合成一个单一的对象。它有两个公共成员变量 firstsecond,可以方便地访问存储的两个值。

__global__ void pair_kernel() {// 创建一个 pair,存储 int 和 float 类型的值cuda::std::pair<int, float> my_pair(10, 2.5f); std::cout << "First value: " << my_pair.first << std::endl;std::cout << "Second value: " << my_pair.second << std::endl;
}

主要参考:

  • how-to-optim-algorithm-in-cuda

点击查看我的更多AI学习笔记github

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL InnoDB存储引擎深度解析:从原理到优化

InnoDB的优势InnoDB之所以成为众多应用的首选&#xff0c;主要得益于以下几个显著优势&#xff1a;事务支持&#xff1a;InnoDB是MySQL中唯一支持ACID&#xff08;原子性、一致性、隔离性、持久性&#xff09;事务的存储引擎。它通过日志和锁机制确保事务的完整性&#xff0c;这…

LLM评测框架Ragas:Natural Language Comparison指标(解决了Ollama推理框架不支持的问题)

Factural Correctness Factural Correctness是事实正确性是评价LLM生成的反馈和reference的事实正确性。该指标用于确定生成的响应与参考文献的一致程度。Factural Correctness取值在0到1之间,越接近于1结果越好。 为了衡量回应和参考文献之间的一致性,该指标使用 LLM 首先将…

HTTP 协议常见字段(请求头/响应头)

HTTP&#xff08;HyperText Transfer Protocol&#xff09;协议通过 请求头&#xff08;Request Headers&#xff09; 和 响应头&#xff08;Response Headers&#xff09; 传递元数据。以下是 最常见的 HTTP 字段 及其作用&#xff1a;1. 通用字段&#xff08;请求和响应均可使…

期货配资软件开发注意事项?

期货配资软件开发 期货配资软件开发涉及多个核心模块&#xff0c;包括资金管理、风险控制、交易接口、用户权限管理等。此类系统需符合金融监管要求&#xff0c;确保资金安全与数据合规。开发过程中需优先考虑高并发、低延迟及系统稳定性。期货资管系统平台搭建方案架构设计 采…

STM32-第十节-DMA直接存储器存取

一、DMA&#xff1a;1.简介&#xff1a;DMA&#xff0c;直接存储区存取DMA可以提供外设和存储器或存储器与存储器见的高速数据传输&#xff0c;无需CPU干预。12个通道&#xff1a;DMA1&#xff08;7个通道&#xff09;&#xff0c;DMA2&#xff08;5个通道&#xff09;每个通道…

服务器设置国外IP无法访问对防御攻击有用吗?

将服务器设置为仅允许国外 IP 访问&#xff0c;限制国内 IP 访问&#xff0c;确实可以在某些特定场景下提高服务器的抗攻击能力&#xff0c;但这并不能完全防御攻击。以下是对这种方法的分析、优缺点以及其他防御攻击的补充措施。1. 仅允许国外 IP 访问是否有用&#xff1f;1.1…

八大作业票(一) 动火安全作业证

动火安全作业证 执行标准:GB30871 GSDH——2200001 申报单位 申请人 作业申请时间 年 月 日 时 分 动火内容 动火方式 动火地点 动火类别 特级动火□ 一级动火□ 二级动火□ 作业负责人 监护人 动火…

NumPy库使用教学,简单详细。

NumPy 使用教学NumPy 是 Python 中用于科学计算的基础库&#xff0c;它提供了高性能的多维数组对象以及用于处理这些数组的工具。下面将结合多个代码文件&#xff0c;详细介绍 NumPy 的各种用法。1. 创建数组1.1 从列表创建数组import numpy as np# 一维数组 list1 [1,2,3,4,5…

vue3:十八、内容管理-实现行内图片的预览、审核功能

一、实现效果 实现图片的显示,大图预览;审核部分的待审核的审核功能 二、图片预览实现 1、参考官网 官网-图片预览 2、图片预览插槽设置 {row,index} 插槽中获取row行信息、index索引信息(指定行图片预览需要用到) style 设置基本样式宽width高height src 设置图片的路径…

Go后端配置文件教程

注&#xff1a;本文为博主&#xff0c;首次接触项目时的入门级配置实操在 Go 后端中&#xff0c;使用配置文件管理参数&#xff08;如数据库连接、服务端口等&#xff09;是必备技能。Viper 是 Go 生态中最流行的配置管理库。支持多种配置文件、环境变量、命令行参数等&#xf…

ubuntu24.04安装CUDA、VLLM、Pytorch等并部署Qwen3-8B-AWQ【50系显卡通用】

1. 系统更新与依赖安装 sudo apt update && sudo apt upgrade -y sudo apt install -y python3-pip python3-venv build-essential git nvidia-driver-575注:RTX 5070 Ti 推荐驱动 ≥550 版本 我是直接官网安装最新的驱动了,反正向上兼容,驱动安装教程可以参考我以…

Azure可靠性架构指南:构建云时代的高可用系统

随着企业加速拥抱数字化转型&#xff0c;云服务的可靠性已成为业务连续性的核心命题。Microsoft Azure凭借其"可靠性即核心"的设计理念&#xff0c;为企业技术决策者与架构师提供了一个可信赖的数字化底座。本文将系统解析Azure如何通过技术架构、工具链与方法论&…

linux 驱动 - v4l2 驱动框架

文章目录一、/dev/videoX1. 查询设备能力2. 获取当前视频格式3. 设置视频格式4. 申请缓冲区1) mmap 方式2) user 分配5. 查询缓冲区信息6. 将缓冲区放入队1) fill_vb2_buffer2) buf_prepare3) get_userptr4) buf_init5) attach_dmabuf 和 map_dmabuf6) start_streaming7) 总结7…

windows内核研究(驱动开发-0环与3环的通信)

驱动开发0环与3环的通信 设备对象 在之前开发窗口程序时&#xff0c;消息都是被封装成一个结构体&#xff08;MSG&#xff09;&#xff0c;在内核开发时&#xff0c;消息被封装成另外一个结构体&#xff1a;IRP&#xff08;I/O Request Package&#xff09; 在窗口程序中&#…

ASP.NET Core Web API 内存缓存(IMemoryCache)入门指南

在 Web API 开发中&#xff0c;频繁访问数据库或第三方服务可能会带来性能瓶颈。为了提升接口响应速度并减轻后端压力&#xff0c;使用缓存是非常有效的优化手段。本文将带你快速上手 ASP.NET Core 提供的内存缓存&#xff08;IMemoryCache&#xff09;&#xff0c;无需安装额外…

Axios Token 设置示例

以下是一个完整的 Axios Token 设置示例&#xff0c;涵盖全局配置、请求拦截器和单次请求设置三种方式&#xff1a;1. 基础配置&#xff08;推荐方案&#xff09;javascript复制代码import axios from axios;// 创建 Axios 实例 const apiClient axios.create({baseURL: https…

Excel数据合并工具:零门槛快速整理

软件介绍 在数据处理工作中&#xff0c;合并Excel同类数据是一项常见但繁琐的任务。今天为大家推荐一款专为简化此类操作设计的工具&#xff0c;它能快速完成工作表内多行同类数据的合并整理&#xff0c;大幅提升数据处理效率。 零门槛操作体验 相比Excel自带的数据透视…

深度学习 -- 梯度计算及上下文控制

深度学习 – 梯度计算及上下文控制 文章目录深度学习 -- 梯度计算及上下文控制一&#xff0c;自动微分1.1 基础概念1.2 计算梯度1.2.1 计算标量梯度1.2.2 计算向量梯度1.2.3 多标量梯度计算1.2.4 多向量梯度计算二&#xff0c;梯度上下文控制2.1 控制梯度计算2.2 累计梯度2.3 梯…

Redisson RLocalCachedMap 核心参数详解

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

【Unity3D实例-功能-移动】角色移动-通过WSAD(Rigidbody方式)

你是否曾梦想在虚拟世界中自由翱翔&#xff0c;像海豚一样在海洋自由穿梭&#xff0c;或者像宇航员一样在宇宙中尽情探索&#xff1f;今天&#xff0c;我们就来聊聊如何在Unity中使用Rigidbody来实现角色移动。 废话不多说&#xff0c;走&#xff0c;让我们马上来一探究竟&…