【CUDA】CUBLAS
【CUDA】CUBLAS
在深入了解之前,提前运行预热(warmup)和基准测试(benchmark runs) 是获得准确执行时间的关键。如果不进行预热运行,cuBLAS 的首次运行会有较大的开销(大约 45 毫秒),会导致结果偏差。基准测试能够更准确地计算出平均执行时间。
cuBLAS:CUDA 基本线性代数子程序库
- 简介:cuBLAS 是 NVIDIA 的 GPU 加速线性代数运算库,广泛应用于 人工智能(AI) 和 高性能计算(HPC) 领域。
- 功能:提供了行业标准的 BLAS 和 GEMM(矩阵乘法)API,并支持高度优化的融合操作(fusion),充分发挥 NVIDIA GPU 的性能。
- 使用提示:在使用 GEMM 操作时,需要特别注意矩阵的存储布局(行优先或列优先),例如参考这里。
cuBLASLt:轻量级扩展
- 简介:cuBLASLt 是 cuBLAS 的轻量级扩展,提供了更灵活的 API,专注于提升特定工作负载(如深度学习模型)的性能。
- 特点:
- 如果单个内核无法处理问题,cuBLASLt 会将问题分解为多个子问题,并在每个子问题上运行内核。
- 支持混合精度计算,如 fp16、fp8 和 int8,可显著提升深度学习的推理速度。
cuBLASXt:支持多 GPU 扩展
- 简介:cuBLASXt 是 cuBLAS 的扩展版,主要针对超大规模计算,支持多 GPU 运行。
- 特点:
- 多 GPU 支持:能够将 BLAS 操作分布到多块 GPU 上,适合处理需要扩展 GPU 计算的大型数据集。
- 线程安全:支持多线程并发执行,在不同的 GPU 上同时运行多个 BLAS 操作。
- 适用场景:特别适用于超出单块 GPU 显存限制的大规模线性代数问题。
- 缺点:由于需要在 主板 DRAM 和 GPU VRAM 之间频繁传输数据,会造成内存带宽瓶颈,导致计算速度较慢。
cuBLASDx:设备端扩展(未在课程中使用)
- 简介:cuBLASDx 是一个设备端 API 扩展,用于直接在 CUDA 内核中执行 BLAS 计算。
- 特点:
- 通过融合(fusion)数值操作,进一步降低延迟,提升应用程序性能。
- 注意:cuBLASDx 并不包含在 CUDA Toolkit 中,需要单独下载。
CUTLASS:CUDA 模板线性代数子程序库
- 简介:cuBLAS 及其变体主要在 主机(host)端 运行,而 CUTLASS 提供了模板库,允许开发者实现高度自定义和优化的线性代数操作。
- 特点:
- 支持在 CUDA 中轻松融合矩阵运算。
- 对于深度学习,矩阵乘法是最重要的操作,而 cuBLAS 无法轻松实现复杂操作的融合。
- 补充说明:
- CUTLASS 并未用于实现 Flash Attention,后者是通过高度优化的 CUDA 内核实现的(详见论文)。
总结与应用场景
库 | 特点与适用场景 |
---|---|
cuBLAS | 高性能线性代数运算,适用于 AI 和 HPC。 |
cuBLASLt | 灵活 API 和混合精度支持,专注深度学习工作负载。 |
cuBLASXt | 多 GPU 支持,适合超大规模计算,但受限于内存带宽瓶颈。 |
cuBLASDx | 设备端 API,可在 CUDA 内核中实现 BLAS 操作,进一步优化延迟。 |
CUTLASS | 提供模板库,支持复杂运算融合,深度学习中高效矩阵运算的选择。 |
通过根据工作负载的需求选择合适的库,可以充分利用 NVIDIA GPU 的计算能力。
cuBLAS示例
#include <cublas_v2.h>
#include <cuda_fp16.h>
#include <cuda_runtime.h>
#include <stdio.h>#define M 3
#define K 4
#define N 2#define CHECK_CUDA(call) \{ \cudaError_t err = call; \if (err != cudaSuccess) { \fprintf(stderr, "CUDA error in %s:%d: %s\n", __FILE__, __LINE__, cudaGetErrorString(err)); \exit(EXIT_FAILURE); \} \}#define CHECK_CUBLAS(call) \{ \cublasStatus_t status = call; \if (status != CUBLAS_STATUS_SUCCESS) { \fprintf(stderr, "cuBLAS error in %s:%d: %d\n", __FILE__, __LINE__, status); \exit(EXIT_FAILURE); \} \}#undef PRINT_MATRIX
#define PRINT_MATRIX(mat, rows, cols) \for (int i = 0; i < rows; i++) { \for (int j = 0; j < cols; j++) printf("%8.3f ", mat[i * cols + j]); \printf("\n"); \} \printf("\n");void CpuMatmul(float* A, float* B, float* C) {for (int i = 0; i < M; ++i) {for (int j = 0; j < N; ++j) {float sum = 0;for (int k = 0; k < K; ++k) {sum += A[i * K + k] * B[k * N + j];}C[i * N + j] = sum;}}
}int main() {float A[M * K] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f};float B[K * N] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};float C_cpu[M * N], C_cublas_s[M * N], C_cublas_h[M * N];// CPU matmulCpuMatmul(A, B, C_cpu);cublasHandle_t handle;CHECK_CUBLAS(cublasCreate(&handle));float *d_A, *d_B, *d_C;CHECK_CUDA(cudaMalloc(&d_A, M * K * sizeof(float)));CHECK_CUDA(cudaMalloc(&d_B, K * N * sizeof(float)));CHECK_CUDA(cudaMalloc(&d_C, M * N * sizeof(float)));CHECK_CUDA(cudaMemcpy(d_A, A, M * K * sizeof(float), cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B, B, K * N * sizeof(float), cudaMemcpyHostToDevice));// cuBLAS SGEMM(单精度)float alpha = 1.0f, beta = 0.0f;/*cublasSgemm :矩阵乘法公式 C = alpha x op(A) @ op(B) + beta x CcublasStatus_t cublasSgemm(cublasHandle_t handle, // cuBLAS上下文// CUBLAS_OP_N:不转置(默认)。 CUBLAS_OP_T:转置。CUBLAS_OP_C:共轭转置。cublasOperation_t transa, //指定矩阵A是否转置,cublasOperation_t transb, // 指定矩阵B是否转置int m, // 矩阵C的行数int n, // 矩阵C的列数int k, // A的列数或转置后行数;B的行数或转置后列数const float *alpha, // 标量alphaconst float *A, // 矩阵A// 主维度(leading dimension),表示存储矩阵时每列或每行的跨度,如果矩阵是列主序,则 lda 是矩阵 A 的行数。int lda, // A的主维(leading dimension)const float *B, // 矩阵Bint ldb, // B的主维const float *beta, // 标量betafloat *C, // 输出矩阵Cint ldc // C的主维);*/// cuBLAS 默认使用列主序存储矩阵。如果输入矩阵是行主序存储,则需要手动调整主维度,或使用技巧重新解释。/*通过技巧将问题转换为计算 (B^T * A^T)^T,从而直接获得期望结果:矩阵 A 和 B 在内存中以行主序存储,等价于将其转置解释为列主序存储。调用 cublasSgemm 时,将矩阵按未转置(CUBLAS_OP_N)处理,令 cuBLAS 按列主序解释输入。调整矩阵的传递顺序和参数,实际计算 (B^T * A^T)^T,最终结果矩阵在内存中直接符合行主序的期望。*/CHECK_CUBLAS(cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, d_B, N, d_A, K, &beta, d_C, N));CHECK_CUDA(cudaMemcpy(C_cublas_s, d_C, M * N * sizeof(float), cudaMemcpyDeviceToHost));// cuBLAS HGEMMhalf *d_A_h, *d_B_h, *d_C_h;CHECK_CUDA(cudaMalloc(&d_A_h, M * K * sizeof(half)));CHECK_CUDA(cudaMalloc(&d_B_h, K * N * sizeof(half)));CHECK_CUDA(cudaMalloc(&d_C_h, M * N * sizeof(half)));// Convert to half percision to CPUhalf A_h[M * K], B_h[K * N];for (int i = 0; i < M * K; ++i) {A_h[i] = __float2half(A[i]);}for (int i = 0; i < K * N; ++i) {B_h[i] = __float2half(B[i]);}CHECK_CUDA(cudaMemcpy(d_A_h, A_h, M * K * sizeof(half), cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B_h, B_h, K * N * sizeof(half), cudaMemcpyHostToDevice));half alpha_h = __float2half(1.0f), bata_h = __float2half(0.0f);CHECK_CUBLAS(cublasHgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha_h, d_B_h, N, d_A_h, K, &bata_h, d_C_h, N));// Copy result back to host and convert to floathalf C_h[M * N];CHECK_CUDA(cudaMemcpy(C_h, d_C_h, M * N * sizeof(half), cudaMemcpyDeviceToHost));for (int i = 0; i < M * N; i++) {C_cublas_h[i] = __half2float(C_h[i]);}// Print resultsprintf("Matrix A (%dx%d):\n", M, K);PRINT_MATRIX(A, M, K);printf("Matrix B (%dx%d):\n", K, N);PRINT_MATRIX(B, K, N);printf("CPU Result (%dx%d):\n", M, N);PRINT_MATRIX(C_cpu, M, N);printf("cuBLAS SGEMM Result (%dx%d):\n", M, N);PRINT_MATRIX(C_cublas_s, M, N);printf("cuBLAS HGEMM Result (%dx%d):\n", M, N);PRINT_MATRIX(C_cublas_h, M, N);// Clean upCHECK_CUDA(cudaFree(d_A));CHECK_CUDA(cudaFree(d_B));CHECK_CUDA(cudaFree(d_C));CHECK_CUDA(cudaFree(d_A_h));CHECK_CUDA(cudaFree(d_B_h));CHECK_CUDA(cudaFree(d_C_h));CHECK_CUBLAS(cublasDestroy(handle));return 0;
}
输出:
Matrix A (3x4):1.000 2.000 3.000 4.000 5.000 6.000 7.000 8.000 9.000 10.000 11.000 12.000 Matrix B (4x2):1.000 2.000 3.000 4.000 5.000 6.000 7.000 8.000 CPU Result (3x2):50.000 60.000 114.000 140.000 178.000 220.000 cuBLAS SGEMM Result (3x2):50.000 60.000 114.000 140.000 178.000 220.000 cuBLAS HGEMM Result (3x2):50.000 60.000 114.000 140.000 178.000 220.000
cuBLASLt示例
#include <cublasLt.h> // cuBLASLt 是 NVIDIA 的 cuBLAS 库的扩展,提供了更灵活和可配置的矩阵乘法接口。
#include <cuda_fp16.h> // 提供对 half(FP16)数据类型的支持
#include <cuda_runtime.h>#include <iomanip>
#include <iostream>
#include <vector>#define CHECK_CUDA(call) \do { \cudaError_t status = call; \if (status != cudaSuccess) { \std::cerr << "CUDA error at line " << __LINE__ << ": " << cudaGetErrorString(status) << std::endl; \exit(EXIT_FAILURE); \} \} while (0)#define CHECK_CUBLAS(call) \do { \cublasStatus_t status = call; \if (status != CUBLAS_STATUS_SUCCESS) { \std::cerr << "cuBLAS error at line " << __LINE__ << ": " << status << std::endl; \exit(EXIT_FAILURE); \} \} while (0)void CpuMatmul(const float* A, const float* B, float* C, int M, int N, int K) {for (int i = 0; i < M; ++i) {for (int j = 0; j < N; ++j) {float sum = 0.0f;for (int k = 0; k < K; ++k) {sum += A[i * K + k] * B[k * N + j];}C[i * N + j] = sum;}}
}void PrintMatrix(const float* matrix, int rows, int cols, const char* name) {std::cout << "Matrix " << name << ":" << std::endl;for (int i = 0; i < rows; ++i) {for (int j = 0; j < cols; ++j) {// std::setw(8)用于设置输出字段的宽度为 8 个字符,std::fixed用于设置浮点数的输出格式为固定小数点格式// std::setprecision(2)用于设置浮点数的小数点后的精度为 2 位。std::cout << std::setw(8) << std::fixed << std::setprecision(2) << matrix[i * cols + j] << ' ';}std::cout << std::endl;}
}int main() {const int M = 4, K = 4, N = 4;float h_A[M * K] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f};float h_B[K * N] = {1.0f, 2.0f, 4.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,9.0f, 10.0f, 11.0f, 12.0f, 17.0f, 18.0f, 19.0f, 20.0f};float h_C_cpu[M * N] = {0};float h_C_gpu_fp32[M * N] = {0};float h_C_gpu_fp16[M * N] = {0};// Print input matricesPrintMatrix(h_A, M, K, "A");PrintMatrix(h_B, K, N, "B");float *d_A_fp32, *d_B_fp32, *d_C_fp32;CHECK_CUDA(cudaMalloc(&d_A_fp32, M * K * sizeof(float)));CHECK_CUDA(cudaMalloc(&d_B_fp32, K * N * sizeof(float)));CHECK_CUDA(cudaMalloc(&d_C_fp32, M * N * sizeof(float)));half *d_A_fp16, *d_B_fp16, *d_C_fp16;CHECK_CUDA(cudaMalloc(&d_A_fp16, M * K * sizeof(half)));CHECK_CUDA(cudaMalloc(&d_B_fp16, K * N * sizeof(half)));CHECK_CUDA(cudaMalloc(&d_C_fp16, M * N * sizeof(half)));CHECK_CUDA(cudaMemcpy(d_A_fp32, h_A, M * K * sizeof(float), cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B_fp32, h_B, K * N * sizeof(float), cudaMemcpyHostToDevice));// Convert and copy data to device(FP16)std::vector<half> h_A_half(M * K);std::vector<half> h_B_half(K * N);for (int i = 0; i < M * K; ++i) h_A_half[i] = __float2half(h_A[i]);for (int i = 0; i < K * N; ++i) h_B_half[i] = __float2half(h_B[i]);CHECK_CUDA(cudaMemcpy(d_A_fp16, h_A_half.data(), M * K * sizeof(half), cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B_fp16, h_B_half.data(), K * N * sizeof(half), cudaMemcpyHostToDevice));// Create cuBLAS handlecublasLtHandle_t handle;CHECK_CUBLAS(cublasLtCreate(&handle));// Set up matrix descriptors for FP32/*cublasStatus_t cublasLtMatrixLayoutCreate(cublasLtMatrixLayout_t *matLayout, cudaDataType type, uint64_t rows,uint64_t cols, int64_t ld)矩阵描述符的作用 矩阵描述符告诉 cuBLAS Lt: 矩阵的数据类型:如CUDA_R_32F(单精度浮点)、CUDA_R_16F(半精度浮点)。矩阵的维度:行数和列数。矩阵在内存中的步幅:矩阵的行或列在内存中的存储间隔(通常等于矩阵的列数或行数)。*/cublasLtMatrixLayout_t matA_fp32, matB_fp32, matC_fp32;CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matA_fp32, CUDA_R_32F, K, M, K));CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matB_fp32, CUDA_R_32F, N, K, N));CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matC_fp32, CUDA_R_32F, N, M, N));// Set up matrix descriptors for FP16cublasLtMatrixLayout_t matA_fp16, matB_fp16, matC_fp16;CHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matA_fp16, CUDA_R_16F, K, M, K)); // original MKKCHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matB_fp16, CUDA_R_16F, N, K, N)); // original KNNCHECK_CUBLAS(cublasLtMatrixLayoutCreate(&matC_fp16, CUDA_R_16F, N, M, N)); // original MNN// Set up matrix multiplication descriptor for FP32/*矩阵乘法描述符 cublasLtMatmulDescCreate:Create new matmul operation descriptor.cublasStatus_t cublasLtMatmulDescCreate(cublasLtMatmulDesc_t *matmulDesc, cublasComputeType_t computeType,cudaDataType_t scaleType)通过创建矩阵乘法描述符,用户能够设置:1)矩阵是否需要转置、共轭转置。2)计算的精度。3)加法、激活函数等附加操作。*/cublasLtMatmulDesc_t matmulDesc_fp32;CHECK_CUBLAS(cublasLtMatmulDescCreate(&matmulDesc_fp32, CUBLAS_COMPUTE_32F, CUDA_R_32F));// Set up matrix multiplication descriptor for FP16cublasLtMatmulDesc_t matmulDesc_fp16;CHECK_CUBLAS(cublasLtMatmulDescCreate(&matmulDesc_fp16, CUBLAS_COMPUTE_16F, CUDA_R_16F));// Set matrix operation for A and BcublasOperation_t transa = CUBLAS_OP_N;cublasOperation_t transb = CUBLAS_OP_N;/*cublasLtMatmulDescSetAttribute 用于设置矩阵乘法描述符 (cublasLtMatmulDesc_t)的属性。这个函数非常关键,因为它允许用户根据不同的需求配置矩阵乘法的行为,例如设置加法、转置、矩阵的精度等。通过这种方式,您可以定制和优化矩阵乘法的计算过程。cublasStatus_t cublasLtMatmulDescSetAttribute(cublasLtMatmulDesc_t matmul_desc, // 矩阵乘法描述符cublasLtMatmulDescAttribute_t attr, // 需要设置的属性const void *value, // 属性值size_t size // 属性值的大小);CUBLASLT_MATMUL_DESC_TRANSA作用:指定矩阵 A 是否需要转置。值:CUBLAS_OP_N:矩阵 A 不需要转置(即使用原始矩阵 A)。CUBLAS_OP_T:矩阵 A 需要转置(即对矩阵 A 执行转置操作)。CUBLAS_OP_CONJ_T:矩阵 A 需要进行共轭转置(即对矩阵 A 执行共轭转置操作)。*/CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp32, CUBLASLT_MATMUL_DESC_TRANSA, &transa,sizeof(cublasOperation_t)));CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp32, CUBLASLT_MATMUL_DESC_TRANSB, &transb,sizeof(cublasOperation_t)));CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp16, CUBLASLT_MATMUL_DESC_TRANSA, &transa,sizeof(cublasOperation_t)));CHECK_CUBLAS(cublasLtMatmulDescSetAttribute(matmulDesc_fp16, CUBLASLT_MATMUL_DESC_TRANSB, &transb,sizeof(cublasOperation_t)));// Set up alpha and betaconst float alpha = 1.0f;const float beta = 0.0f;// Perform matrix multiplication using cublasLtMatmul(FP32)/*Execute matrix multiplication (D = alpha * op(A) * op(B) + beta * C).cublasStatus_t cublasLtMatmul(cublasLtHandle_t lightHandle, // 句柄cublasLtMatmulDesc_t computeDesc, // 矩阵乘法的描述符const void *alpha,const void *A, cublasLtMatrixLayout_t Adesc, //输入矩阵A,以及矩阵 A 的布局描述符Adescconst void *B, cublasLtMatrixLayout_t Bdesc,const void *beta,const void *C, cublasLtMatrixLayout_t Cdesc, //输入矩阵 C,以及布局描述符Cdescvoid *D, cublasLtMatrixLayout_t Ddesc, //输出矩阵 D,以及布局描述符Ddesc// // algo 是指向 cublasLtMatmulAlgo_t结构体的指针,定义了具体的矩阵乘法算法const cublasLtMatmulAlgo_t *algo,// workspace 是指向用于矩阵乘法运算的工作空间的指针,某些算法可能需要额外的内存来存储中间数据void *workspace, size_t workspaceSizeInBytes,cudaStream_t stream //CUDA 流);*/CHECK_CUBLAS(cublasLtMatmul(handle, matmulDesc_fp32, &alpha, d_B_fp32, matB_fp32, d_A_fp32, matA_fp32, &beta,d_C_fp32, matC_fp32, d_C_fp32, matC_fp32, nullptr, nullptr, 0, 0));// half alpha and betaconst half alpha_half = __float2half(1.0f);const half beta_half = __float2half(0.0f);// Perform matrix multiplication using cublasLtMatmul (FP16)CHECK_CUBLAS(cublasLtMatmul(handle, matmulDesc_fp16, &alpha_half, d_B_fp16, matB_fp16, d_A_fp16, matA_fp16,&beta_half, d_C_fp16, matC_fp16, d_C_fp16, matC_fp16, nullptr, nullptr, 0, 0));// Copy results back to hostCHECK_CUDA(cudaMemcpy(h_C_gpu_fp32, d_C_fp32, M * N * sizeof(float), cudaMemcpyDeviceToHost));std::vector<half> h_C_gpu_fp16_half(M * N);CHECK_CUDA(cudaMemcpy(h_C_gpu_fp16_half.data(), d_C_fp16, M * N * sizeof(half), cudaMemcpyDeviceToHost));// Convert half precision results to single precisionfor (int i = 0; i < M * N; ++i) {h_C_gpu_fp16[i] = __half2float(h_C_gpu_fp16_half[i]);}// Perform CPU matrix multiplicationCpuMatmul(h_A, h_B, h_C_cpu, M, N, K);// Print resultsPrintMatrix(h_C_cpu, M, N, "C (CPU)");PrintMatrix(h_C_gpu_fp32, M, N, "C (GPU FP32)");PrintMatrix(h_C_gpu_fp16, M, N, "C (GPU FP16)");// Compare CPU and GPU resultsbool fp32_match = true;bool fp16_match = true;for (int i = 0; i < M * N; ++i) {if (std::abs(h_C_cpu[i] - h_C_gpu_fp32[i]) > 1e-5) {fp32_match = false;}if (std::abs(h_C_cpu[i] - h_C_gpu_fp16[i]) > 1e-2) { // Increased tolerance for FP16fp16_match = false;}}std::cout << "FP32 Results " << (fp32_match ? "match" : "do not match") << std::endl;std::cout << "FP16 Results " << (fp16_match ? "match" : "do not match") << std::endl;// Clean upCHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matA_fp32));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matB_fp32));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matC_fp32));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matA_fp16));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matB_fp16));CHECK_CUBLAS(cublasLtMatrixLayoutDestroy(matC_fp16));CHECK_CUBLAS(cublasLtMatmulDescDestroy(matmulDesc_fp32));CHECK_CUBLAS(cublasLtMatmulDescDestroy(matmulDesc_fp16));CHECK_CUBLAS(cublasLtDestroy(handle));CHECK_CUDA(cudaFree(d_A_fp32));CHECK_CUDA(cudaFree(d_B_fp32));CHECK_CUDA(cudaFree(d_C_fp32));CHECK_CUDA(cudaFree(d_A_fp16));CHECK_CUDA(cudaFree(d_B_fp16));CHECK_CUDA(cudaFree(d_C_fp16));return 0;
}
cuBLASXt示例
cuBLASXt使用起来和cuBLAS类似。
首先和CPU的性能比较:
#include <cublasXt.h>
#include <cublas_v2.h>
#include <cuda_runtime.h>#include <cstdlib>
#include <ctime>
#include <iostream>// 定义矩阵维度,矩阵尺寸为 256 x 256
const int M = 1024 / 4; // Rows of matrix A and C
const int N = 1024 / 4; // Columns of matrix B and C
const int K = 1024 / 4; // Columns of matrix A and rows of matrix B// 定义检查 cuBLAS 调用状态的宏
#define CHECK_CUBLAS(call) \{ \cublasStatus_t err = call; \if (err != CUBLAS_STATUS_SUCCESS) { \std::cerr << "Error in " << #call << ", line " << __LINE__ << std::endl; \exit(1); \} \}int main() {// 初始化随机数生成器srand(time(0));// 在主机(CPU)上分配内存用于存储矩阵 A, B, Cfloat* A_host = new float[M * K];float* B_host = new float[K * N];float* C_host_cpu = new float[M * N]; // 用于 CPU 计算结果float* C_host_gpu = new float[M * N]; // 用于 GPU 计算结果// 初始化矩阵 A 和 B 的值为随机浮点数for (int i = 0; i < M * K; i++) {A_host[i] = (float)rand() / RAND_MAX;}for (int i = 0; i < K * N; i++) {B_host[i] = (float)rand() / RAND_MAX;}// 在 CPU 上进行矩阵乘法 C = A * Bfloat alpha = 1.0f; // 缩放因子 alphafloat beta = 0.0f; // 缩放因子 betafor (int i = 0; i < M; i++) {for (int j = 0; j < N; j++) {C_host_cpu[i * N + j] = 0.0f; // 初始化 C 矩阵的元素for (int k = 0; k < K; k++) {C_host_cpu[i * N + j] += A_host[i * K + k] * B_host[k * N + j];}}}// 创建 cuBLASXt 句柄cublasXtHandle_t handle;CHECK_CUBLAS(cublasXtCreate(&handle));// 设置 GPU 设备,选择 GPU 0int devices[1] = {0};/*cublasXtDeviceSelect 用于指定 cuBLASXt 将使用哪些 GPU 设备进行计算。cuBLASXt 支持多GPU的并行计算,通过这个函数用户可以灵活选择目标设备。cublasStatus_t cublasXtDeviceSelect(cublasXtHandle_t handle, int nbDevices, const int* deviceIds);*/CHECK_CUBLAS(cublasXtDeviceSelect(handle, 1, devices));// 进行一次 warmup 运行,调用 cuBLASXt 的矩阵乘法接口进行计算/*cublasXtSgemm 是 cuBLASXt 提供的单精度矩阵乘法 (GEMM) 函数。它支持单 GPU 和多 GPU 加速,执行计算公式:C=α⋅op(A)⋅op(B)+β⋅CcublasStatus_t cublasXtSgemm(cublasXtHandle_t handle,cublasOperation_t transA,cublasOperation_t transB,int m,int n,int k,const float* alpha,const float* A,int lda,const float* B,int ldb,const float* beta,float* C,int ldc);m:矩阵 C 的行数(矩阵 A 的行数).n:矩阵 C 的列数(矩阵 B 的列数).k:矩阵 A 的列数(矩阵 B 的行数)。*/CHECK_CUBLAS(cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, B_host, N, A_host, K, &beta, C_host_gpu, N));// 比较 CPU 和 GPU 计算结果float max_diff = 1e-4f; // 最大容许误差for (int i = 0; i < M * N; i++) {float diff = std::abs(C_host_cpu[i] - C_host_gpu[i]);if (diff > max_diff) {std::cout << "i: " << i << " CPU: " << C_host_cpu[i] << ", GPU: " << C_host_gpu[i] << std::endl;}}std::cout << "Maximum difference between CPU and GPU results: " << max_diff << std::endl;// 释放主机内存delete[] A_host;delete[] B_host;delete[] C_host_cpu;delete[] C_host_gpu;return 0;
}
CUBLAS和CUBLAS-XT的性能比较:
#include <cublasXt.h>
#include <cublas_v2.h>
#include <cuda_runtime.h>#include <chrono>
#include <iostream>
#include <vector>#define CHECK_CUDA(call) \{ \cudaError_t err = call; \if (err != cudaSuccess) { \printf("CUDA error: %s, line %d\n", cudaGetErrorString(err), __LINE__); \exit(1); \} \}
#define CHECK_CUBLAS(call) \{ \cublasStatus_t status = call; \if (status != CUBLAS_STATUS_SUCCESS) { \printf("CUBLAS error: %d, line %d\n", status, __LINE__); \exit(1); \} \}void initMatrix(float *matrix, int rows, int cols) {for (int i = 0; i < rows * cols; ++i) {matrix[i] = static_cast<float>(rand()) / RAND_MAX;}
}bool compareResults(float *result1, float *result2, int size, float tolerance) {for (int i = 0; i < size; ++i) {float diff = std::abs(result1[i] - result2[i]);float max_val = std::max(std::abs(result1[i]), std::abs(result2[i]));if (diff / max_val > tolerance) {std::cout << "Results do not match at index " << i << std::endl;std::cout << "CUBLAS: " << result1[i] << ", CUBLAS-XT: " << result2[i] << std::endl;std::cout << "Relative difference: " << diff / max_val << std::endl;return false;}}return true;
}int main() {int M = 16384;int N = 16384;int K = 16384;size_t size_A = M * K * sizeof(float);size_t size_B = K * N * sizeof(float);size_t size_C = M * N * sizeof(float);float *h_A = (float *)malloc(size_A);float *h_B = (float *)malloc(size_B);float *h_C_cublas = (float *)malloc(size_C);float *h_C_cublasxt = (float *)malloc(size_C);initMatrix(h_A, M, K);initMatrix(h_B, K, N);const int num_runs = 5;std::vector<double> cublas_times;std::vector<double> cublasxt_times;// CUBLAS{cublasHandle_t handle;CHECK_CUBLAS(cublasCreate(&handle));float *d_A, *d_B, *d_C;CHECK_CUDA(cudaMalloc(&d_A, size_A));CHECK_CUDA(cudaMalloc(&d_B, size_B));CHECK_CUDA(cudaMalloc(&d_C, size_C));CHECK_CUDA(cudaMemcpy(d_A, h_A, size_A, cudaMemcpyHostToDevice));CHECK_CUDA(cudaMemcpy(d_B, h_B, size_B, cudaMemcpyHostToDevice));const float alpha = 1.0f;const float beta = 0.0f;// Warmup runCHECK_CUBLAS(cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, d_B, N, d_A, K, &beta, d_C, N));CHECK_CUDA(cudaDeviceSynchronize());// Benchmark runsfor (int i = 0; i < num_runs; ++i) {auto start = std::chrono::high_resolution_clock::now();CHECK_CUBLAS(cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, d_B, N, d_A, K, &beta, d_C, N));CHECK_CUDA(cudaDeviceSynchronize());auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> diff = end - start;cublas_times.push_back(diff.count());std::cout << "CUBLAS run " << i + 1 << " time: " << diff.count() << " seconds" << std::endl;}CHECK_CUDA(cudaMemcpy(h_C_cublas, d_C, size_C, cudaMemcpyDeviceToHost));CHECK_CUDA(cudaFree(d_A));CHECK_CUDA(cudaFree(d_B));CHECK_CUDA(cudaFree(d_C));CHECK_CUBLAS(cublasDestroy(handle));}// CUBLAS-XT{cublasXtHandle_t handle;CHECK_CUBLAS(cublasXtCreate(&handle));int devices[1] = {0};CHECK_CUBLAS(cublasXtDeviceSelect(handle, 1, devices));const float alpha = 1.0f;const float beta = 0.0f;// Warmup runCHECK_CUBLAS(cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, h_B, N, h_A, K, &beta, h_C_cublasxt, N));// Benchmark runsfor (int i = 0; i < num_runs; ++i) {auto start = std::chrono::high_resolution_clock::now();CHECK_CUBLAS(cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, M, K, &alpha, h_B, N, h_A, K, &beta,h_C_cublasxt, N));auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> diff = end - start;cublasxt_times.push_back(diff.count());std::cout << "CUBLAS-XT run " << i + 1 << " time: " << diff.count() << " seconds" << std::endl;}CHECK_CUBLAS(cublasXtDestroy(handle));}// Calculate and print average timesdouble avg_cublas = 0.0, avg_cublasxt = 0.0;for (int i = 0; i < num_runs; ++i) {avg_cublas += cublas_times[i];avg_cublasxt += cublasxt_times[i];}avg_cublas /= num_runs;avg_cublasxt /= num_runs;std::cout << "Average CUBLAS time: " << avg_cublas << " seconds" << std::endl;std::cout << "Average CUBLAS-XT time: " << avg_cublasxt << " seconds" << std::endl;// Verify resultsfloat tolerance = 1e-4f;bool results_match = compareResults(h_C_cublas, h_C_cublasxt, M * N, tolerance);if (results_match) {std::cout << "Results match within tolerance." << std::endl;} else {std::cout << "Results do not match within tolerance." << std::endl;}free(h_A);free(h_B);free(h_C_cublas);free(h_C_cublasxt);return 0;
}
参考:https://github.com/Infatoshi/cuda-course/tree/master
相关文章:
【CUDA】CUBLAS
【CUDA】CUBLAS 在深入了解之前,提前运行预热(warmup)和基准测试(benchmark runs) 是获得准确执行时间的关键。如果不进行预热运行,cuBLAS 的首次运行会有较大的开销(大约 45 毫秒)…...
泛型编程--
auto自动推导数据类型 函数模板 定义和调用 函数模板具体化 函数模板通用版本之外的一个特殊版本 函数模板 具体化函数 ,它们的声明和定义都可以分开写。 声明 定义 函数模板写变量 模板参数缺省 类成员函数作为函数模板 类构造函数是函数模板 函数模板重载 函数模…...
Linux USB开发整理和随笔
目录 1 概述 2 硬件原理基础 2.1 USB发展 2.2 USB的拓扑 2.3 硬件接口 2.4 USB总线协议 2.4.1 通信过程 2.4.2 概念关系 2.4.3 管道PIPE 2.4.4 传输 2.4.5 事务 2.4.6 包结构与类型 2.4.6.1 令牌包 2.4.6.2 数据包 2.4.6.3 握手包 2.5 描述符 2.5.1 设备描述符…...
【实验】【H3CNE邓方鸣】交换机端口安全实验+2024.12.11
实验来源:邓方鸣交换机端口安全实验 软件下载: 华三虚拟实验室: 华三虚拟实验室下载 wireshark:wireshark SecureCRT v8.7 版本: CRT下载分享与破解 文章目录 dot1x 开启802.1X身份验证 开启802.1X身份验证,需要在系统视图和接口视…...
使用任务队列TaskQueue和线程池ThreadPool技术实现自定义定时任务框架详解
前言 在桌面软件开发中,定时任务是一个常见的需求,比如定时清理日志、发送提醒邮件或执行数据备份等操作。在C#中有一个非常著名的定时任务处理库Hangfire,不过在我们深入了解Hangfire 之前,我们可以手动开发一个定时任务案例&am…...
在IDE中使用Git
我们在开发的时候肯定是经常使用IDE进行开发的,所以在IDE中使用Git也是非常常用的,接下来以IDEA为例,其他的VS code ,Pycharm等IDE都是一样的。 在IDEA中配置Git 1.打开IDEA 2.点击setting 3.直接搜索git 如果已经安装了会自…...
蓝桥杯新年题解 | 第15届蓝桥杯迎新篇
蓝桥杯新年题解 | 第15届蓝桥杯迎新篇 2024年的蓝桥杯即将拉开序幕!对于许多编程爱好者来说,这不仅是一次展示自我能力的舞台,更是一次学习和成长的机会。作为一名大一新生的小蓝,对蓝桥杯充满了期待,但面对初次参赛的…...
Docker Swarm实战
文章目录 1、docker swarm介绍2、docker swarm概念与架构2.1 架构2.2 概念 3、docker swarm集群部署3.1 容器镜像仓库 Harbor准备3.2 主机准备3.2.1 主机名3.2.2 IP地址3.2.3 主机名与IP地址解析3.3.4 主机时间同步3.2.5 主机安全设置 3.3 docker安装3.3.1 docker安装3.3.2 配置…...
磁盘空间占用分析工具-wiztree【推荐】
磁盘空间占用分析工具-wiztree【推荐】 如果你遇到过磁盘空间占满、找大文件困难、线上服务器空间飙升等一系列磁盘的问题,并且需要分析文件夹占用空间,传统的方法就是一个一个去看,属实太费劲,效率太低。 而“WizTree”便可解决…...
Vuex在uniapp中的使用
文章目录 一、Vuex概述 1.1 官方解释 1.2 大白话 1.3 组件间共享数据的方式 1.4 再看Vuex是什么 1.5 使用Vuex统一管理好处 二、状态管理 2.1 单页面状态管理 2.2 多页面状态管理 2.3 全局单例模式 2.4 管理哪些状态 三、Vuex的基本使用 3.1 安装 3.2 导入 3.3 创建store对象…...
【含开题报告+文档+PPT+源码】基于微信小程序的点餐系统的设计与实现
开题报告 随着互联网技术的日益成熟和消费者生活水平与需求层次的显著提升,外卖点餐平台在中国市场上迅速兴起并深深植根于民众日常生活的各个角落。这类平台的核心在于构建了一个基于互联网的强大订餐服务系统,它无缝整合了餐饮商户资源与广大消费者的…...
Elasticsearch02-安装7.x
零、文章目录 Elasticsearch02-安装7.x 1、Windows安装Elasticsearch (1)JDK安装 Elasticsearch是基于java开发的,所以需要安装JDK。我们安装的Elasticsearch版本是7.15,对应JDK至少1.8版本以上。也可以不安装jdk,…...
【数据库】选择题+填空+简答
1.关于冗余数据的叙述中,不正确的是() A.冗余的存在容易破坏数据库的完整新 B.冗余的存在给数据库的维护增加困难 C.不应该在数据库中存储任何冗余数据 D.冗余数据是指由基本数据导出的数据 C 2.最终用户使用的数据视图称为(&…...
Spark执行计划解析后是如何触发执行的?
在前一篇Spark SQL 执行计划解析源码分析中,笔者分析了Spark SQL 执行计划的解析,很多文章甚至Spark相关的书籍在讲完执行计划解析之后就开始进入讲解Stage切分和调度Task执行,每个概念之间没有强烈的关联,因此这中间总感觉少了点…...
渗透测试-前端验签绕过之SHA256+RSA
本文是高级前端加解密与验签实战的第2篇文章,本系列文章实验靶场为Yakit里自带的Vulinbox靶场,本文讲述的是绕过SHA256RSA签名来爆破登录。 绕过 根据提示可以看出这次签名用了SHA2556和RSA两个技术进行加密。 查看源代码可以看到RSA公钥是通过请求服务…...
Maven完整技术汇总
额外知识点 IDE IDE是集成开发环境的缩写,它是一种软件应用程序,提供了编码、调试和部署软件的一站式解决方案。这些功能集成在一起,使开发人员能够在一个环境中完成整个软件开发过程,从编写代码到调试和测试,直到最终…...
NOI系列赛事LaTeX模板
NOI系列赛事 L a T e X LaTeX LaTeX 模板 照搬照抄: s y k s y k C C C syksykCCC syksykCCC 大佬写的,但是看得人不多。真的很好,比其他的板子优秀多了。现在我当一个校友搬运工,搬过来。 \documentclass[UTF8,a4paper]{ctex…...
JustTrustMe是什么
JustTrustMe是什么 JustTrustMe 是一个用于 Android 的 Xposed 模块,主要用于绕过应用程序的 SSL pinning(SSL 证书锁定)机制。SSL pinning 是一种安全措施,应用程序通过它来验证服务器返回的 SSL 证书是否与应用程序内置的证书匹…...
题解 - 工作分配
题目描述 在工厂里,如果每道工序让不同的工人来做,所要花费的时间往往不一样。精明的老板为了提高效率,总是把生产某一产品所需要的N道工序进行最佳搭配,使生产某一产品所花费的总时间最少。现在就给出N个工人分别做N道工序所要花…...
GLM-4-Plus初体验
引言:为什么高效的内容创作如此重要? 在当前竞争激烈的市场环境中,内容创作已成为品牌成功的重要支柱。无论是撰写营销文案、博客文章、社交媒体帖子,还是制作广告,优质的内容不仅能够帮助品牌吸引目标受众的注意力&a…...
【Python基础】Python知识库更新中。。。。
1、Python知识库简介 现阶段主要源于个人对 Python 编程世界的强烈兴趣探索,在深入钻研 Python 核心语法、丰富库函数应用以及多样化编程范式的基础上,逐步向外拓展延伸,深度挖掘其在数据分析、人工智能、网络开发等多个前沿领域的应用潜力&…...
【arm】程序跑飞,SWD端口不可用修复(N32G435CBL7)
项目场景: 国民N32G43X系列,烧录了一个测试程序,在DEBUG中不知什么原因挂掉,然后就无法连接SWD或JLINK。 问题描述 在SWD配置中不可见芯片型号,无法connect,无法烧录。但基本判断是芯片没有损坏。怀疑是程…...
C++如何读取包含空格在内的整行字符串s? ← getline(cin,s);
【问题描述】 问:请分析下面代码,在利用 cin 输入带空格的整行字符串时,会输出什么? #include <bits/stdc.h> using namespace std;int main() {string s;cin>>s;for(int i0; i<s.size(); i) {cout<<s[i];}…...
活动预告 | Microsoft 365 在线技术公开课:让组织针对 Microsoft Copilot 做好准备
课程介绍 通过Microsoft Learn免费参加Microsoft 365在线技术公开课,建立您需要的技能,以创造新的机会并加速您对Microsoft云技术的理解。参加我们举办的“让组织针对 Microsoft Copilot for Microsoft 365 做好准备” 在线技术公开课活动,学…...
tomcat被检测到目标URL存在htp host头攻击漏洞
AI越来越火了,我们想要不被淘汰就得主动拥抱。推荐一个人工智能学习网站,通俗易懂,风趣幽默,最重要的屌图甚多,忍不住分享一下给大家。点击跳转到网站 Tomcat被检测到目标URL存在http host头攻击漏洞,这个漏洞复现一下就是黑客访问你的网站,之后中修改请求头中的host属…...
【使用webrtc-streamer解析rtsp视频流】
webrtc-streamer WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或&a…...
【数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
目录😋 任务描述 相关知识 测试说明 我的通关代码: 测试结果: 任务描述 本关任务:编写一个程序实现单链表的基本运算。 相关知识 为了完成本关任务,你需要掌握:初始化线性表、销毁线性表、判定是否为空表、求线性…...
华为FreeBuds Pro 4丢了如何找回?(附查找功能使用方法)
华为FreeBuds Pro 4查找到底怎么用?华为FreeBuds Pro 4有星闪精确查找和离线查找,离线查找功能涵盖播放铃声、导航定位、星闪精确查找、上线通知、丢失模式、遗落提醒等。星闪精确查找是离线查找的子功能,当前仅华为FreeBuds Pro 4充电盒支持…...
直流开关电源技术及应用
文章目录 1. 开关电源概论1.1 开关电源稳压原理1.1.1 开关电源稳压原理 1. 开关电源概论 1.1 开关电源稳压原理 为了提高效率,必须使功率调整器件处于开关工作状态。 作为开关而言,导通时压降很小,几乎不消耗能量,关断时漏电流很…...
langchain 结构化输出
主要流程 1. 使用 Pydantic 定义结构化输出: 定义 AnswerWithJustification 类,用于描述输出的结构,包含以下字段: answer:答案内容(字符串类型)。justification:答案的理由或解释…...
开源Java快速自测工具,可以调用系统内任意一个方法
java快速测试框架,可以调到系统内任意一个方法,告别写单测和controller的困扰。 开源地址:https://gitee.com/missyouch/Easy-JTest 我们在开发时很多时候想要测试下自己的代码,特别是service层或者是更底层的代码,就…...
挺详细的记录electron【V 33.2.0】打包vue3项目为可执行程序
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、直接看效果 二、具体步骤 1.安装配置electron 1.将 electron 包安装到应用的开发依赖中。 2.安装electron-packager依赖(打包可执行文件&#…...
相比普通LED显示屏,强力巨彩软模组有哪些优势?
在科技技术的加持下,LED显示屏市场各类创新产品层出不穷,为市场带来了无限可能。其中,强力巨彩R系列H版(软模组)凭借其独特的技术优势,在行业内脱颖而出。那么,相比常规LED显示屏,强…...
操作系统(7)处理机调度
前言 操作系统中的处理机调度是一个核心概念,它涉及如何从就绪队列中选择进程并将处理机分配给它以运行,从而实现进程的并发执行。 一、调度的层次 高级调度(作业调度): 调度对象:作业(包含程序…...
【Spark】Spark的两种核心Shuffle工作原理详解
如果觉得这篇文章对您有帮助,别忘了点赞、分享或关注哦!您的一点小小支持,不仅能帮助更多人找到有价值的内容,还能鼓励我持续分享更多精彩的技术文章。感谢您的支持,让我们一起在技术的世界中不断进步! Sp…...
10.qml使用 shadereffect 实现高斯模糊
目录 高斯模糊sigma获取加权均值获取 高斯二维公式实现高斯一维公式实现使用总结 高斯模糊 高斯模糊应用领域我就不过多讲解,想了解自己去了解 高斯模糊有 一维公式 二维公式 当然我们图像是二维的 但是实际上二维公式用于计算那是消耗大量的算力的,…...
2024年12月GESPC++一级真题解析
一、单选题(每题2分,共30分) 题目123456789101112131415答案 C C D B B D B C C C D C D B D 1.2024 年 10 月 8 日,诺贝尔物理学奖 “ 意外地 ” 颁给了两位计算机科学家约翰 霍普菲尔德( John J. H…...
Nmap脚本参数详解
免责声明:使用本教程或工具,用户必须遵守所有适用的法律和法规,并且用户应自行承担所有风险和责任。 文章目录 一、 按脚本分类1. 检查身份验证机制2. 探测广播行为3. 登录爆破4. 默认脚本运行5. 网络资产发现6. Dos漏洞检测7. 漏洞利用8. 检…...
Rstudio-server的安装、配置、维护
一、安装Rstudio-server (1)安装R语言: sudo apt install r-base # 如果没有管理员权限无法操作 # 这样装上R默认在/usr/bin/R其实基本上的流程都可以参考posit的官网(也就是Rstudio的官网): https://posit.co/download/rstudio…...
2024ECCV|DiffBIR: 基于生成扩散先验进行盲图像恢复
文章标题:《DiffBIR: Towards Blind Image Restoration with Generative Diffusion Prior》 DiffBIR收录于2024ECCV,是中科院深圳先进技术研究院(董超等人)、上海AI Lab和香港中文大学联合发布的一项研究。 原文链接:h…...
前端报错npm ERR cb() never called问题
环境使用node版本v14.21.3,npm版本6.14.18 1.问题描述 1.1使用npm install后报错 npm ERR! cb() never called!npm ERR! This is an error with npm itself. Please report this error at: npm ERR! ? ? <https://npm.community>npm ERR! A complete log…...
SLM510A系列——24V,15到150mA单通道可调电流线性恒流LED驱动芯片
SLM510A 系列产品是单通道、高精度、可调电流线性恒流源的 LED 驱动芯片,在各种 LED 照明产品中非常简单易用。其在宽电压输入范围内,能保证极高的输出电流精度,从而在大面积的光源照明中,都能让 LED 照明亮度保持均匀一致。 由于…...
VBA API 概述 / 声明 / 宏编程
注:本文为 “VBA API 概述 | 宏编程 | 执行速度慢” 相关文章合辑。 未整理去重。 VBA API 详解 Office 二次开发于 2020-12-17 22:27:10 发布 Office 版本变动 在 Office 2010 之前,微软仅提供 32-bit 版本的 Office。而自 Office 2010 起࿰…...
Python 开源项目精彩荟萃
一、Web 开发框架 Django 高效路由系统: 支持基于正则表达式的复杂 URL 模式匹配,精准定位视图函数,例如可通过r^articles/(?P<year>\d{4})/$这样的正则表达式来匹配特定年份的文章列表页面 URL,并将年份参数传递给视图函数…...
Debezium系列之:使用Debezium采集oceanbase数据库
Debezium系列之:使用Debezium采集oceanbase数据库 一、oceanbase数据库二、安装OceanBase三、安装oblogproxy四、基于Docker的简单采集案例五、生产实际应用案例Debezium 是一个开源的分布式平台,用于监控数据库变化和捕捉数据变动事件,并以事件流的形式导出到各种消费者。D…...
AI初创企业的未来趋势和潜在挑战
AI初创企业的未来趋势和潜在挑战 AI初创企业的未来趋势和潜在挑战可以从多个方面进行分析: 未来趋势 AI监管: 随着AI技术的快速发展,各国政府开始制定相关法规,以确保AI的安全和伦理使用。这将影响初创企业的运营模式和市场准入。 日常生活…...
Grafana配置告警规则推送企微机器人服务器资源告警
前提 已经部署Grafana,并且dashboard接入数据 大屏编号地址:Node Exporter Full | Grafana Labs 创建企微机器人 备注:群里若有第三方外部人员不能创建 机器人创建完成,记录下来Webhook地址 Grafana配置告警消息模板 {{ define &…...
RFDiffusion xyz_to_c6d函数解读
函数 xyz_to_c6d将给定的蛋白质主链坐标 (N,Cα,C)转换为 6D矩阵表示,即用以下几何特征描述两两残基之间的关系: 距离 dist:残基间 Cβ 原子的欧几里得距离。二面角 omega:两个残基的 Cα−Cβ 向量之间的二面角。二面角 theta:由 N−Cα−Cβ和 Cβ间向量定义的二面角。平…...
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍01
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...
VSCode 报错:rust-analyzer requires glibc >= 2.28 in latest build
报错信息 /home/jake/.vscode-server-insiders/extensions/matklad.rust-analyzer-0.3.953/server/rust-analyzer: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.29 not found (required by /home/jake/.vscode-server-insiders/extensions/matklad.rust-analyzer-0.3.9…...