CUDA-MODE课程笔记 第8课: CUDA性能检查清单
我的课程笔记,欢迎关注:https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/cuda-mode
CUDA-MODE课程笔记 第8课: CUDA性能检查清单
课程笔记
这节课实际上算是CUDA-MODE 课程笔记 第一课: 如何在 PyTorch 中 profile CUDA kernels 这节课更细节的讲解。另外关于nsight compute相关指标细节解释可以参考 CUDA-MODE 第一课课后实战(上),
CUDA-MODE 第一课课后实战(下) 这两篇笔记。
将GPU用于计算,我们最关心的肯定是性能。比较幸运的是,当我们掌握一些性能优化技巧之后它往往会经常被用到。这节课将会系统的介绍这些性能优化技巧。
本节课的课件和代码都在 https://github.com/cuda-mode/lectures 开源,我们可以用nvcc编译lecture8下面的cu文件,然后使用ncu进行profile。此外,这里的方法遵循了 https://arxiv.org/pdf/1804.06826.pdf 这篇论文的风格。up主也非常推荐大家阅读下这篇论文,用claude 3.5问了一下paper的主要内容,如下截图:
可以看到这篇论文主要是对Volta架构的GPU的架构细节进行分析,对性能优化是很重要的。
这张Slides从物理学的角度分析了下SRAM和DRAM的区别:
- DRAM由1个晶体管和1个电容器构成;SRAM: 由6个晶体管构成
- SRAM 比 DRAM 更快,但也更贵;SRAM 占用更多空间且发热更多;
实际上SRAM就对应了GPU的Shared Memory,而DRAM对应的则是Shared Memory。
这里的youtube链接作者Bill是NVIDIA的首席科学家,他解释了很多为什么GPU设计成现在这个样子,并且由浅入深,基础细节讲的非常清楚。
这里的"性能检查清单"(Performance checklist),列出了一系列优化GPU程序性能的策略和技巧:
- 合并全局内存访问(Coalesced Global Memory Access)
- 最大化占用率(Maximize occupancy)
- 理解是内存受限还是计算受限(Understand if memory or compute bound)
- 最小化线程分化(Minimize control divergence)
- Tiling以更好的重用数据(Tiling of reused data)
- 私有化(Privatization)
- Thread Coarsening
- 使用更好的数学方法重写算法(Rewrite your algorithm using better math)
这里的Privatization指的应该就是Shared Memory/寄存器优化全局内存读取,而Coarsening大概指的就是一个线程应该完成多少任务,一般情况下我们让一个线程完成的任务尽量少,但是在Compute Bound情况下,让一个线程执行更多的工作可以让程序运行得更快。最后一点更好的数学方法重写算法的经典例子就是Flash Attention。
这张Slides讲述了GPU内存访问延迟的相关内容,下面的Figure3和表格都来自 https://arxiv.org/pdf/2208.11174 ,这个表格(Table IV),列出了不同类型内存的访问延迟(以时钟周期为单位):
- 全局内存(Global memory): 290 cycles
- L2 缓存: 200 cycles
- L1 缓存: 33 cycles
- 共享内存(Shared Memory): 读取23 cycles,写入19 cycles
我后面也找到了这个paper里面做micro benchmark的代码:https://www.stuffedcow.net/research/cudabmk?q=research/cudabmk ,后面如果有空继续阅读下这篇 paper 以及测试代码。
这张Slides讲述了延迟(latency)在计算机系统中的重要性和一些相关概念。
- 标题 “It’s the latency stupid” 强调了延迟的重要性。
- 吞吐量(Throughput)和延迟(Latency)的对比:
- 吞吐量容易提高,但延迟却很难降低。
- 举例说明:即使你可以并行使用80条电话线,每条线传输一个比特,但100毫秒的延迟仍然存在。
- 量化(Quantization)技术:
- 用于减少数据包大小的一种方法。
- 例如,Bolo(可能是某个系统或协议)尽可能使用字节(byte)而不是16位或32位字来减少数据包大小。
- 底部提供了一个网址链接,包含更多关于这个话题的详细讨论。
这张Slides开始介绍内存合并(Memory Coalescing)的概念。我们无法减少延迟,但可以通过读取连续的内存元素来隐藏延迟。Slides建议在进行案例研究时要关注以下三个方面:
- DRAM Throughput(DRAM吞吐量)
- Duration(持续时间)
- L1 cache throughput(L1缓存吞吐量)
这里说的内存合并的案例就是 https://github.com/cuda-mode/lectures/blob/main/lecture_008/coalesce.cu 这里所展示的。代码如下:
#include <iostream>
#include <cuda_runtime.h>__global__ void copyDataNonCoalesced(float *in, float *out, int n) {int index = blockIdx.x * blockDim.x + threadIdx.x;if (index < n) {out[index] = in[(index * 2) % n];}
}__global__ void copyDataCoalesced(float *in, float *out, int n) {int index = blockIdx.x * blockDim.x + threadIdx.x;if (index < n) {out[index] = in[index];}
}void initializeArray(float *arr, int n) {for(int i = 0; i < n; ++i) {arr[i] = static_cast<float>(i);}
}int main() {const int n = 1 << 24; // Increase n to have a larger workloadfloat *in, *out;cudaMallocManaged(&in, n * sizeof(float));cudaMallocManaged(&out, n * sizeof(float));initializeArray(in, n);int blockSize = 128; // Define block size// int blockSize = 1024; // change this when talking about occupancyint numBlocks = (n + blockSize - 1) / blockSize; // Ensure there are enough blocks to cover all elements// Launch non-coalesced kernelcopyDataNonCoalesced<<<numBlocks, blockSize>>>(in, out, n);cudaDeviceSynchronize();initializeArray(out, n); // Reset output array// Launch coalesced kernelcopyDataCoalesced<<<numBlocks, blockSize>>>(in, out, n);cudaDeviceSynchronize();cudaFree(in);cudaFree(out);return 0;
}
这里段程序比较简单,用于演示内存合并(Memory Coalescing)的概念和其对性能的影响。它主要做了以下事情:
- 定义了两个CUDA kernel:
- copyDataNonCoalesced kernel:非合并内存访问模式,以非连续的方式读取输入数组(使用 (index * 2) % n 作为索引),这种访问模式会导致非合并的内存访问,可能降低性能。
- copyDataCoalesced kernel:合并内存访问模式,以连续的方式读取输入数组(直接使用 index 作为索引),这种访问模式允许合并内存访问,可以提高性能。
- 主函数:
- 分配统一内存(Unified Memory)用于输入和输出数组,初始化输入数组。
- 设置CUDA网格和块的大小,分别运行非合并和合并的kernel,在每次kernel执行后使用 cudaDeviceSynchronize() 确保GPU操作完成。
接着使用nvcc -o benchmark coalesce.cu
来编译程序,然后执行ncu benchmark
来Profile程序。
对于copyDataNonCoalesced kernel来说,DRAM内存吞吐量大约是89%,L1 Cache的吞吐量是30%,kernel的执行时间是764us。
对于copyDataCoalesced kernel来说,L1 Cache的吞吐量大约是37%,DRAM内存吞吐量是82%,执行时间是558us。
我们可以看到合并内存访问的kernel是有明显的性能提升的。可以预见,随着输入数据量的增大合并内存访问的优势会更明显。ncu的结果里面还提示计算的理论occupancy(100.0%)和实测的实际occupancy占用(77%)之间的差异可能是由于 kernel 执行期间的warp调度开销或工作负载不平衡导致的。在同一kernel 的不同块之间以及块内的不同 warps 之间都可能发生负载不平衡。把上面程序中的int blockSize = 128
改成int blockSize = 1024
再次用ncu profile,可以发现occupancy提升到了85.94%。
这张Slides讨论了GPU中的占用率(Occupancy)问题,主要内容如下:
- 两种quantization问题:
- a) Tile quantization:矩阵维度不能被线程块Tile大小整除。
- b) Wave quantization:Tile总数不能被GPU上的SM(流多处理器)数量整除。
- 性能图表比较和分析:
- 左图(a):cuBLAS v10 上 NN GEMM 的性能
- 右图(b):cuBLAS v11 上 NN GEMM 的性能
- 两图都是在 M = 1024, N = 1024 的矩阵维度下进行的测试
- 左图(a)显示性能呈现明显的阶梯状,有大幅波动。
- 右图(b)显示性能波动较小,整体更加平滑。
我们可以看到cuBLAS v11 可能采用了更好的调度策略或优化技术,减少了由于Tile和Wave Quantization 导致的性能波动。
这张Slides讲解了在PyTorch中使用padding(填充)来解决Tensor Core矩阵乘法维度要求的问题。具体内容如下:
- 在PyTorch环境中,使用padding是解决某些问题的方法。
- 表格展示了不同cuBLAS和cuDNN版本下,使用Tensor Core的数据精度要求。这些要求适用于矩阵维度M、N和K。
- 版本区分:
- 左列:cuBLAS < 11.0 和 cuDNN < 7.6.3 的旧版本
- 右列:cuBLAS ≥ 11.0 和 cuDNN ≥ 7.6.3 的新版本
- 数据类型的要求:
- INT8:旧版本要求16的倍数;新版本总是可用,但16的倍数最高效,在A100上128的倍数最佳。
- FP16:旧版本要求8的倍数;新版本总是可用,但8的倍数最高效,在A100上64的倍数最佳。
- TF32:旧版本不适用;新版本总是可用,但4的倍数最高效,在A100上32的倍数最佳。
- FP64:旧版本不适用;新版本总是可用,但2的倍数最高效,在A100上16的倍数最佳。
新版本的cuBLAS和cuDNN提供了更灵活的Tensor Core使用条件。而A100 GPU可能需要更大的倍数来获得最佳性能。Padding可以用来将矩阵维度调整为这些推荐的倍数,以提高性能。
在CUDA中提升Occupancy的一个方法是修改kernel。
CUDA Occupancy calculator工具可以帮我们自动计算达到更好Occupancy的kernel启动参数,在上一节合并访存的.cu中调用这个Api结果显示,对于T4 GPU,最优的配置是网格大小为40,块大小为1024。代码见:https://github.com/cuda-mode/lectures/blob/main/lecture_008/occupancy.cu
在对这个程序进行ncu的时候有新的问题,那就是下面所展示的:
警告(WRN):内存利用率高于计算利用率:请查看内存工作负载分析部分以识别DRAM瓶颈。检查内存重放(合并)指标,以确保您正在有效利用传输的字节。同时考虑是否可以通过每次内存访问执行更多工作(kernel融合)或是否有可以(重新)计算的值。
接下来开始讨论这个问题
讨论之前需要先了解一下这张Slides展示的Roofline模型,它决定了一个cuda kernel是compute bound还是memory bound。
这张Slides讲解了算术强度(Arithmetic intensity)的概念及其在处理器性能分析中的应用。这个slides来自gtc2019的一个讲解。
左侧指标是数学运算和内存操作的算法混合,称为算术强度。右侧指标是处理器的ops/byte比率。例如,V100 GPU可以执行125/0.9=139 FLOPS/B。比较算术强度和ops/byte比率可以指出算法受什么因素限制。
下面还给出了操作类型及其算术强度表格:
- Residual addition(残差加法):0.166,受内存限制
- ReLU activation(ReLU激活):0.25,受内存限制
- Batch normalization(批量归一化):O(10),受内存限制
- Convolution(卷积):1-10000+(假设FP16数据),可能受内存或数学运算限制
链接:https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9926-tensor-core-performance-the-ultimate-guide.pdf
这张slides讲解了ReLU(Rectified Linear Unit)函数的算术强度分析:
- ReLU函数定义:f(x) = max(0, x),应用于向量的每个元素。
- 操作描述:对每个元素进行1次读取、1次比较操作,可能还有1次写入。
- 数据类型:假设使用float32,即每个数占4字节(32位)。
- 计算分析:
- 操作数(Ops):1(每个元素一次比较操作)
- 字节数(Byte):2 * 4 = 8(读取和可能的写入,每次4字节)
- 算术强度计算:
- 最坏情况:1/8(当每个元素都需要写入时)
- 最好情况:1/4(当不需要写入时,只有读取操作)
结论:1/4 < 1,表明ReLU操作受内存带宽限制(Memory bound)
这张Slides对Float16的ReLU进行了算术强度分析,可以看打这种情况下最坏的算术强度是1/4,而不是Float32时的1/8,因此量化是可以提高计算强度的。
这张Slides讲解了矩阵乘法(Matmul)的算术强度分析。其中:
- FLOPS(浮点运算次数)计算:
- 对C中的每个输出元素,需要A的一行和B的一列做点积
- 需要N次乘法和N次加法
- 总FLOPS = M * K * 2N
- 字节数计算:
- 加载矩阵A和B:MN + NK
- 写入输出矩阵C:MK
- 总字节数 = MN + NK + MK
- 算术强度(AI)计算:
- AI = 2MNK / (MN + NK + MK)
- 结论:
- 对于大型矩阵,计算受限(Compute bound)
- 否则,带宽受限(Bandwidth bound)
这张Slides总结了如何优化不同类型的kernels:
- 带宽受限的kernel(Bandwidth Bound Kernels)优化策略:
- Fuse(融合):合并多个操作以减少内存访问
- Quantize(量化):使用更小的数据类型来减少内存传输
- Compile(编译):可能指使用特定的编译技术来优化内存访问模式
- 计算受限的kernel(Compute Bound Kernels)优化策略:
- Write a better algorithm(编写更好的算法):这意味着需要从算法层面进行优化
关于矩阵乘法Tiling减少全局内存访问请查看以前的CUDA-MODE 课程笔记 第四课: PMPP 书的第4-5章笔记 。
这张Slides对应这里的代码: https://github.com/cuda-mode/lectures/blob/main/lecture_008/divergence.cu ,主要是对下面2个kernel进行分析:
__global__ void processArrayWithDivergence(int *data, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) {if (data[idx] % 2 == 0) {data[idx] = data[idx] * 2; // 注意这个分支比下面的分支要慢,可能一个Warp里执行这个分支的线程会落后,Warp里的其它线程必须等待这些线程计算完成} else {data[idx] = data[idx] + 1;}}
}__global__ void processArrayWithoutDivergence(int *data, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N) {int isEven = !(data[idx] % 2); // 这里做的事情和上面相同,但是规避了线程分化问题data[idx] = isEven * (data[idx] * 2) + (!isEven) * (data[idx] + 1);}
}
- 控制分歧(control divergence)与占用率(occupancy)有关,但如果条件语句导致大量线程闲置,这是不好的。
- processArrayWithDivergence 耗时 0.074272 毫秒; processArrayWithoutDivergence 耗时 0.024704 毫秒;这表明去除control divergence可以显著提高性能(约3倍)。
- “ncu --set full divergence” 用这行命令来设置线程control divergence分析。
对于compute bound的kernel,让线程可以做更多工作,可能会更快。
- 性能比较:
- 运行命令:main ~/lecturex ./benchmark
- VecAdd 执行时间:0.245600 ms
- VecAddCoarsened 执行时间:0.015264 ms
- 关键观察:
- VecAddCoarsened启动了一半的线程数量
- 尽管线程数减少,但执行速度显著提高(约16倍)
这里的代码在 https://github.com/cuda-mode/lectures/blob/main/lecture_008/coarsening.cu 。
这也许可以解释Lecture 7中为什么对于Int4 Weight Only量化的高效kernel实现比普通的fp16的Kernel跑得更快。
这张Slides讨论了在GPU编程中的"私有化"(Privatization)技术。要点为:
- 将部分更新应用到数据的私有副本上,然后再写回全局或共享内存。
- 示例:
- 滑动窗口算法(Sliding window algorithm)
- 图示:1 2 [3] [4] [5] 6 7
- 这表明算法在一个局部窗口内进行操作。
- Privatization的优势:
- 更高的占用率(Higher occupancy)
- 更高的计算SM吞吐量(Higher compute SM throughput)
- 更低的DRAM吞吐量(Lower DRAM throughput)
这个滑动窗口算法对应的例子就是Mistral等大模型里面的滑动窗口注意力算法,
解释下这个图:
- 左侧矩阵:Vanilla Attention
- 展示了传统注意力机制,每个token都可以关注到所有其他token。
- 矩阵是下三角形,表示每个token只能关注到它自己和之前的token。
- 中间矩阵:Sliding Window Attention
- 展示了滑动窗口注意力机制,每个token只关注固定窗口大小内的相邻token。
- 这里窗口大小W=3,可以看到每个token只与前后3个token有连接。
- 右侧图:Effective Context Length
- 展示了多层滑动窗口注意力如何扩大有效上下文长度。
- 每一层都可以使信息向前传播W个token。
总结来说,传统注意力的操作数量与序列长度成二次方关系,内存使用随token数线性增长。在推理时,这会导致更高的延迟和更低的吞吐量,因为缓存可用性降低。滑动窗口注意力通过限制每个token最多只关注前一层的W个token来缓解这个问题。虽然窗口外的token不直接参与注意力计算,但它们仍然可以影响下一个词的预测。在每个注意力层,信息可以向前传播W个token。经过k个注意力层后,信息可以向前传播最多k × W个token。
继续讨论Privatization技术,https://github.com/cuda-mode/lectures/blob/main/lecture_008/privatization.cu 这里的核心代码是:
// CUDA kernel for vector addition without privatization
__global__ void vectorAdd(const float *a, const float *b, float *result, int n) {int index = threadIdx.x + blockIdx.x * blockDim.x;if (index < n) {result[index] = a[index] + b[index];}
}// CUDA kernel for vector addition with privatization
__global__ void vectorAddPrivatized(const float *a, const float *b, float *result, int n) {int index = threadIdx.x + blockIdx.x * blockDim.x;if (index < n) {float a_private = a[index]; // Load into private memoryfloat b_private = b[index]; // Load into private memoryresult[index] = a_private + b_private;}
}
我们把a[index]
,b[index]
加载到private memory里面避免对全局内存的直接操作,但在这个VectorAdd的例子中没有加速。
但是在下面的滑动窗口求和的例子中通过把global memory加载到shared memory中,然后进行累加时求和操作就是在shared memory中进行操作。
代码链接:https://github.com/cuda-mode/lectures/blob/main/lecture_008/privatization2.cu
// Kernel without privatization: Direct global memory access
__global__ void windowSumDirect(const float *input, float *output, int n, int windowSize) {int idx = blockIdx.x * blockDim.x + threadIdx.x;int halfWindow = windowSize / 2;if (idx < n) {float sum = 0.0f;for (int i = -halfWindow; i <= halfWindow; ++i) {int accessIdx = idx + i;if (accessIdx >= 0 && accessIdx < n) {sum += input[accessIdx];}}output[idx] = sum;}
}// Kernel with privatization: Preload window elements into registers
__global__ void windowSumPrivatized(const float *input, float *output, int n, int windowSize) {int idx = blockIdx.x * blockDim.x + threadIdx.x;int halfWindow = windowSize / 2;__shared__ float sharedData[1024]; // Assuming blockDim.x <= 1024// Load input into shared memory (for demonstration, assuming window fits into shared memory)if (idx < n) {sharedData[threadIdx.x] = input[idx];__syncthreads(); // Ensure all loads are completefloat sum = 0.0f;for (int i = -halfWindow; i <= halfWindow; ++i) {int accessIdx = threadIdx.x + i;// Check bounds within shared memoryif (accessIdx >= 0 && accessIdx < blockDim.x && (idx + i) < n && (idx + i) >= 0) {sum += sharedData[accessIdx];}}output[idx] = sum;}
}
作者最后讲的一点是以Flash Attention为例,如果你可以从数学的角度重写算法,有可能可以让代码的性能大幅度提升。比如Flash Attention利用Safe Softmax的数学形式分块计算Attention。这部分讲解已经非常多了,大家可以参考 https://github.com/BBuf/how-to-optim-algorithm-in-cuda/README.md 里面收集的Flash Attention相关的资料,最后这几张Slides就不再赘述了。
总结
这节课相当于对Lecture 1更系统的补充,重点介绍GPU kernel优化的一些实用技术和分析工具ncu。课程深入探讨了“性能检查清单”,概述了关键的优化策略:
- 合并全局内存访问: 确保线程访问连续的内存位置,以最大限度地提高内存带宽利用率。
- 最大化占用率: 优化kernel启动参数,以充分利用 GPU 的处理能力。
- 理解内存与计算限制: 分析kernel特征以确定限制因素(内存带宽或计算能力),并应用适当的优化技术。
- 最小化线程的分化: 避免导致warp内的线程采用不同执行路径的条件语句,从而导致性能下降。
- Tiling以重用数据: 组织数据访问模式,以最大限度地提高缓存中的数据局部性和重用率。
- 私有化: 利用私有内存(寄存器或共享内存)来减少全局内存访问并提高占用率。
- 线程粗化: 调整线程粒度以平衡工作负载并最小化线程开销。
- 算法重写: 探索替代的数学公式或算法以提高计算效率。
up主还通过cuda代码示例和使用 ncu 工具的分析结果来说明这些概念。它展示了内存合并、占用率优化和控制分歧最小化的好处。它还介绍了用于分析kernel性能瓶颈的 Roofline 模型,以及算术强度的概念,以确定kernel是受内存限制还是受计算限制。接着up主讨论了私有化技术,重点介绍了在滑动窗口算法中使用共享内存来改善数据局部性和减少全局内存访问。最后,简单描述了一下通过从数学角度重写算法来获得显著性能提升的潜力,并以 Flash Attention 为主要示例,这里就没有截最后几张slides了,毕竟Flash Attention的资料非常多了。
相关文章:

CUDA-MODE课程笔记 第8课: CUDA性能检查清单
我的课程笔记,欢迎关注:https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/cuda-mode CUDA-MODE课程笔记 第8课: CUDA性能检查清单 课程笔记 这节课实际上算是CUDA-MODE 课程笔记 第一课: 如何在 PyTorch 中 profile CUDA kernels 这…...

【备战蓝桥杯青少组】第二天 奇特的砖墙
真题 第十四届省赛 编程题 第5题 工人砌了一面奇特的砖墙,该墙由N列砖组成(1≤N≤1e6),且每列砖的数量为Ki(1≤Ki≤1e4,相邻砖块之间无缝隙),每块砖的长宽高都为1。小蓝为了美化这面…...

图像处理 -- 仿射变换之Affine Transformation
仿射变换(Affine Transformation) 仿射变换是图像处理中的一种基本操作,通过线性变换和平移实现图像的几何变换。仿射变换包括旋转、缩放、平移、翻转、错切(shear)等操作。 1. 仿射变换的作用 旋转:将图…...

Nuxt3【项目配置】nuxt.config.ts
按环境添加配置 export default defineNuxtConfig({// 生产环境的配置$production: {routeRules: {/**: { isr: true }}},// 开发环境的配置$development: {//} })运行时的配置 runtimeConfig export default defineNuxtConfig({runtimeConfig: {// 只在服务器端可用的私有键ap…...

中智讯“2024高校人工智能边缘应用项目实战师资培训班”圆满举办
7月24日——7月28日,中智讯“2024高校人工智能&边缘应用项目实战师资培训班”在昆明理工大学成功举办。本次培训由昆明理工大学人工智能产业学院主办,中智讯(武汉)科技有限公司承办。来自昆明理工大学、西南林业大学、云南民族…...

IIS发布打包后文件
1.打开IIS软件 2 添加网站, 自定义网站名称-选择要放置的资源路径-选择IP地址 3.打开放置的资源目录放置打包后文件 4.选择浏览 搜索不到IIS可进行一下操作 控制面板-程序和功能-启用或关闭windows功能-勾选IIS...

四个自定义 SHAP 图
超越 Python 包,创建 SHAP 值的定制可视化 SHAP 值是了解模型如何进行预测的绝佳工具。SHAP 包提供了许多可视化效果,使这个过程更加简单。话虽如此,我们不必完全依赖这个包。我们可以通过创建自己的 SHAP 图来进一步了解模型的工作原理。在本…...

为什么使用HTTPS?
HTTPS现在是所有Web活动的首选协议,因为它是用户保护敏感信息的最安全方式。 HTTPS不仅对请求用户信息的网站至关重要。除了用户直接发送的信息外,攻击者还可以从不安全的连接中跟踪行为和身份数据。 HTTP为网站所有者带来的好处除了数据安全之外&…...

软件设计-系统架构师(五十五)
1在网络规划中,政府内外网之间应该部署网络安全防护设备。在下图部署的设备A是(),对设备A作用描述错误的是()。 问题1 A IDS B 防火墙 C 网闸 D UTM 问题2 A 双主机系统,即使外网被黑客攻击…...

三分钟学会线缆电流估算
今天带你用三分钟的时间,学会电源线承受电流估算,不浪费时间,直接开始吧。 工作温度30℃,长期连续90%负载下的载流量 1.5平方毫米――14A 2.5平方毫米――26A 4平方毫米――32A 6平方毫米――47A 16平方毫米――92A 25平方毫米――120A 3…...

Snipaste 的一款替代工具 PixPin,支持 gif 截图、长截图和 OCR 文字识别,功能不是一点点强!
Snipaste 的一款替代工具 PixPin,支持 gif 截图、长截图和 OCR 文字识别,功能不是一点点强! PixPin 的名字来源于“Pixel Pin”,简单来说是一个截图、贴图的工具,但是 PixPin 以截图和贴图两大功能为核心做了大量的优…...

Oracle基础教程
体系结构 数据库 一个操作系统仅有一个数据库 实例 拥有一系列后台进程和存储结构,一个数据库可拥有一个或多个实例,一般只有1个实例 数据文件(.dbf/.ora) 数据文件是数据库的物理存储单元,一个表空间由一个或多…...

电脑如何录屏?三款电脑录屏工具分享
电脑如何录屏?作为一个经常需要录制电脑屏幕大职场人,不是为了制作教程、记录会议,就是偶尔想自己做个游戏解说视频。市面上的录屏软件琳琅满目,经过一番尝试和比较,我选出了三款我个人认为表现不错的软件,…...

idea2024建立maven web项目servlet 6.0
(1) 下载好tomcat 10.1.28 打开tomcat.apache.org官网下载 (2)配置好maven (3)idea 2024打开,建立项目 选择maven java项目 (4)在项目src/main/下 建立webapp/WEB-INF目录,在…...

游戏开放式新手引导框架设计
强制性引导:只能点某个按钮 优:程序简单 缺: 玩家体验差 开放式引导:不强制点 优:玩家体验好 缺: 程序复杂 需求分析: 1.开放式引导,引导是到达某个条件后进行一系列行为(…...

【Hot100】LeetCode—189. 轮转数组
目录 1- 思路自定义 reverse 翻转函数 2- 实现⭐189. 轮转数组——题解思路 3- ACM 实现 原题链接:189. 轮转数组 1- 思路 自定义 reverse 翻转函数 2- 实现 ⭐189. 轮转数组——题解思路 class Solution {public void rotate(int[] nums, int k) {k % nums.lengt…...

javaweb学习之HTML(一)
推荐学习使用网站 w3school 在线教程 认识HTML HTML(HyperText Markup Language)是超文本标记语言,它是一个用于创建网页和网页应用程序的标准标记语言。HTML文档由一系列的元素(elements)组成,这些元素通…...

项目实战:Qt+Opencv相机标定工具v1.3.0(支持打开摄像头、视频文件和网络地址,支持标定过程查看、删除和动态评价误差率,支持追加标定等等)
若该文为原创文章,转载请注明出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/141334834 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、Op…...

【数据结构】汇总八、排序算法
排序Sort 【注意】本章是 排序 的知识点汇总,全文1万多字,含有大量代码和图片,建议点赞收藏(doge.png)!! 【注意】在这一章,记录就是数据的意思。 排序可视化网站: D…...

Java-分割list并执行多线程任务的工具类
要创建一个用于分割列表并执行多线程任务的工具类,你可以使用 Java 的 ExecutorService 和 ThreadPoolExecutor 来实现。下面是一个详细的示例,展示了如何创建这样一个工具类。 步骤 1: 创建线程池 首先,创建一个线程池来执行任务。 步骤 2: 分割列表 接着,定义一个方…...

Springboot-从服务器获取一个输入流,转成视频文件存到oss
要在Spring Boot应用中从服务器获取一个输入流,然后将该流转换为视频文件并存储到阿里云 OSS中,你可以遵循以下步骤: 设置阿里云OSS客户端:首先,你需要配置阿里云OSS客户端,以便能够上传文件到OSS。 获取输入流:使用HTTP客户端(如RestTemplate或WebClient)从服务器…...

[Meachines] [Easy] Bastion SMB未授权访问+VHD虚拟硬盘挂载+注册表获取NTLM哈希+mRemoteNG远程管理工具权限提升
信息收集 IP AddressOpening Ports10.10.10.134TCP:22, 135, 139, 445, 5985, 47001, 49664, 49665, 49666, 49667, 49668, 49669, 49670 $ nmap -p- 10.10.10.134 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH fo…...

STM32标准库学习笔记-9.DMA 直接存储器存取
参考教程:【STM32入门教程-2023版 细致讲解 中文字幕】 DMA(Direct Memory Access) DMA(Direct Memory Access)直接存储器存取DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预…...

ubuntu VCS+verdi安装遇到的一些问题
主体流程:https://blog.51cto.com/u_15346322/4995147 我的是Ubuntu22.4 安装目录问题 执行 ./installer -gui 只能安装到home 或者 sudo ./setup.sh -install_as_root 能安装到/usr/ 目录 运行 vcs 出现 bin/sh: Illegal option -h sudo rm -f /bin/sh sudo…...

使用Poi-tl对word模板生成动态报告
一、pom依赖问题: <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.2</version> </dependency> 使用 poi-tl 的 1.12.2版本,如果使用了poi依赖&#x…...

day45-dynamic programming-part12-8.16
tasks for today: 1. 115.不同的子序列 2. 583.两个字符串选的删除操作 3. 72.编辑距离 4. 总结编辑两个序列关系的问题 ------------------------------------------------------------------- 1. 115.不同的子序列 In this practice, it is necessary to compare with t…...

C# String的方法
目录 #region 知识点九 字符串切割 #region 知识点一 字符串指定位置获取 #region 知识点二 字符串拼接 #region 知识点三 正向查找字符位置 #region 知识点四 反向查找指定字符串位置 #region 知识点五 移除指定位置后的字符 #region 知识点六 替换指定字符串 #region 知识点七…...

Oracle RAC vs Clusterware vs ASM
Oracle RAC vs Clusterware vs ASM Oracle RACCache FusionRAC后台进程自动负载管理DBA管理工具Oracle ClusterwareCRS组件HAS组件管理工具Oracle ASMASM实例ASM磁盘组镜像和故障组ASM磁盘ASM文件Oracle RAC RAC即Real Application Clusters,是一种Oracle高可用部署架构。Orac…...

“华为杯”第十五届中国研究生数学建模竞赛-F题:机场新增卫星厅对中转旅客影响的研究
目录 摘 要: 一、 问题重述 1.1 研究背景 1.2 已知信息 1.3 需要解决的问题 二、 模型假设 三、 符号说明 四、 问题一模型的建立与求解 4.1 问题描述与分析 4.2 模型的求解 4.3 求解结果与分析 五、 问题二模型的建立与求解 5.1 问题描述与分析 5.2 模型的求解 5.3 求解结果与…...

正点原子linux开发板 qt程序交叉编译执行
1.开发板光盘 A-基础资料->5、开发工具->1、交叉编译器->fsl-imx-x11-glibc-x86_64-meta-toolchain-qt5-cortexa7hf-neon-toolchain-4.1.15-2.1.0.sh 拷贝到 Ubuntu 虚拟机 用文件传输系统或者共享文件夹传输到linux虚拟机 用ls -l查看权限,如果是白色的使…...