当前位置: 首页 > 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 ------------------- 标题…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

若依登录用户名和密码加密

/*** 获取公钥&#xff1a;前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...