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

CUDA从入门到精通(六)——CUDA编程模型(二)

1. 核函数类型限定符

CUDA 核函数的常用函数类型限定符及其相关信息的表格:

限定符执行端调用方式备注
__global__设备端(GPU)从主机代码使用 <<<...>>> 调用核函数用于声明核函数,在 GPU 上执行。只能从主机代码调用。通常没有返回值。
__device__设备端(GPU)只能从设备代码(核函数或其他设备函数)调用用于声明设备函数,只能在 GPU 上执行,不能从主机代码调用。
__host__主机端(CPU)只能从主机代码调用用于声明主机函数,必须在 CPU 上执行,不能从设备代码调用。
__host__ __device__主机端(CPU)和设备端(GPU)可以从主机或设备代码调用该函数可以在主机和设备上执行,适用于需要兼容主机和设备的通用函数。
__launch_bounds__设备端(GPU)用于核函数声明用于提示编译器优化线程块的大小和寄存器的使用。
__restrict__设备端(GPU)用于指针参数声明用于声明指针,告诉编译器该指针所指向的内存不会被其他指针修改,有助于性能优化。
  • 详细说明:
  1. __global__

    • 核函数限定符,表示该函数是由 GPU 上的线程执行的。
    • 从主机代码中调用,使用 <<<...>>> 语法进行配置。
  2. __device__

    • 用于声明设备函数,函数仅在 GPU 代码中执行。
    • 只能被核函数或其他设备函数调用,无法从主机代码直接调用。
  3. __host__

    • 用于声明主机函数,表示该函数只能在 CPU 上执行。
    • 只能从主机代码中调用,不能从设备代码中调用。
  4. __host__ __device__

    • 允许函数在主机和设备上都执行,兼容两端的调用。
    • 适用于那些通用的函数,它们可以同时在主机和设备上执行。
  5. __launch_bounds__

    • 用于优化核函数的执行,提供线程块大小和寄存器使用的提示。
    • 提示编译器对核函数的线程调度进行优化。
  6. __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 事件是用于测量时间的高精度工具。通过创建事件并在核函数执行前后记录时间,可以计算出核函数的执行时间。

  1. 创建事件:使用 cudaEventCreate() 创建事件。
  2. 记录事件:在核函数调用前后使用 cudaEventRecord() 记录事件。
  3. 计算时间:使用 cudaEventElapsedTime() 计算两个事件之间的时间差。
  4. 清理事件:使用 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;
}
  1. 核函数kernel_function 是一个简单的核函数,执行一些计算。
  2. 事件创建:使用 cudaEventCreate() 创建 startstop 事件。
  3. 记录事件
    • 在调用核函数之前,使用 cudaEventRecord(start) 记录开始时间。
    • 在核函数调用之后,使用 cudaEventRecord(stop) 记录结束时间。
  4. 同步事件:使用 cudaEventSynchronize(stop) 确保核函数执行完成。
  5. 计算时间:使用 cudaEventElapsedTime(&milliseconds, start, stop) 计算两个事件之间的时间差,单位为毫秒。
  6. 输出时间:输出核函数的执行时间。
  7. 清理事件:使用 cudaEventDestroy() 清理事件,释放资源。
  • CUDA 设备同步:在记录结束事件后,确保使用 cudaEventSynchronize() 等待核函数完成,以获得准确的时间。
  • 错误检查:在实际应用中,建议在每个 CUDA API 调用后添加错误检查,以确保没有发生错误。
  • 多次测量:为了获得更稳定的性能数据,可以多次运行核函数并计算平均时间。

除了使用 CUDA 提供的 硬件性能计数器(如 CPI计时器)外,您还可以基于 CPU计时器nvprof 工具进行核函数执行时间的计时。下面我会详细介绍这两种方法。

2. 基于 CPU 计时器计时

虽然 CUDA 核函数运行在 GPU 上,但我们仍然可以使用 CPU计时器 来测量 CUDA 程序的执行时间,尤其是对核函数调用前后以及数据传输的时间进行测量。常用的 CPU 计时器有 std::chronoclock(),它们可以用于测量 CPU 时间。

  1. 使用 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 计时

nvprofNVIDIA Profiler,一个命令行工具,能够提供丰富的性能分析数据,帮助你了解 CUDA 程序的执行情况,包括内存传输、核函数执行时间、硬件性能计数等。使用 nvprof,你可以轻松地获取核函数的执行时间和其他性能指标。

使用 nvprof 计时

nvprof 可以用来记录 CUDA 核函数的执行时间、内存传输情况以及硬件级别的性能指标(如执行周期、指令数等)。它是 NVIDIA Profiler 工具的一部分,非常适用于性能分析。

  1. 编译 CUDA 程序
    首先,编译您的 CUDA 程序,确保使用了调试信息(-g 标志)。例如:

    nvcc -g -G -o my_program my_program.cu
    
  2. 运行 nvprof
    使用 nvprof 命令运行您的 CUDA 程序并获取核函数执行时间:

    nvprof --metrics time_elapsed ./my_program
    

    这将显示核函数的执行时间(单位为微秒)。

  3. 获取更多性能指标
    nvprof 还可以显示有关硬件资源的其他信息,如执行周期数、指令数等。您可以通过 --metrics 选项获取多个指标:

    nvprof --metrics sm__cycles_elapsed.avg,sm__inst_executed.avg ./my_program
    
    • sm__cycles_elapsed.avg:执行的平均周期数。
    • sm__inst_executed.avg:执行的平均指令数。
  4. 获取具体核函数的时间
    如果只关注某个特定的核函数,您可以使用以下命令:

    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 = 2000000
  • sm__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(&currentDevice);
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
获取当前设备 IDcudaGetDevice(&currentDevice)N/A
选择设备cudaSetDevice(deviceId)N/A
查询设备内存和使用情况N/Anvidia-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 核函数的常用函数类型限定符及其相关信息的表格&#xff1a; 限定符执行端调用方式备注__global__设备端&#xff08;GPU&#xff09;从主机代码使用 <<<...>>> 调用核函数用于声明核函数&#xff0c;在 GPU 上执行。只能从主机代…...

*【每日一题 基础题】 [蓝桥杯 2023 省 B] 飞机降落

题目描述 N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空&#xff0c;到达时它的剩余油料还可以继续盘旋 Di 个单位时间&#xff0c;即它最早可以于 Ti 时刻开始降落&#xff0c;最晚可以于 Ti Di 时刻开始降落。降落过程需要 Li个单位时间…...

作业Day4: 链表函数封装 ; 思维导图

目录 作业&#xff1a;实现链表剩下的操作&#xff1a; 任意位置删除 按位置修改 按值查找返回地址 反转 销毁 运行结果 思维导图 作业&#xff1a;实现链表剩下的操作&#xff1a; 1>任意位置删除 2>按位置修改 3>按值查找返回地址 4>反转 5>销毁 任意…...

线性规划中的几种逻辑表达式

线性规划中的几种逻辑表达式 注意&#xff1a; 摘录字刘博士的《数学建模与数学规划》&#xff0c; 以便用时可查。 实际上Gurobi API 中自身放啊变的逻辑表达式函数&#xff0c;下面列出自定义的实现方式。 1 逻辑与 如果 x 1 1 x_1 1 x1​1, x 2 1 x_2 1 x2​1, 那…...

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异步通信方式特点&#xff1a; 串口通信过程 STM32串口异步通信需要定义的参数: USART框图&#xff1a; 常用的串口相关寄存器 串口操作相关库函数 ​编辑 串口配置的一般步骤 STM32的串口通信接口 UART&#xff1a;通用异步收发器USART&am…...

分割双声道音频-Audacity和ffmpeg

双声道音频资源&#xff1a; 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&#xff1a; 决定是否将慢管理语句&#xff08;如 ALTER TABLE 等&#xff09;记录到慢查询日志中。 log_slow_extra &#xff1a; MySQL 和 MariaDB 中的一个系…...

电子应用设计方案-60:智能床垫系统方案设计

智能床垫系统方案设计 一、引言 智能床垫作为智能家居的一部分&#xff0c;旨在为用户提供更舒适的睡眠体验和健康监测功能。本方案将详细描述智能床垫系统的设计理念、功能模块及技术实现。 二、系统概述 1. 系统目标 - 实时监测睡眠状态&#xff0c;包括心率、呼吸、体动等…...

聊聊航空航天软件中常用的SIFT(Software-Implemented Fault Tolerance)三版本方案

一、SIFT技术 在软件程序控制流程中&#xff0c;特别是在SIFT&#xff08;Software-Implemented Fault Tolerance&#xff09;系统中使用三版本编程&#xff08;Three-Version Programming, 3VP&#xff09;意味着为同一个任务创建三个独立的软件版本。每个版本由不同的开发团…...

智能座舱进阶-应用框架层-Jetpack主要组件

Jetpack的分类 1. DataBinding&#xff1a;以声明方式将可观察数据绑定到界面元素&#xff0c;通常和ViewModel配合使用。 2. Lifecycle&#xff1a;用于管理Activity和Fragment的生命周期&#xff0c;可帮助开发者生成更易于维护的轻量级代码。 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, 组件数据&#xff1a;${this.dades}), // 利用data里的dades数据&#xff0c;展示在页面上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构建的单页应用&#xff08;SPA&#xff09;中&#xff0c;特别是像ruoyi-vue这样的框架&#xff0c;如果启用了HTML5历史记录模式进行路由管理&#xff0c;那么用户直接访问子路径或刷新页面时可能会遇到404错误。这是因为当用户尝试访问一个非根路径时…...

Leetcode打卡:找到稳定山的下标

执行结果&#xff1a;通过 题目&#xff1a; 3258 找到稳定山的下标 有 n 座山排成一列&#xff0c;每座山都有一个高度。给你一个整数数组 height &#xff0c;其中 height[i] 表示第 i 座山的高度&#xff0c;再给你一个整数 threshold 。 对于下标不为 0 的一座山&#xf…...

51c嵌入式~单片机~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/12362395 一、STM32代码远程升级之IAP编程 IAP是什么 有时项目上需要远程升级单片机程序&#xff0c;此时需要接触到IAP编程。 IAP即为In Application Programming&#xff0c;解释为在应用中编程&#xff0c;用户自己的…...

基于vue3实现小程序手机号一键登录

在Vue 3中实现小程序手机号一键登录&#xff0c;你需要结合小程序的API和Vue 3的框架特性。以下是一个基本的实现步骤和示例代码&#xff1a; 步骤 创建Vue 3项目&#xff1a;如果你还没有Vue 3项目&#xff0c;你需要先创建一个。这可以通过Vue CLI或者其他方式来完成。 集成…...

车辆重识别代码笔记12.19

1、resnet_ibn_a和resnet网络的区别 ResNet-IBN-A 是在 ResNet 基础上进行了一些改进的变种&#xff0c;具体来说&#xff0c;它引入了 Instance Batch Normalization (IBN) 的概念&#xff0c;这在某些任务中&#xff08;如图像识别、迁移学习等&#xff09;有显著的性能提升。…...

c语言---预处理

预处理的概念 预处理是C语言编译过程的第一个阶段。在这个阶段&#xff0c;预处理器会根据预处理指令对源程序进行处理&#xff0c;这些指令以#开头&#xff0c;比如#include、#define等。预处理的主要目的是对源程序进行文本替换和文件包含等操作&#xff0c;为后续的编译步骤…...

Spring Cloud Sleuth 分布式链路追踪入门

您好&#xff0c;我是今夜写代码,今天学习下分布式链路组件Spring Cloud Sleuth。 本文内容 介绍了分布式链路的思想 Sleuth 和 Zipkin 简单集成Demo,并不涉及 Sleuth原理。 为什么要用链路追踪&#xff1f; 微服务架构下&#xff0c;一个复杂的电商应用&#xff0c;完成下…...

无人机航测系统技术特点!

一、无人机航测系统的设计逻辑 无人机航测系统的设计逻辑主要围绕实现高效、准确、安全的航空摄影测量展开。其设计目标是通过无人机搭载相机和传感器&#xff0c;利用先进的飞行控制系统和数据处理技术&#xff0c;实现对地表信息的全方位、高精度获取。 需求分析&#xff1…...

uniapp使用腾讯地图接口的时候提示此key每秒请求量已达到上限或者提示此key每日调用量已达到上限问题解决

要在创建的key上添加配额 点击配额之后进入分配页面&#xff0c;分配完之后刷新uniapp就可以调用成功了。...

【Prompt Engineering】3.文本概括

一、引言 文本信息量大&#xff0c;LLM在文本概括任务上展现出强大能力。本章介绍如何通过编程方式调用API接口实现文本概括功能。 首先&#xff0c;我们需要引入 zhipuAI 包&#xff0c;加载 API 密钥&#xff0c;定义 getCompletion 函数。 from zhipuai import ZhipuAIke…...

5G 模组 初始化状态检测

5G 模组 上电检测 5G 模组 上电检测 #终端上电后&#xff0c;待模组正常启动&#xff0c;再进入 控制台。 #vim /etc/profile##新增 until [ -c /dev/ttyUSB1 ] doecho -e "Wait module[5G] up ... "sleep 5 done ##新增The End....

常用的前端框架介绍

在前端开发中&#xff0c;有几个常用的框架技术&#xff0c;它们各自具有独特的特点和优势。 1. React&#xff1a; • 组件化开发&#xff1a;React 鼓励将 UI 拆分成可复用的组件&#xff0c;每个组件负责渲染 UI 的一部分。 • 虚拟 DOM&#xff1a;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&#xff08;Proximal Policy Optimization&#xff09;是一种强化学习中的策略优化算法&#xff0c;主要用于训练智能体以改善在环境中表现的能力。PPO通过以下几个关键点来提高模型性能&#xff1a; 近端优化&#xff1a;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项目管理]九.项目质量管理

九&#xff0e;项目质量管理 9.1项目质量管理的重要性 对于很多IT项目的差劲&#xff0c;大多数人只可以忍受。项目质量管理是IT项目管理的重要组成部分&#xff0c;对于提高项目成功率、降低项目成本、提升客户满意度至关重要。尽管很多人对IT项目的质量问题感到无奈&#x…...

Unity中的委托和事件(UnityAction、UnityEvent)

委托和事件 &#x1f392;什么是委托&#xff0c;委托的关键字是Delegate&#xff0c;委托是一种函数的容器&#xff0c;运行将函数做为变量来进行传递 通过Delegate关键字我们声明了一个无参无返回的委托&#xff0c;通过这个委托我们可以存储无参无返回的函数 public deleg…...

图像生成工具WebUI

介绍 Stable Diffusion WebUI&#xff08;AUTOMATIC1111&#xff0c;简称A1111&#xff09;是一个为高级用户设计的图形用户界面&#xff08;GUI&#xff09;&#xff0c;它提供了丰富的功能和灵活性&#xff0c;以满足复杂和高级的图像生成需求。如今各种人工智能满天飞&…...

Python面试常见问题及答案12

问题&#xff1a; 请解释Python中的GIL&#xff08;全局解释器锁&#xff09;是什么&#xff1f; ○ 答案&#xff1a; GIL是Python解释器中的一种机制&#xff0c;用于确保任何时候只有一个线程在执行Python字节码。这在多线程场景下可能影响性能优化&#xff0c;但对于单线程…...

javalock(六)CyclicBarrier

注意&#xff1a;CyclicBarrier不是AQS的派生类&#xff0c;而是CyclicBarrier内部使用了ReentrantLock.Condition 和CountDownLatch一样&#xff0c;都是计数减为0就可以成功获取锁 和CountDownLatch不同的是&#xff1a; 1&#xff1a;CountDownLatch的await和countdown操作…...

React 19有哪些新特性?

写在前面 2024.12.5&#xff0c;React 团队在 react.dev/blog 上发表了帖子 react.dev/blog/2024/1… React 19 正式进入了 stable 状态 React 团队介绍了一些新的特性和 Breaking Changes&#xff0c;并提供了升级指南&#xff0c; React 19: 新更新、新特性和新Hooks Reac…...

大数据治理:构建数据驱动的智慧教学体系

随着大数据技术在教育领域的逐渐渗透&#xff0c;大数据治理在教学中的应用日益广泛&#xff0c;它为提升教学质量、优化教学资源配置以及实现个性化教学提供了有力支持。 一、大数据治理在教学数据管理中的应用 在教学过程中&#xff0c;会产生海量的数据&#xff0c;如学生的…...

梳理你的思路(从OOP到架构设计)_浅尝架构师的滋味03

目录 1、分与合&#xff1a; 强龙与地头蛇的分工 分工 & 合作 分工的时间点 客人来之前做「分」&#xff0c;客人来之后做「合」 2、结语 肯德基餐厅 火锅店 汽车 从分工到外包模式 1、分与合&#xff1a; 强龙与地头蛇的分工 EIT造形用来表达架构师的先「分」与买…...

ChatGPT与领域特定语言的集成

用ChatGPT做软件测试 领域特定语言&#xff08;Domain-Specific Language&#xff0c;DSL&#xff09;是一种编程语言&#xff0c;专门设计用于满足特定领域或问题领域的需求。它是一种定制的语言&#xff0c;通常包括特定领域的专业术语以及相应的语法规则。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&#xff0c;停止 SQL Server 代理…...

MyBatis(二)

一、MyBatis 和 JDBC 有什么区别&#xff1f; JDBC 是 Java 访问数据库的基础 API&#xff0c;它需要大量的样板代码。比如&#xff0c;使用 JDBC 进行查询时&#xff0c;需要加载驱动、建立连接、创建语句、执行查询、处理结果集和关闭资源等操作。代码比较繁琐且容易出错。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博客生成标签和分类页

个人博客地址&#xff1a;Hexo博客生成标签和分类页 | 一张假钞的真实世界。 标签页 默认情况下&#xff0c;Hexo站点创建后&#xff0c;需手动生成标签页。如不生成&#xff0c;在点击“标签”菜单时会出现以下错误&#xff1a; Cannot GET /tags/ 执行以下命令创建标签页…...

Linux基础 -- 使用Linux Shell通过TCP发送消息

使用Linux Shell通过TCP发送消息 本文档介绍如何使用Linux Shell命令&#xff0c;通过TCP协议向服务器发送消息&#xff0c;示例中的目标服务器地址为 192.168.1.32&#xff0c;端口为 15000。 示例代码 使用 printf 和 netcat&#xff08;简称 nc&#xff09;工具实现&…...

联表查询相关语法

1.查询sql语句的执行顺序 sql:语法 select distinct * from 左表名 (left/inner/right)join 右表名 on 连接条件 where 筛选条件 group by 分组的列表(按什么字段分组) having 分组条件 order by 排序的字段 limit 分页 以上为语法结构&#xff0c;顺序不能乱执行顺序&#x…...

upload-labs(1-19关)通关攻略

Pass-01 本关思路&#xff1a;删除前端js校验 进入第一关环境 桌面新建一个php文件&#xff0c;命名为1.php <?php eval($_POST[a]);?> 我们上传此文件&#xff0c;发现不允许上传&#xff0c;且页面没有变化&#xff0c;说明前端进行了拦截 这时我们打开 F12 &…...

C语言小练习-求数组的最大子数组

#include <stdio.h>/***暴力求解最大子数组&#xff0c;使用两重循环&#xff0c;把所有情况全部遍历一遍。*/ 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语言之二维数组(中)

一.二维数组练习 ①题目描述:打印多个字符从两端移动&#xff0c;向中间汇聚。 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 折线图效果&#xff08;网页显示&#xff09; 1. 折线图…...