当前位置: 首页 > news >正文

【CUDA】Triton

【CUDA】Triton

1. CUDA 与 Triton 的基本区别

CUDA 编程模型:

在传统的 CUDA 编程中,CUDA 是标量程序,带有阻塞线程(blocked threads)

  • 标量程序(Scalar Program):表示我们直接为每个线程编写操作代码,每个线程处理一个数据元素。
  • 阻塞线程(Blocked Threads):为了高效地处理大数据集,线程被组织成线程块(thread blocks)。线程块之间的计算是隔离的,线程块内的线程通过共享内存(shared memory)进行协作。

在 CUDA 中,程序员直接处理线程和线程块。你需要自己编写线程级别的细节,如如何加载数据、如何存储结果、如何管理共享内存等。CUDA 程序员需要掌握这些低级细节,以便充分利用 GPU 的硬件特性。

Triton 编程模型:

Triton 是基于 CUDA 的高层抽象。Triton 是带有标量线程(scalar threads)的块化程序(blocked program)

  • 块化程序(Blocked Program):表示你不再直接操作单个线程,而是关注线程块层次的优化。你编写的程序将自动被编译器转换为低级代码。
  • 标量线程(Scalar Threads):在 Triton 中,程序员不再需要管理线程级别的细节(如内存访问模式),编译器会自动为你处理这些复杂的操作,如数据加载、存储、共享内存使用等。

2. Triton vs CUDA:高层抽象 vs 低层控制

在 CUDA 中,程序员需要掌握线程级操作,包括如何组织线程、如何管理共享内存和同步等。程序员通常需要关注线程块之间如何交互,以及如何利用硬件特性(如共享内存、寄存器)来提高效率。简而言之,CUDA 提供的是较为底层的控制,允许程序员手动优化每个操作的细节。

而在 Triton 中,程序员更多关注高层操作,例如卷积、矩阵乘法等深度学习中的标准操作。Triton 会通过编译器自动处理低级细节,如数据加载、存储、内存调度、线程间同步等。Triton 让深度学习的 GPU 编程更像是高级语言编程,减少了繁琐的底层优化工作。

直观理解:
  • CUDA:程序员编写的是标量程序,并通过线程块来管理计算工作。你需要手动优化每个线程的行为以及线程块之间的协调。这意味着你要深入理解 GPU 的硬件架构。
  • Triton:程序员编写的是块化程序(关注大块操作),并使用标量线程。Triton 的编译器将自动处理线程级别的操作,使得程序员不再需要处理每个线程的细节,能更专注于高级操作。

3. Triton 的优势:简化深度学习编程

Triton 的一个重要特点是为深度学习程序员提供了一个更高层次的抽象,使得复杂的 GPU 编程变得更简洁。程序员不再需要掌握底层的线程管理或内存优化,可以像写 Python 代码一样编写高效的 GPU 核心操作(如卷积、矩阵乘法等)。这对于深度学习研究者来说是一个很大的优势,因为他们的工作更关注模型设计和算法优化,而不是 GPU 编程的低级细节。

举个例子,传统上使用 CUDA 实现 cuBLAS 或 cuDNN 等高效的深度学习库需要深入理解 GPU 架构和高效的内存管理策略,而 Triton 让 Python 程序员通过一个更直观的 API 编写出与这些库同样高效的代码。

4. 为什么不能直接跳过 CUDA 使用 Triton?

Triton 是建立在 CUDA 之上的,因此,理解 CUDA 的一些基本概念仍然是非常重要的。虽然 Triton 抽象了很多复杂的低级细节,但它的性能仍然依赖于底层 CUDA 的硬件特性。

  • CUDA 是 Triton 的基础:Triton 的编译器最终会将代码转换成 CUDA 代码并在 GPU 上执行。因此,了解 CUDA 中的线程组织、内存模型等基础概念仍然是优化性能的重要基础。
  • 自定义优化:对于一些特定场景,程序员可能需要手动优化代码,或针对特定硬件架构进行调优,这时可能需要直接编写 CUDA 内核,或者深入理解 CUDA 的底层特性来进一步优化 Triton 生成的代码。

5. 资源学习

  • Triton 文档:可以通过官方文档详细了解 Triton 的编程模型、API 和最佳实践。
  • OpenAI 博客:可以深入了解 Triton 的设计思想、底层实现以及与 CUDA 的关系。
  • GitHub:可以查看 Triton 的源代码、示例程序和开源项目,帮助你理解如何使用 Triton 编写高效的深度学习程序。

总结:

  • CUDA 是一种低级 GPU 编程框架,程序员需要自己处理线程调度、内存访问等底层优化细节。
  • Triton 提供了一个更高层次的抽象,简化了深度学习 GPU 编程,让程序员能够专注于算法层次的开发,而不需要担心低级硬件细节。
  • Triton 是建立在 CUDA 基础之上的,因此了解 CUDA 的基本概念对深入理解 Triton 及其性能优化非常重要。

Code

通过下面简易的代码,学习triton的使用方法,以及了解triton相较于cuda的高层次抽象。

vec_add.py

这个程序使用 TritonPyTorch 实现了向量加法(x + y),并对比了两者的性能。Triton 是一个用于编写高效 GPU 内核的工具,类似于 CUDA 但更简单。程序的核心是一个 Triton 内核 add_kernel,它分块处理数据并支持边界检查。通过性能测试,程序比较了 Triton 和 PyTorch 原生加法操作的吞吐量(GB/s),并生成图表展示结果。最终目的是展示 Triton 在高性能计算中的优势。

import torch
import triton
import triton.language as tl@triton.jit
def add_kernel(x_ptr,  # *Pointer* to first input vector. 指向第一个输入向量的指针。y_ptr,  # *Pointer* to second input vector. 指向第二个输入向量的指针。output_ptr,  # *Pointer* to output vector. 指向输出向量的指针。n_elements,  # Size of the vector. 向量的大小。BLOCK_SIZE: tl.constexpr,  # Number of elements each program should process. 每个程序应处理的元素数量。# NOTE: `constexpr` so it can be used as a shape value. 注意:`constexpr` 因此它可以用作形状值。):# There are multiple 'programs' processing different data. We identify which program# 有多个“程序”处理不同的数据。需要确定是哪一个程序:pid = tl.program_id(axis=0)  # We use a 1D launch grid so axis is 0. 使用 1D 启动网格,因此轴为 0。# This program will process inputs that are offset from the initial data.# 该程序将处理相对初始数据偏移的输入。# For instance, if you had a vector of length 256 and block_size of 64, the programs would each access the elements [0:64, 64:128, 128:192, 192:256].# 例如,如果有一个长度为 256, 块大小为 64 的向量,程序将各自访问 [0:64, 64:128, 128:192, 192:256] 的元素。# Note that offsets is a list of pointers:# 注意 offsets 是指针列表:block_start = pid * BLOCK_SIZEoffsets = block_start + tl.arange(0, BLOCK_SIZE)# Create a mask to guard memory operations against out-of-bounds accesses.# 创建掩码以防止内存操作超出边界访问。mask = offsets < n_elements# Load x and y from DRAM, masking out any extra elements in case the input is not a multiple of the block size.# 从 DRAM 加载 x 和 y,如果输入不是块大小的整数倍,则屏蔽掉任何多余的元素。x = tl.load(x_ptr + offsets, mask=mask)y = tl.load(y_ptr + offsets, mask=mask)output = x + y# Write x + y back to DRAM.# 将 x + y 写回 DRAM。tl.store(output_ptr + offsets, output, mask=mask)def add(x: torch.Tensor, y: torch.Tensor):# We need to preallocate the output.# 需要预分配输出。output = torch.empty_like(x)assert x.is_cuda and y.is_cuda and output.is_cudan_elements = output.numel()# The SPMD launch grid denotes the number of kernel instances that run in parallel.# SPMD(单程序多数据) 启动网格表示并行运行的内核实例的数量。# It is analogous to CUDA launch grids. It can be either Tuple[int], or Callable(metaparameters) -> Tuple[int].# 它类似于 CUDA 启动网格。它可以是 Tuple[int],也可以是 Callable(metaparameters) -> Tuple[int]。# In this case, we use a 1D grid where the size is the number of blocks:# 在这种情况下,使用 1D 网格,其中大小是块的数量:grid = lambda meta: (triton.cdiv(n_elements, meta['BLOCK_SIZE']), )# NOTE:# 注意:#  - Each torch.tensor object is implicitly converted into a pointer to its first element.#  - 每个 torch.tensor 对象都会隐式转换为其第一个元素的指针。#  - `triton.jit`'ed functions can be indexed with a launch grid to obtain a callable GPU kernel.#  - `triton.jit` 函数可以通过启动网格索引来获得可调用的 GPU 内核。#  - Don't forget to pass meta-parameters as keywords arguments.#  - 不要忘记以关键字参数传递元参数。add_kernel[grid](x, y, output, n_elements, BLOCK_SIZE=1024)# We return a handle to z but, since `torch.cuda.synchronize()` hasn't been called, the kernel is still running asynchronously at this point.# 返回 z 的句柄,但由于 `torch.cuda.synchronize()` 尚未被调用,此时内核仍在异步运行。return outputtorch.manual_seed(0)
size = 2**25
x = torch.rand(size, device='cuda')
y = torch.rand(size, device='cuda')@triton.testing.perf_report(triton.testing.Benchmark(x_names=['size'],  # Argument names to use as an x-axis for the plot. 用作绘图 x 轴的参数名称。x_vals=[2**i for i in range(12, 28, 1)],  # Different possible values for `x_name`. `x_name` 的不同可能值。x_log=True,  # x axis is logarithmic. x 轴为对数。line_arg='provider',  # Argument name whose value corresponds to a different line in the plot. 参数名称,其值对应于绘图中的不同线条。line_vals=['triton', 'torch'],  # Possible values for `line_arg`. `line_arg` 的可能值。line_names=['Triton', 'Torch'],  # Label name for the lines. 线条的标签名称。styles=[('blue', '-'), ('green', '-')],  # Line styles. 线条样式。ylabel='GB/s',  # Label name for the y-axis. y 轴标签名称。plot_name='vector-add-performance',  # Name for the plot. Used also as a file name for saving the plot. 绘图名称。也用作保存绘图的文件名。args={},  # Values for function arguments not in `x_names` and `y_name`. 不在 `x_names` 和 `y_name` 中的函数参数值。))
def benchmark(size, provider):x = torch.rand(size, device='cuda', dtype=torch.float32)y = torch.rand(size, device='cuda', dtype=torch.float32)quantiles = [0.5, 0.2, 0.8]if provider == 'torch':ms, min_ms, max_ms = triton.testing.do_bench(lambda: x + y, quantiles=quantiles)if provider == 'triton':ms, min_ms, max_ms = triton.testing.do_bench(lambda: add(x, y), quantiles=quantiles)# 3 * x.numel() * x.element_size() 因为在执行 x + y 操作时,实际上是执行了三个步骤:加载 x 张量到 GPU,加载 y 张量到 GPU,将 x + y 的结果写回到 GPU 内存gbps = lambda ms: 3 * x.numel() * x.element_size() / ms * 1e-6return gbps(ms), gbps(max_ms), gbps(min_ms)benchmark.run(print_data=True, show_plots=True,save_path='./')

可以看到,二者性能十分接近
在这里插入图片描述

image-20250215222856862

softmax.cu

#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>__global__ void softmax_cuda(float* input, float* output, int B, int N) {int tid = blockIdx.x * blockDim.x + threadIdx.x;int bid = blockIdx.y;if (tid < N && bid < B) {// 简易实现,每个线程都在重复计算整个 batch 的最大值、指数和、softmax!int offset = bid * N;float max_val = input[offset];for (int i = 1; i < N; ++i) {max_val = max(max_val, input[offset + i]);}float sum = 0.0f;for (int i = 0; i < N; ++i) {sum += expf(input[offset + i] - max_val);}for (int i = 0; i < N; ++i) {output[offset + i] = expf(input[offset + i] - max_val) / sum;}}
}void softmax(float* x, int N) {float max = x[0];for (int i = 1; i < N; ++i) {if (x[i] > max) max = x[i];}float sum = 0.0f;for (int i = 0; i < N; ++i) {x[i] = exp(x[i] - max);sum += x[i];}for (int i = 0; i < N; ++i) {x[i] /= sum;}
}int main() {const int B = 32;    // 批量大小const int N = 1024;  // 行长float* x_cpu = (float*)malloc(B * N * sizeof(float));float* x_gpu = (float*)malloc(B * N * sizeof(float));float *d_input, *d_output;for (int i = 0; i < B * N; ++i) {x_cpu[i] = (float)rand() / RAND_MAX;x_gpu[i] = x_cpu[i];}cudaMalloc((void**)&d_input, B * N * sizeof(float));cudaMalloc((void**)&d_output, B * N * sizeof(float));cudaMemcpy(d_input, x_gpu, B * N * sizeof(float), cudaMemcpyHostToDevice);int theads_per_block = 256;int blocks_per_grid_x = (N + theads_per_block - 1) / theads_per_block;dim3 grid_dim(blocks_per_grid_x, B);// 二维网格,一维blocksoftmax_cuda<<<grid_dim, theads_per_block>>>(d_input, d_output, B, N);cudaMemcpy(x_gpu, d_output, B * N * sizeof(float), cudaMemcpyDeviceToHost);softmax(x_cpu, N);float max_diff = 0.0f;for (int i = 0; i < N; ++i) {float diff = fabsf(x_cpu[i] - x_gpu[i]);if (diff > max_diff) {max_diff = diff;}}printf("Maximum difference between CPU and GPU results (first batch): %e\n", max_diff);// Clean upfree(x_cpu);free(x_gpu);cudaFree(d_input);cudaFree(d_output);return 0;
}

softmax.py

import torch
import triton
import triton.language as tl@triton.jit
def softmax_kernel(output_ptr, input_ptr, input_row_stride, output_row_stride, n_cols,BLOCK_SIZE: tl.constexpr,
):# Get the program IDrow_idx = tl.program_id(axis=0)# Compute the memory offsets for this rowrow_start_ptr = input_ptr + row_idx * input_row_strideout_row_start_ptr = output_ptr + row_idx * output_row_stride# Load the row into SRAMrow = tl.load(row_start_ptr + tl.arange(0, BLOCK_SIZE), mask=tl.arange(0, BLOCK_SIZE) < n_cols, other=-float('inf'))# other=-float('inf') 为了保证数值稳定性,在 softmax 计算中,指数运算前减去了最大值。如果某些列的值为负无穷,在指数运算后会变成零,不会影响最终的归一化结果。# Compute max for numerical stabilityrow_max = tl.max(row, axis=0)# Subtract max from row and exponentiatenumerator = tl.exp(row - row_max)# Compute sum for normalizationdenominator = tl.sum(numerator, axis=0)# Normalizesoftmax_output = numerator / denominator# Store the outputtl.store(out_row_start_ptr + tl.arange(0, BLOCK_SIZE), softmax_output, mask=tl.arange(0, BLOCK_SIZE) < n_cols)def triton_softmax(x):n_rows, n_cols = x.shapeoutput = torch.empty_like(x)# Determine the block sizeBLOCK_SIZE = triton.next_power_of_2(n_cols)BLOCK_SIZE = min(BLOCK_SIZE, 1024)  # Launch the Triton kernelgrid = (n_rows,)softmax_kernel[grid](output, x,x.stride(0), output.stride(0),n_cols, BLOCK_SIZE=BLOCK_SIZE)return output# Set up the input tensor
torch.manual_seed(0)
x = torch.randn(256, 1000, device='cuda')
# x = torch.tensor([[1.0, 2.0, 3.0]], device='cuda')
# Compute softmax using PyTorch
torch_result = torch.softmax(x, dim=1)# Compute softmax using Triton
triton_result = triton_softmax(x)# Compare results
max_diff = torch.max(torch.abs(torch_result - triton_result))
print(f"Maximum difference between PyTorch and Triton results: {max_diff:.2e}")# Check if results are close
is_close = torch.allclose(torch_result, triton_result, rtol=1e-5, atol=1e-5)
print(f"Results are close: {is_close}")

参考资料:https://github.com/Infatoshi/cuda-course/tree/master/08_Triton

相关文章:

【CUDA】Triton

【CUDA】Triton 1. CUDA 与 Triton 的基本区别 CUDA 编程模型&#xff1a; 在传统的 CUDA 编程中&#xff0c;CUDA 是标量程序&#xff0c;带有阻塞线程&#xff08;blocked threads&#xff09;。 标量程序&#xff08;Scalar Program&#xff09;&#xff1a;表示我们直接…...

Windows环境搭建ES集群

搭建步骤 下载安装包 下载链接&#xff1a;https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.27-windows-x86_64.zip 解压 解压并复制出3份 es-node1配置 config/elasticsearch.yml cluster.name: xixi-es-win node.name: node-1 path.data: D:\\wor…...

langchain学习笔记之消息存储在内存中的实现方法

langchain学习笔记之消息存储在内存中的实现方法 引言背景消息存储在内存的实现方法消息完整存储&#xff1a;完整代码 引言 本节将介绍 langchain \text{langchain} langchain将历史消息存储在内存中的实现方法。 背景 在与大模型交互过程中&#xff0c;经常出现消息管理方…...

怎么在智能合约中植入deepseek

怎么在智能合约中植入deepseek 这里写目录标题 怎么在智能合约中植入deepseek方法概述具体步骤1. 部署大语言模型到链下2. 创建预言机(Oracle)a. 部署预言机节点b. 创建自定义预言机接口(Custom Oracle)3. 设计智能合约a. 编写Solidity代码b. 部署智能合约4. 调用流程注意事…...

驱动开发系列37 - Linux Graphics 2D 绘制流程(二)- 画布创建和窗口关联

一:概述 前面介绍Pixmap表示一块画布,是绘制发生的地方,本节看看驱动程序如何为画布分配内存/显存,以及如何与窗口关联的。 二:为画布分配BO 在系统启动时(用户登录系统之后,会重启Xorg),在 Xorg 服务器初始化时,要为屏幕创建根窗口的 Pixmap,并绑定到 GPU framebu…...

B. Longest Divisors Interval

time limit per test 2 seconds memory limit per test 256 megabytes Given a positive integer nn, find the maximum size of an interval [l,r][l,r] of positive integers such that, for every ii in the interval (i.e., l≤i≤rl≤i≤r), nn is a multiple of ii. …...

前端与后端的对接事宜、注意事项

前端与后端的对接事宜、注意事项 一、对接核心流程(完整生命周期) #mermaid-svg-6yzij6OD8DKqiMLD {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6yzij6OD8DKqiMLD .error-icon{fill:#552222;}#mermaid-svg-6yzi…...

【第13章:自监督学习与少样本学习—13.2 少样本学习(FSL)与元学习(Meta-Learning)的基础理论与应用案例】

凌晨三点的急诊室,值班医生李大夫正在使用AI辅助诊断系统——面对一张仅有3个标注病例的罕见皮肤病影像,系统竟然给出了95%置信度的准确诊断。这种"见微知著"的超能力,正是少样本学习技术创造的医学奇迹。 一、突破数据荒漠:少样本学习的生存法则 1.1 从人类学习…...

函数防抖和节流

所谓防抖&#xff0c;就是指触发事件后在 n 秒内函数只能执行一次&#xff0c; 如果在 n 秒内又触发了事件&#xff0c;则会重新计算函数执行时间&#xff0c; 短时间高频率触发只有最后一次触发成功 开发使用场景&#xff1a; 搜索框防抖 fn代表要被防抖或者节流的函数&#x…...

linux--关于linux文件IO(2) open、read、lseek、stat

open 在linux中的读写文件有对应的命令。在终端中输入man 2 open可以打开open的手册页&#xff0c;注意man 2是linux自己的函数的一些手册&#xff0c;man 3是C库的手册 打开手册页之后找到open函数的用法如下&#xff1a; #以下是需要的库文件&#xff0c;man 2 open打开直接…...

利用xtquant高效获取财务数据:量化分析的重要补充

利用xtquant高效获取财务数据&#xff1a;量化分析的重要补充 在量化交易领域&#xff0c;虽然市场行情数据是核心&#xff0c;但财务数据作为企业基本面的重要反映&#xff0c;同样不可忽视。通过深入分析企业的财务报表&#xff0c;投资者可以更好地理解企业的经营状况和未来…...

Unity UI个人总结

个人总结&#xff0c;太简单的直接跳过。 一、缩放模式 1.固定像素大小 就是设置一个100x100的方框&#xff0c;在1920x1080像素下在屏幕中长度占比1/19&#xff0c;在3840x2160&#xff0c;方框在屏幕中长度占比1/38。也就是像素长款不变&#xff0c;在屏幕中占比发生变化 2.…...

Javascript的数据类型

Javascript的数据类型 1.基本数据类型1.1七种基本数据类型1.2单独说说BigInt‌1.3其它注意点 2.引用数据类型3.基本数据类型和引用数据类型的区别4.双等于号和三等于号的区别5.Javascript的类型转换机制5.1显示转换(强制转换)5.2隐式转换(1)减、乘、除(2)加(加法要区别算,因为不…...

Day3 25/2/16 SUN

【一周刷爆LeetCode&#xff0c;算法大神左神&#xff08;左程云&#xff09;耗时100天打造算法与数据结构基础到高级全家桶教程&#xff0c;直击BTAJ等一线大厂必问算法面试题真题详解&#xff08;马士兵&#xff09;】https://www.bilibili.com/video/BV13g41157hK?p4&v…...

欧洲分组加密算法之Kasumi

目录 (1)FL函数 (2)FO函数 (3)FI函数 密钥扩展算法 欧洲分组加密算法之Kasumi Kasumi分组密码算法是由欧洲标准机构ETSI(European Telecommunications Standards Institute)下属的安全算法组于1999年设计的,被用于构造A5/3、GEA3、f8和f9算法,参与移动通信系统无线…...

vue使用v-chart的实践心得

开发Vue2和Vue3时&#xff0c;我们常常需要将数据以图表的形式展示给用户&#xff0c;而 V-Chart 作为一个轻量级且易于集成的图表库&#xff0c;是 Vue 开发的首选。这篇文章&#xff0c;我将写一下关于我在使用这方面的心得。 echarts官网&#xff1a;https://echarts.apach…...

Endnote使用笔记——持续更新

&#xff08;1&#xff09;如果样式库里没有想要的期刊格式&#xff0c;可以到这个网址进行下载&#xff0c;并放在本地安装Endnote的文件下边的styles文件里&#xff1a; https://endnote.com/downloads/styles/ &#xff08;2&#xff09;EndNote导入参考文献时&#xff0c;关…...

Tetragon:一款基于eBPF的运行时环境安全监控工具

关于Tetragon Tetragon是一款基于eBPF的运行时环境安全监控工具&#xff0c;该工具可以帮助广大研究人员检测并应对安全重大事件&#xff0c;例如流程执行事件、系统调用活动、I/O活动&#xff08;包括网络和文件访问等&#xff09;。 在 Kubernetes 环境中使用时&#xff0c;…...

CAS单点登录(第7版)23.Webflow 管理

如有疑问&#xff0c;请看视频&#xff1a;CAS单点登录&#xff08;第7版&#xff09; Webflow 管理 概述 Webflow定制 CAS 使用 Spring Webflow 对登录和注销协议进行脚本处理。Spring Web Flow 构建在 Spring MVC 之上&#xff0c;并允许实现 Web 应用程序的“流”。流封装…...

word文档中标题的自动编号问题

最近研究了下标题自动编号&#xff0c;记录下来&#xff0c;以备后用。 &#xff08;1&#xff09;从编号1开始&#xff0c;如&#xff1a; 1 ------------------------ 标题1 1.1 ------------------- 标题2 1.1.1 ------------------- 标题3 1.1.1.1 ------------------- 标题…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...