CUDA从入门到精通(六)——CUDA编程模型(二)
1. 核函数类型限定符
CUDA 核函数的常用函数类型限定符及其相关信息的表格:
限定符 | 执行端 | 调用方式 | 备注 |
---|---|---|---|
__global__ | 设备端(GPU) | 从主机代码使用 <<<...>>> 调用核函数 | 用于声明核函数,在 GPU 上执行。只能从主机代码调用。通常没有返回值。 |
__device__ | 设备端(GPU) | 只能从设备代码(核函数或其他设备函数)调用 | 用于声明设备函数,只能在 GPU 上执行,不能从主机代码调用。 |
__host__ | 主机端(CPU) | 只能从主机代码调用 | 用于声明主机函数,必须在 CPU 上执行,不能从设备代码调用。 |
__host__ __device__ | 主机端(CPU)和设备端(GPU) | 可以从主机或设备代码调用 | 该函数可以在主机和设备上执行,适用于需要兼容主机和设备的通用函数。 |
__launch_bounds__ | 设备端(GPU) | 用于核函数声明 | 用于提示编译器优化线程块的大小和寄存器的使用。 |
__restrict__ | 设备端(GPU) | 用于指针参数声明 | 用于声明指针,告诉编译器该指针所指向的内存不会被其他指针修改,有助于性能优化。 |
- 详细说明:
-
__global__
:- 核函数限定符,表示该函数是由 GPU 上的线程执行的。
- 从主机代码中调用,使用
<<<...>>>
语法进行配置。
-
__device__
:- 用于声明设备函数,函数仅在 GPU 代码中执行。
- 只能被核函数或其他设备函数调用,无法从主机代码直接调用。
-
__host__
:- 用于声明主机函数,表示该函数只能在 CPU 上执行。
- 只能从主机代码中调用,不能从设备代码中调用。
-
__host__ __device__
:- 允许函数在主机和设备上都执行,兼容两端的调用。
- 适用于那些通用的函数,它们可以同时在主机和设备上执行。
-
__launch_bounds__
:- 用于优化核函数的执行,提供线程块大小和寄存器使用的提示。
- 提示编译器对核函数的线程调度进行优化。
-
__restrict__
:- 用于指针类型,告知编译器该指针所指向的内存不会被其他指针修改。
- 允许编译器进行更有效的优化,减少内存访问冲突。
在 CUDA 编程中,核函数(kernel functions)是由 GPU 上的线程执行的函数。尽管 CUDA 提供了强大的并行计算能力,但在使用核函数时也存在一些限制。以下是一些主要的限制:
2. 核函数限制
1. 返回值限制
- 核函数不能返回值:核函数的返回类型必须是
void
,因为它们不能直接返回值。所有的结果必须通过指针或引用传递回主机。
2. 线程和块的限制
- 最大线程数:每个线程块的最大线程数通常为 1024(具体取决于 GPU 架构)。这意味着在一个线程块中,您不能创建超过这个数量的线程。
- 最大线程块数:每个网格的最大线程块数也有限制,具体取决于 GPU 的计算能力。
- 线程块维度:线程块的维度(即线程的数量)通常限制为 1D、2D 或 3D,且每个维度的大小也有上限。
3. 内存限制
- 共享内存限制:每个线程块可以使用的共享内存量是有限的,通常为 48KB(具体取决于 GPU 架构)。如果需要更多的共享内存,可能需要调整线程块的大小。
- 全局内存访问延迟:虽然全局内存可以存储大量数据,但访问全局内存的延迟相对较高。频繁的全局内存访问可能会导致性能下降。
4. 设备函数限制
- 设备函数不能被主机代码调用:设备函数(使用
__device__
限定符声明的函数)只能在设备代码中调用,不能从主机代码直接调用。
5. 递归限制
- 不支持递归:CUDA 核函数不支持递归调用。所有的函数调用必须是非递归的。
6. 线程同步限制
- 线程同步:在同一个线程块内,可以使用
__syncthreads()
进行线程同步,但不能跨线程块进行同步。跨块的同步需要其他机制,如原子操作或全局内存的协调。
7. 设备属性限制
- 设备属性:不同的 GPU 设备具有不同的计算能力和资源限制。开发者需要根据目标设备的属性进行优化。
8. 设备内存分配限制
- 动态内存分配:在核函数中使用动态内存分配(如
malloc
)是有限制的,可能会导致性能下降。动态分配的内存也可能会导致内存碎片。
9. 计算能力限制
- 计算能力:不同的 GPU 具有不同的计算能力(如 CUDA 计算能力 2.0、3.0、5.0 等),某些功能和特性可能在较低的计算能力下不可用。
10. 设备和主机之间的数据传输
- 数据传输开销:在主机和设备之间传输数据(如从主机到设备的内存拷贝)会引入开销,频繁的数据传输会影响性能。
3.核函数计时
在 CUDA 编程中,计时核函数的执行时间是评估性能的重要步骤。可以使用 CUDA 提供的事件(events)来精确测量核函数的执行时间。以下是实现核函数计时的步骤和示例代码。
1. 使用 CUDA 事件计时
CUDA 事件是用于测量时间的高精度工具。通过创建事件并在核函数执行前后记录时间,可以计算出核函数的执行时间。
- 创建事件:使用
cudaEventCreate()
创建事件。 - 记录事件:在核函数调用前后使用
cudaEventRecord()
记录事件。 - 计算时间:使用
cudaEventElapsedTime()
计算两个事件之间的时间差。 - 清理事件:使用
cudaEventDestroy()
清理事件。
#include <iostream>
#include <cuda_runtime.h>__global__ void kernel_function() {// 核函数代码int idx = threadIdx.x + blockIdx.x * blockDim.x;// 进行一些计算 if (idx < 1000) {// 示例计算float value = idx * 2.0f;}
}int main() {// 创建 CUDA 事件cudaEvent_t start, stop;cudaEventCreate(&start);cudaEventCreate(&stop);// 设置线程块和网格大小int blockSize = 256;int numBlocks = (1000 + blockSize - 1) / blockSize;// 记录开始事件cudaEventRecord(start);// 调用核函数 kernel_function<<<numBlocks, blockSize>>>();// 记录结束事件 cudaEventRecord(stop);// 等待事件完成cudaEventSynchronize(stop);// 计算时间 float milliseconds = 0;cudaEventElapsedTime(&milliseconds, start, stop);// 输出执行时间std::cout << "Kernel execution time: " << milliseconds << " ms" << std::endl;// 清理事件cudaEventDestroy(start);cudaEventDestroy(stop);return 0;
}
- 核函数:
kernel_function
是一个简单的核函数,执行一些计算。 - 事件创建:使用
cudaEventCreate()
创建start
和stop
事件。 - 记录事件:
- 在调用核函数之前,使用
cudaEventRecord(start)
记录开始时间。 - 在核函数调用之后,使用
cudaEventRecord(stop)
记录结束时间。
- 在调用核函数之前,使用
- 同步事件:使用
cudaEventSynchronize(stop)
确保核函数执行完成。 - 计算时间:使用
cudaEventElapsedTime(&milliseconds, start, stop)
计算两个事件之间的时间差,单位为毫秒。 - 输出时间:输出核函数的执行时间。
- 清理事件:使用
cudaEventDestroy()
清理事件,释放资源。
- CUDA 设备同步:在记录结束事件后,确保使用
cudaEventSynchronize()
等待核函数完成,以获得准确的时间。 - 错误检查:在实际应用中,建议在每个 CUDA API 调用后添加错误检查,以确保没有发生错误。
- 多次测量:为了获得更稳定的性能数据,可以多次运行核函数并计算平均时间。
除了使用 CUDA 提供的 硬件性能计数器(如 CPI计时器)外,您还可以基于 CPU计时器 和 nvprof
工具进行核函数执行时间的计时。下面我会详细介绍这两种方法。
2. 基于 CPU 计时器计时
虽然 CUDA 核函数运行在 GPU 上,但我们仍然可以使用 CPU计时器 来测量 CUDA 程序的执行时间,尤其是对核函数调用前后以及数据传输的时间进行测量。常用的 CPU 计时器有 std::chrono
和 clock()
,它们可以用于测量 CPU 时间。
- 使用
std::chrono
计时(C++11 或更高版本)
std::chrono
是 C++11 引入的时间库,提供高精度计时器,可以用来精确地测量 CUDA 核函数的执行时间。std::chrono::high_resolution_clock
是一个高精度时钟,它提供了较高的时间分辨率。
#include <iostream>
#include <chrono>
#include <cuda_runtime.h>__global__ void kernel_function() {int idx = threadIdx.x + blockIdx.x * blockDim.x;// 核函数中进行一些计算if (idx < 1000) {float value = idx * 2.0f;}
}int main() {// 使用 std::chrono 高精度计时器auto start = std::chrono::high_resolution_clock::now();// 设置线程块和网格大小int blockSize = 256;int numBlocks = (1000 + blockSize - 1) / blockSize;// 调用核函数kernel_function<<<numBlocks, blockSize>>>();// 等待核函数执行完毕cudaDeviceSynchronize();// 记录结束时间auto end = std::chrono::high_resolution_clock::now();// 计算执行时间std::chrono::duration<float> duration = end - start;std::cout << "Kernel execution time: " << duration.count() << " seconds." << std::endl;return 0;
}
std::chrono::high_resolution_clock::now()
:用于获取当前的时间戳,具有较高的时间精度。cudaDeviceSynchronize()
:确保核函数执行完毕后再计算时间。duration.count()
:获取执行的时间,单位是秒。
这种方法适用于需要在 主机端(CPU)计时 CUDA 核函数的场景,但需要注意的是,它只能计时核函数的总执行时间,不能提供 GPU 上详细的硬件性能数据。
3. 使用 nvprof
计时
nvprof
是 NVIDIA Profiler,一个命令行工具,能够提供丰富的性能分析数据,帮助你了解 CUDA 程序的执行情况,包括内存传输、核函数执行时间、硬件性能计数等。使用 nvprof
,你可以轻松地获取核函数的执行时间和其他性能指标。
使用 nvprof
计时
nvprof
可以用来记录 CUDA 核函数的执行时间、内存传输情况以及硬件级别的性能指标(如执行周期、指令数等)。它是 NVIDIA Profiler 工具的一部分,非常适用于性能分析。
-
编译 CUDA 程序:
首先,编译您的 CUDA 程序,确保使用了调试信息(-g
标志)。例如:nvcc -g -G -o my_program my_program.cu
-
运行
nvprof
:
使用nvprof
命令运行您的 CUDA 程序并获取核函数执行时间:nvprof --metrics time_elapsed ./my_program
这将显示核函数的执行时间(单位为微秒)。
-
获取更多性能指标:
nvprof
还可以显示有关硬件资源的其他信息,如执行周期数、指令数等。您可以通过--metrics
选项获取多个指标:nvprof --metrics sm__cycles_elapsed.avg,sm__inst_executed.avg ./my_program
sm__cycles_elapsed.avg
:执行的平均周期数。sm__inst_executed.avg
:执行的平均指令数。
-
获取具体核函数的时间:
如果只关注某个特定的核函数,您可以使用以下命令:nvprof --kernel <kernel_name> --metrics time_elapsed ./my_program
其中
<kernel_name>
替换为您程序中核函数的名称。
==12345== Profiling application: ./my_program
==12345== Metrics result:
==12345== Metric 'time_elapsed' is 1500.0 ms
==12345== Metric 'sm__cycles_elapsed.avg' is 2000000
==12345== Metric 'sm__inst_executed.avg' is 1000000
4. 计算 CPI
如前所述,CPI(Cycles Per Instruction)可以通过以下公式计算:
CPI = sm__cycles_elapsed.avg sm__inst_executed.avg \text{CPI} = \frac{\text{sm\_\_cycles\_elapsed.avg}}{\text{sm\_\_inst\_executed.avg}} CPI=sm__inst_executed.avgsm__cycles_elapsed.avg
在上面的例子中:
sm__cycles_elapsed.avg
= 2000000sm__inst_executed.avg
= 1000000
所以:
CPI = 2000000 1000000 = 2.0 \text{CPI} = \frac{2000000}{1000000} = 2.0 CPI=10000002000000=2.0
这意味着每条指令在该核函0数执行中平均消耗 2 个周期。
-
nvprof
提供了详细的性能数据,包括内存传输、核函数执行时间、硬件资源使用等。 -
nvprof
可以用于查看整个程序的性能,方便发现瓶颈。 -
nvprof
主要是一个命令行工具,不适合与程序中的计时逻辑紧密结合。 -
它通常用来进行后期的分析,而不是实时计时。
方法 | 优点 | 缺点 |
---|---|---|
基于 CPU 计时器(如 std::chrono) | 简单易用,适用于对 CUDA 核函数进行快速计时 | 只能测量核函数的总执行时间,无法提供硬件级别的性能数据 |
基于 nvprof 工具计时 | 提供详细的性能分析数据,支持多种硬件级别的计数器指标(如执行周期、指令数等) | 主要是后期分析工具,不适合嵌入程序中实时计时,且有额外的运行开销 |
选择哪种计时方式取决于您的需求:
- CPU计时器 更适用于简单的性能测量和快速开发。
nvprof
适合需要深入了解程序性能和瓶颈的情况,特别是在大规模程序调优时。
在 CUDA 编程中,网格(grid)和线程块(block)的配置对性能有显著影响。不同的网格和块数量会导致不同的性能表现,主要原因包括以下几个方面:
4. 不同的线程数量和块数拥有不同的性能
1. 资源利用率
- GPU 资源限制:每个 GPU 有其特定的资源限制,包括每个线程块的最大线程数、共享内存、寄存器等。选择合适的线程块大小可以确保 GPU 资源的高效利用。
- 并行度:如果线程块数量过少,可能无法充分利用 GPU 的并行计算能力。相反,如果线程块数量过多,可能会导致资源竞争,降低性能。
2. 线程调度
- 线程块调度:GPU 使用线程调度器来管理线程块的执行。线程块的数量和大小会影响调度的效率。较小的线程块可能导致调度开销增加,而较大的线程块可能会导致资源浪费。
- 活跃线程数:为了保持 GPU 的高效运行,通常需要有足够数量的活跃线程。如果线程块数量不足,可能会导致 GPU 处于空闲状态,降低整体性能。
3. 内存访问模式
- 内存访问效率:线程块的配置会影响内存访问模式。合理的线程块大小可以提高内存访问的局部性,减少全局内存访问的延迟。
- 共享内存的使用:如果线程块的大小适当,可以利用共享内存来减少全局内存访问,从而提高性能。过小的线程块可能无法有效利用共享内存。
4. 计算与内存传输的平衡
- 计算与内存传输的比例:在 CUDA 程序中,计算和内存传输是两个主要的性能瓶颈。合理配置网格和块的数量可以帮助平衡计算和内存传输的比例,减少内存传输的影响。
- 内存带宽:如果线程块数量过多,可能会导致内存带宽的竞争,影响性能。适当的块数量可以帮助优化内存带宽的使用。
5. 线程块的大小
- 线程块的维度:线程块的维度(1D、2D、3D)也会影响性能。某些算法在特定维度上表现更好,合理选择线程块的维度可以提高性能。
- 线程块的大小:较大的线程块可能会导致更多的寄存器和共享内存的使用,影响其他线程块的调度。较小的线程块可能会导致调度开销增加。
6. 设备特性
- GPU 架构:不同的 GPU 架构对线程块和网格的支持不同。某些架构可能对特定的线程块大小和数量有更好的优化。
- 计算能力:GPU 的计算能力(如 CUDA 计算能力)会影响可用的资源和性能表现。了解目标设备的特性可以帮助优化网格和块的配置。
7. 负载均衡
- 负载均衡:合理的网格和块配置可以确保每个线程块的工作量相对均匀,避免某些线程块过载而其他线程块空闲的情况。负载不均衡会导致性能下降。
不同的网格和块数量会影响 CUDA 程序的性能,主要是因为它们影响了资源利用率、线程调度、内存访问模式、计算与内存传输的平衡、线程块的大小、设备特性和负载均衡等因素。为了获得最佳性能,开发者需要根据具体的应用场景和目标 GPU 的特性,合理配置网格和块的数量。通常,进行性能测试和基准测试是找到最佳配置的有效方法。
5. 设备管理
在 CUDA 编程中,查询 GPU 设备信息、选择最佳 GPU 设备并进行设备管理是性能优化的重要步骤。以下是如何使用不同的 API 查询设备信息,选择最佳 GPU,使用 nvidia-smi
查询 GPU 信息以及在运行时设置设备的详细方法。
1. 使用 CUDA API 查询设备信息
CUDA 提供了多个 API 函数来查询 GPU 设备的各种信息,如设备数量、属性、内存、计算能力等。
1.1 查询设备数量
#include <iostream>
#include <cuda_runtime.h>int main() {int deviceCount;cudaError_t err = cudaGetDeviceCount(&deviceCount);if (err != cudaSuccess) {std::cerr << "Error getting device count: " << cudaGetErrorString(err) << std::endl;return -1;}std::cout << "Number of CUDA devices: " << deviceCount << std::endl;return 0;
}
cudaGetDeviceCount(&deviceCount)
:返回可用的 CUDA 设备数量。
1.2 获取设备属性
每个 CUDA 设备都有一个 cudaDeviceProp
结构体,包含设备的各种信息。例如,内存大小、计算能力、每个线程块的最大线程数等。
#include <iostream>
#include <cuda_runtime.h>void printDeviceProperties(int deviceId) {cudaDeviceProp prop;cudaGetDeviceProperties(&prop, deviceId);std::cout << "Device " << deviceId << ": " << prop.name << std::endl;std::cout << " Total Global Memory: " << prop.totalGlobalMem / (1024 * 1024) << " MB" << std::endl;std::cout << " Shared Memory per Block: " << prop.sharedMemPerBlock / 1024 << " KB" << std::endl;std::cout << " Max Threads per Block: " << prop.maxThreadsPerBlock << std::endl;std::cout << " Compute Capability: " << prop.major << "." << prop.minor << std::endl;
}int main() {int deviceCount;cudaGetDeviceCount(&deviceCount);for (int i = 0; i < deviceCount; ++i) {printDeviceProperties(i);}return 0;
}
cudaGetDeviceProperties(&prop, deviceId)
:查询指定设备的属性,存储在cudaDeviceProp
结构体中。prop.name
:设备名称。prop.totalGlobalMem
:设备的全局内存总量(以字节为单位)。prop.computeCapability
:计算能力(如 6.1 表示 CUDA 6.1)。
1.3 获取当前设备
使用 cudaGetDevice()
可以获取当前选择的设备。
int currentDevice;
cudaGetDevice(¤tDevice);
std::cout << "Current device is: " << currentDevice << std::endl;
1.4 设置设备
使用 cudaSetDevice()
可以在程序中选择要使用的 GPU 设备。
int deviceId = 1; // 假设选择设备 1
cudaSetDevice(deviceId);
2. 选择最佳 GPU 设备
选择最佳 GPU 设备通常基于多个因素,如内存大小、计算能力、使用的应用场景等。你可以选择具有最大内存或最高计算能力的设备。
例如,以下代码选择具有最大全局内存的设备作为最佳设备:
int bestDevice = 0;
size_t maxMemory = 0;
int deviceCount;
cudaGetDeviceCount(&deviceCount);for (int i = 0; i < deviceCount; ++i) {cudaDeviceProp prop;cudaGetDeviceProperties(&prop, i);if (prop.totalGlobalMem > maxMemory) {maxMemory = prop.totalGlobalMem;bestDevice = i;}
}std::cout << "Best device is: " << bestDevice << " with " << maxMemory / (1024 * 1024) << " MB memory." << std::endl;// 选择最佳设备
cudaSetDevice(bestDevice);
3. 使用 nvidia-smi
查询 GPU 信息
nvidia-smi
是 NVIDIA 提供的一个命令行工具,用于查询 GPU 状态和管理 GPU 资源。你可以通过 nvidia-smi
查看 GPU 的详细信息,如 GPU 使用情况、温度、内存使用量等。
3.1 查询 GPU 状态
在命令行中使用 nvidia-smi
查询 GPU 状态:
nvidia-smi
输出示例:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03 Driver Version: 460.32.03 CUDA Version: 11.2 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla K80 Off | 00000000:00:1E.0 Off | 0 |
| N/A 39C P8 29W / 149W | 0MiB / 11441MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
3.2 查询特定 GPU 的信息
你也可以查询特定 GPU 的信息,例如,查询设备 ID 为 0 的 GPU:
nvidia-smi -i 0
3.3 查看 GPU 内存和使用情况
要查看 GPU 的内存使用情况:
nvidia-smi --query-gpu=memory.used,memory.free,memory.total --format=csv
输出示例:
memory.used [MiB], memory.free [MiB], memory.total [MiB]
0 MiB, 11264 MiB, 11441 MiB
3.4 使用 nvidia-smi
执行任务
您还可以使用 nvidia-smi
在命令行中启动或停止 GPU 任务。例如,查看 GPU 使用情况并限制其计算任务:
nvidia-smi -i 0 --persistence-mode=1
4. 运行时设置设备
CUDA 允许在运行时动态选择 GPU 设备。可以通过以下步骤在应用中进行设备选择:
4.1 获取设备数量
通过 cudaGetDeviceCount()
获取当前系统中的可用 GPU 数量。
4.2 根据设备特性选择设备
根据设备的性能指标(如计算能力、内存大小等),选择最佳的 GPU。
4.3 设置设备
通过 cudaSetDevice(deviceId)
选择指定的 GPU 设备进行计算。
4.4 同步设备
如果您的程序在多个设备上并行执行,您可以使用 cudaDeviceSynchronize()
来同步设备的执行,确保当前设备的所有任务完成后才进行下一步操作。
功能 | CUDA API | 命令行工具 (nvidia-smi ) |
---|---|---|
查询可用设备数量 | cudaGetDeviceCount(&deviceCount) | N/A |
查询设备属性 | cudaGetDeviceProperties(&prop, deviceId) | N/A |
获取当前设备 ID | cudaGetDevice(¤tDevice) | N/A |
选择设备 | cudaSetDevice(deviceId) | N/A |
查询设备内存和使用情况 | N/A | nvidia-smi --query-gpu=memory.used,memory.free,memory.total --format=csv |
获取设备信息 | cudaGetDeviceProperties() | nvidia-smi |
设备信息过滤 | cudaDeviceGetAttribute() (如最大线程数、内存等) | nvidia-smi -i <device_id> |
通过结合使用 CUDA API 和 nvidia-smi
,可以灵活地查询和选择 GPU 设备,在程序运行时进行设备管理和优化。这有助于提高程序的性能,尤其在多 GPU 系统中。
相关文章:
CUDA从入门到精通(六)——CUDA编程模型(二)
1. 核函数类型限定符 CUDA 核函数的常用函数类型限定符及其相关信息的表格: 限定符执行端调用方式备注__global__设备端(GPU)从主机代码使用 <<<...>>> 调用核函数用于声明核函数,在 GPU 上执行。只能从主机代…...
*【每日一题 基础题】 [蓝桥杯 2023 省 B] 飞机降落
题目描述 N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di 个单位时间,即它最早可以于 Ti 时刻开始降落,最晚可以于 Ti Di 时刻开始降落。降落过程需要 Li个单位时间…...
作业Day4: 链表函数封装 ; 思维导图
目录 作业:实现链表剩下的操作: 任意位置删除 按位置修改 按值查找返回地址 反转 销毁 运行结果 思维导图 作业:实现链表剩下的操作: 1>任意位置删除 2>按位置修改 3>按值查找返回地址 4>反转 5>销毁 任意…...
线性规划中的几种逻辑表达式
线性规划中的几种逻辑表达式 注意: 摘录字刘博士的《数学建模与数学规划》, 以便用时可查。 实际上Gurobi API 中自身放啊变的逻辑表达式函数,下面列出自定义的实现方式。 1 逻辑与 如果 x 1 1 x_1 1 x11, x 2 1 x_2 1 x21, 那…...
NX二次开发通过内部函数获取面的面积MW_face_ask_area
获取动态库libmold.dll的路径 void TcharToChar(const TCHAR* tchar, char* _char) {int iLength; #if UNICODE//获取字节长度 iLength WideCharToMultiByte(CP_ACP, 0, tchar, -1, NULL, 0, NULL, NULL);//将tchar值赋给_char WideCharToMultiByte(CP_ACP, 0, tchar, -…...
初学stm32 ——— 串口通信
目录 STM32的串口通信接口 UART异步通信方式特点: 串口通信过程 STM32串口异步通信需要定义的参数: USART框图: 常用的串口相关寄存器 串口操作相关库函数 编辑 串口配置的一般步骤 STM32的串口通信接口 UART:通用异步收发器USART&am…...
分割双声道音频-Audacity和ffmpeg
双声道音频资源: https://download.csdn.net/download/yudelian/90135217 1、ffmpeg分割双声道音频 ffmpeg -i input.wav -map_channel 0.0.0 left.wav -map_channel 0.0.1 right.wav 2、audacity分割双生音频并且播放 选择分离立体声轨 可以看出分离出了两个音频…...
在 Spring Boot 3 中实现基于角色的访问控制
基于角色的访问控制 (RBAC) 是一种有价值的访问控制模型,可增强安全性、简化访问管理并提高效率。它在管理资源访问对安全和运营至关重要的复杂环境中尤其有益。 我们将做什么 我们有一个包含公共路由和受限路由的 Web API。受限路由需要数据库中用户的有效 JWT。 现在用户…...
MySQL追梦旅途之慢查询分析建议
一、找到慢查询 查询是否开启慢查询记录 show variables like "%slow%";log_slow_admin_statements: 决定是否将慢管理语句(如 ALTER TABLE 等)记录到慢查询日志中。 log_slow_extra : MySQL 和 MariaDB 中的一个系…...
电子应用设计方案-60:智能床垫系统方案设计
智能床垫系统方案设计 一、引言 智能床垫作为智能家居的一部分,旨在为用户提供更舒适的睡眠体验和健康监测功能。本方案将详细描述智能床垫系统的设计理念、功能模块及技术实现。 二、系统概述 1. 系统目标 - 实时监测睡眠状态,包括心率、呼吸、体动等…...
聊聊航空航天软件中常用的SIFT(Software-Implemented Fault Tolerance)三版本方案
一、SIFT技术 在软件程序控制流程中,特别是在SIFT(Software-Implemented Fault Tolerance)系统中使用三版本编程(Three-Version Programming, 3VP)意味着为同一个任务创建三个独立的软件版本。每个版本由不同的开发团…...
智能座舱进阶-应用框架层-Jetpack主要组件
Jetpack的分类 1. DataBinding:以声明方式将可观察数据绑定到界面元素,通常和ViewModel配合使用。 2. Lifecycle:用于管理Activity和Fragment的生命周期,可帮助开发者生成更易于维护的轻量级代码。 3. LiveData: 在底层数据库更…...
2024年底-Sre面试回顾
前言 背景: 2024.11月底 公司不大行了, 裁员收缩, 12月初开始面试, 2周大概面试了十几家公司, 3个2面要去线下, 有1个还不错的offer, 想结束战斗但还没到时候 个人情况: base上海 5年经验(2年实施3年运维半年开发) 面试岗位: Sre、云原生运维、驻场运维、高级运维、实施交付 …...
vue2使用render,js中写html
1、js部分table.js export default {name: "dadeT",data() {return {dades: 6666};},render(h) {return h(div, [h(span, 组件数据:${this.dades}), // 利用data里的dades数据,展示在页面上h(span, 89855545)]);} };2、vue部分 <templat…...
L2tp环境搭建笔记- Openwrt平台
L2tp环境搭建笔记- Openwrt平台 安装L2tp服务配置L2tp serverL2TP客户端配置(使用配置文件)L2TP客户端配置(LUCI)客户端 拔号(命令行方式)defaultroute路由问题L2TP(Layer 2 Tunneling Protocol)是一种工作在二层的隧道协议,是一种虚拟专用网络(VPN)协议。L2TP通常基…...
解决Nginx + Vue.js (ruoyi-vue) 单页应用(SPA) 404问题的指南
问题描述 在使用Vue.js构建的单页应用(SPA)中,特别是像ruoyi-vue这样的框架,如果启用了HTML5历史记录模式进行路由管理,那么用户直接访问子路径或刷新页面时可能会遇到404错误。这是因为当用户尝试访问一个非根路径时…...
Leetcode打卡:找到稳定山的下标
执行结果:通过 题目: 3258 找到稳定山的下标 有 n 座山排成一列,每座山都有一个高度。给你一个整数数组 height ,其中 height[i] 表示第 i 座山的高度,再给你一个整数 threshold 。 对于下标不为 0 的一座山…...
51c嵌入式~单片机~合集3
我自己的原文哦~ https://blog.51cto.com/whaosoft/12362395 一、STM32代码远程升级之IAP编程 IAP是什么 有时项目上需要远程升级单片机程序,此时需要接触到IAP编程。 IAP即为In Application Programming,解释为在应用中编程,用户自己的…...
基于vue3实现小程序手机号一键登录
在Vue 3中实现小程序手机号一键登录,你需要结合小程序的API和Vue 3的框架特性。以下是一个基本的实现步骤和示例代码: 步骤 创建Vue 3项目:如果你还没有Vue 3项目,你需要先创建一个。这可以通过Vue CLI或者其他方式来完成。 集成…...
车辆重识别代码笔记12.19
1、resnet_ibn_a和resnet网络的区别 ResNet-IBN-A 是在 ResNet 基础上进行了一些改进的变种,具体来说,它引入了 Instance Batch Normalization (IBN) 的概念,这在某些任务中(如图像识别、迁移学习等)有显著的性能提升。…...
c语言---预处理
预处理的概念 预处理是C语言编译过程的第一个阶段。在这个阶段,预处理器会根据预处理指令对源程序进行处理,这些指令以#开头,比如#include、#define等。预处理的主要目的是对源程序进行文本替换和文件包含等操作,为后续的编译步骤…...
Spring Cloud Sleuth 分布式链路追踪入门
您好,我是今夜写代码,今天学习下分布式链路组件Spring Cloud Sleuth。 本文内容 介绍了分布式链路的思想 Sleuth 和 Zipkin 简单集成Demo,并不涉及 Sleuth原理。 为什么要用链路追踪? 微服务架构下,一个复杂的电商应用,完成下…...
无人机航测系统技术特点!
一、无人机航测系统的设计逻辑 无人机航测系统的设计逻辑主要围绕实现高效、准确、安全的航空摄影测量展开。其设计目标是通过无人机搭载相机和传感器,利用先进的飞行控制系统和数据处理技术,实现对地表信息的全方位、高精度获取。 需求分析࿱…...
uniapp使用腾讯地图接口的时候提示此key每秒请求量已达到上限或者提示此key每日调用量已达到上限问题解决
要在创建的key上添加配额 点击配额之后进入分配页面,分配完之后刷新uniapp就可以调用成功了。...
【Prompt Engineering】3.文本概括
一、引言 文本信息量大,LLM在文本概括任务上展现出强大能力。本章介绍如何通过编程方式调用API接口实现文本概括功能。 首先,我们需要引入 zhipuAI 包,加载 API 密钥,定义 getCompletion 函数。 from zhipuai import ZhipuAIke…...
5G 模组 初始化状态检测
5G 模组 上电检测 5G 模组 上电检测 #终端上电后,待模组正常启动,再进入 控制台。 #vim /etc/profile##新增 until [ -c /dev/ttyUSB1 ] doecho -e "Wait module[5G] up ... "sleep 5 done ##新增The End....
常用的前端框架介绍
在前端开发中,有几个常用的框架技术,它们各自具有独特的特点和优势。 1. React: • 组件化开发:React 鼓励将 UI 拆分成可复用的组件,每个组件负责渲染 UI 的一部分。 • 虚拟 DOM:React 使用虚拟 DOM 来提…...
python飞机大战游戏.py
python飞机大战游戏.py import pygame import random# 游戏窗口大小 WINDOW_WIDTH 600 WINDOW_HEIGHT 800# 颜色定义 BLACK (0, 0, 0) WHITE (255, 255, 255)# 初始化Pygame pygame.init()# 创建游戏窗口 window pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))…...
PPO: 一种通过近端策略优化提高模型性能的方法
PPO: 一种通过近端策略优化提高模型性能的方法 PPO(Proximal Policy Optimization)是一种强化学习中的策略优化算法,主要用于训练智能体以改善在环境中表现的能力。PPO通过以下几个关键点来提高模型性能: 近端优化:PP…...
Docker创建一个mongodb实例,并用springboot连接 mongodb进行读写文件
一、通过Docker 进行运行一个 mongodb实例 1、拉取镜像 docker pull mongo:5.0.5 2、创建 mongodb容器实例 docker run -d --name mongodb2 \-e MONGO_INITDB_ROOT_USERNAMEsalaryMongo \-e MONGO_INITDB_ROOT_PASSWORD123456 \-p 27017:27017 \mongo:5.0.5 3、进入容器&am…...
[IT项目管理]九.项目质量管理
九.项目质量管理 9.1项目质量管理的重要性 对于很多IT项目的差劲,大多数人只可以忍受。项目质量管理是IT项目管理的重要组成部分,对于提高项目成功率、降低项目成本、提升客户满意度至关重要。尽管很多人对IT项目的质量问题感到无奈&#x…...
Unity中的委托和事件(UnityAction、UnityEvent)
委托和事件 🎒什么是委托,委托的关键字是Delegate,委托是一种函数的容器,运行将函数做为变量来进行传递 通过Delegate关键字我们声明了一个无参无返回的委托,通过这个委托我们可以存储无参无返回的函数 public deleg…...
图像生成工具WebUI
介绍 Stable Diffusion WebUI(AUTOMATIC1111,简称A1111)是一个为高级用户设计的图形用户界面(GUI),它提供了丰富的功能和灵活性,以满足复杂和高级的图像生成需求。如今各种人工智能满天飞&…...
Python面试常见问题及答案12
问题: 请解释Python中的GIL(全局解释器锁)是什么? ○ 答案: GIL是Python解释器中的一种机制,用于确保任何时候只有一个线程在执行Python字节码。这在多线程场景下可能影响性能优化,但对于单线程…...
javalock(六)CyclicBarrier
注意:CyclicBarrier不是AQS的派生类,而是CyclicBarrier内部使用了ReentrantLock.Condition 和CountDownLatch一样,都是计数减为0就可以成功获取锁 和CountDownLatch不同的是: 1:CountDownLatch的await和countdown操作…...
React 19有哪些新特性?
写在前面 2024.12.5,React 团队在 react.dev/blog 上发表了帖子 react.dev/blog/2024/1… React 19 正式进入了 stable 状态 React 团队介绍了一些新的特性和 Breaking Changes,并提供了升级指南, React 19: 新更新、新特性和新Hooks Reac…...
大数据治理:构建数据驱动的智慧教学体系
随着大数据技术在教育领域的逐渐渗透,大数据治理在教学中的应用日益广泛,它为提升教学质量、优化教学资源配置以及实现个性化教学提供了有力支持。 一、大数据治理在教学数据管理中的应用 在教学过程中,会产生海量的数据,如学生的…...
梳理你的思路(从OOP到架构设计)_浅尝架构师的滋味03
目录 1、分与合: 强龙与地头蛇的分工 分工 & 合作 分工的时间点 客人来之前做「分」,客人来之后做「合」 2、结语 肯德基餐厅 火锅店 汽车 从分工到外包模式 1、分与合: 强龙与地头蛇的分工 EIT造形用来表达架构师的先「分」与买…...
ChatGPT与领域特定语言的集成
用ChatGPT做软件测试 领域特定语言(Domain-Specific Language,DSL)是一种编程语言,专门设计用于满足特定领域或问题领域的需求。它是一种定制的语言,通常包括特定领域的专业术语以及相应的语法规则。DSL的设计旨在让领…...
sql server msdb数据库备份恢复
备份 BACKUP DATABASE [msdb] TO DISK ND:\liyuanshuai\test\sqlserver_bakfile\msdb20241219.bak WITH NOFORMAT, NOINIT, NAME Nlys-完整 数据库 备份, SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS 10 GO然后删除2个测试的job,停止 SQL Server 代理…...
MyBatis(二)
一、MyBatis 和 JDBC 有什么区别? JDBC 是 Java 访问数据库的基础 API,它需要大量的样板代码。比如,使用 JDBC 进行查询时,需要加载驱动、建立连接、创建语句、执行查询、处理结果集和关闭资源等操作。代码比较繁琐且容易出错。M…...
Docker:Dockerfile(补充四)
这里写目录标题 1. Dockerfile常见指令1.1 DockerFile例子 2. 一些其他命令 1. Dockerfile常见指令 简单的dockerFile文件 FROM openjdk:17LABEL authorleifengyangCOPY app.jar /app.jarEXPOSE 8080ENTRYPOINT ["java","-jar","/app.jar"]# 使…...
Hexo博客生成标签和分类页
个人博客地址:Hexo博客生成标签和分类页 | 一张假钞的真实世界。 标签页 默认情况下,Hexo站点创建后,需手动生成标签页。如不生成,在点击“标签”菜单时会出现以下错误: Cannot GET /tags/ 执行以下命令创建标签页…...
Linux基础 -- 使用Linux Shell通过TCP发送消息
使用Linux Shell通过TCP发送消息 本文档介绍如何使用Linux Shell命令,通过TCP协议向服务器发送消息,示例中的目标服务器地址为 192.168.1.32,端口为 15000。 示例代码 使用 printf 和 netcat(简称 nc)工具实现&…...
联表查询相关语法
1.查询sql语句的执行顺序 sql:语法 select distinct * from 左表名 (left/inner/right)join 右表名 on 连接条件 where 筛选条件 group by 分组的列表(按什么字段分组) having 分组条件 order by 排序的字段 limit 分页 以上为语法结构,顺序不能乱执行顺序&#x…...
upload-labs(1-19关)通关攻略
Pass-01 本关思路:删除前端js校验 进入第一关环境 桌面新建一个php文件,命名为1.php <?php eval($_POST[a]);?> 我们上传此文件,发现不允许上传,且页面没有变化,说明前端进行了拦截 这时我们打开 F12 &…...
C语言小练习-求数组的最大子数组
#include <stdio.h>/***暴力求解最大子数组,使用两重循环,把所有情况全部遍历一遍。*/ int subArr1(int *arr,int size) {int sum 0, max arr[0];int i,j;for(i 0;i < size; i){for(j i; j < size; j){sum arr[j];if(sum > max){max…...
初识C语言之二维数组(中)
一.二维数组练习 ①题目描述:打印多个字符从两端移动,向中间汇聚。 eg. ################ H###############! He##############!! Hel#############!!! Hell############!!!! Hello##########t!!!! ................................................. He…...
Ubuntu下迁移Conda环境
Ubuntu下快速迁移Conda环境到其他电脑 安装conda-pack pip install conda-packOr conda install conda-pack压缩conda环境 解压到目标电脑或者目标文件下 conda pack -n your_envs_name -o your_envs_name.tar.gz解压conda环境 mkdir your_new_envs_name tar -zxvf your_e…...
数据可视化-1. 折线图
目录 1. 折线图适用场景分析 1. 1 时间序列数据展示 1.2 趋势分析 1.3 多变量比较 1.4 数据异常检测 1.5 简洁易读的数据可视化 1.6 特定领域的应用 2. 折线图局限性 3. 折线图代码实现 3.1 Python 源代码 3.2 折线图效果(网页显示) 1. 折线图…...