向量化计算

例子: 图像亮度调整

假设我们有一张黑白照片,需要将每个像素的亮度增加20%。每个像素的亮度值用一个0-255的整数表示

数据表示

原始图像: 1,000×1,000像素 = 1百万像素

每个像素: 8 位无符号整数(0-255)

操作: 新像素值 = 旧像素值 * 1.2, 然后截断到 255

方法1:标量处理 (单车道公路)

c 复制代码
// 标量代码 - 一次处理一个像素
for (int i = 0; i < 1000000; i++) {
    // 加载一个像素(1个数据)
    uint8_t pixel = image[i];
    
    // 计算新值(需要转换为浮点数)
    float temp = pixel * 1.2f;
    
    // 截断并存储
    if (temp > 255.0f) temp = 255.0f;
    result[i] = (uint8_t)temp;
}

标量处理的特性:

  • 每次循环处理1个数据
  • 每个时钟周期完成1个乘法操作
  • 效率低: 就像单车道公路,一次只能过一辆车

假设:

  • CPU 频率: 3.0 Ghz (每秒30亿个周期)
  • 每个像素需要5个时钟周期处理
  • 总时间: 1,000,000像素 × 5周期/像素 ÷ 3,000,000,000周期/秒 ≈ 0.00167秒

方法2:向量化处理(四车道高速公路)

现代CPU有512位向量寄存器,可以同时处理多个数据:

  • 512位 ÷ 8位/像素 = 64个像素一次处理!
  • 使用AVX-512指令集
c 复制代码
// 向量化代码 - 一次处理64个像素
#include <immintrin.h>

// 创建向量常量:1.2重复64次
__m512 scale_factor = _mm512_set1_ps(1.2f);
// 创建向量常量:255.0重复64次
__m512 max_value = _mm512_set1_ps(255.0f);

for (int i = 0; i < 1000000; i += 64) {
    // 1. 加载64个像素到向量寄存器
    // 注意:需要将8位整数转换为32位浮点数
    __m512i pixels_8bit = _mm512_loadu_si512(&image[i]);
    
    // 2. 转换为32位整数,再转换为浮点数(分两步)
    __m512i pixels_32bit = _mm512_cvtepu8_epi32(_mm512_extracti64x4_epi64(pixels_8bit, 0));
    __m512 pixels_float = _mm512_cvtepi32_ps(pixels_32bit);
    
    // 3. 乘以1.2(64个像素同时计算!)
    __m512 scaled = _mm512_mul_ps(pixels_float, scale_factor);
    
    // 4. 限制最大值255(64个比较同时进行!)
    scaled = _mm512_min_ps(scaled, max_value);
    
    // 5. 转换回8位并存储
    __m512i result_32bit = _mm512_cvtps_epi32(scaled);
    // ... 进一步打包为8位并存储
    _mm512_storeu_si512(&result[i], packed_8bit);
}

向量化处理的特性:

  • 向量化处理的特性
  • 每个时钟周期完成64个乘法操作
  • 效率高: 就像64车道高速公路,一次过64辆车

假设:

  • CPU 频率: 3.0 GHz (相同)
  • 每64个像素需要20个时钟周期处理(由于转换操作更复杂)
  • 总循环次数: 1,000,000 ÷ 64 ≈ 15,625次
  • 总时间 = 15,625次 × 20周期/次 ÷ 3,000,000,000周期/秒 ≈ 0.000104秒

性能与能效对比

1. 性能对比

指标 标量处理 向量化处理 加速比
处理时间 0.00167秒 0.000104秒 16倍
吞吐量 60万像素/秒 960万像素/秒 16倍

虽然理论上512位可以处理64个8位像素,但由于需要数据类型转换(8 位 -> 32 位浮点 -> 计算 -> 32位整数 -> 8位),实际加速不是64位,但仍有显著提升

2. 能效对比(关键点)

能量消耗分析

假设:

  • 每个标量乘法消耗 1单位能量
  • 每个向量乘法(64个像素同时)消耗 1.2单位能量(只多20%!)

标量处理总消耗

  • 1,000,000次乘法 × 1单位能量 = 1,000,000单位能
  • 总能耗 = 1,000,000单位

向量化处理总能耗

  • 15,625次向量乘法 × 1.2单位能量 = 18,750单位能量
  • 还需要考虑数据类型转换的能耗,假设额外50%:总能耗 = 18,750 × 1.5 = 28,125单位能量

能效对比表

指标 标量处理 向量化处理 改善
总能耗 1,000,000单位 28,125单位 降低35.5倍
单位像素能耗 1.0单位/像素 0.028单位/像素 降低35.5倍
能量延迟积 1,000,000×0.00167=1,670 28,125×0.000104=2.93 降低570倍

为什么向量化更节能?

分摊开销原理

  • 前端开销相同:取指、解码、调度等步骤,标量和向量指令都需要
  • 后端收益倍增: 执行单元一次完成多个操作
  • 结果: 64倍的计算量,只增加少量能耗

硬件实现效率

向量单元内部是并行电路,不是64个独立单元

控制逻辑共享,数据通路并行

就像工厂流水线:一条生产线同时组装64个产品,比64条小生产线更高校

实际芯片设计示例

以Intel AVX-512 向量单元为例

text 复制代码
标量浮点单元 (FPU):
- 面积: 0.1 mm²
- 功耗: 50 mW @ 3.0 GHz
- 峰值: 1 操作/周期

AVX-512向量单元:
- 面积: 0.8 mm² (8倍)
- 功耗: 120 mW @ 3.0 GHz (2.4倍)
- 峰值: 16个单精度或8个双精度操作/周期 (16倍或8倍)

能效比:

  • 标量:50 mW / 1 操作 = 50 mW/操作
  • AVX-512单精度:120 mW / 16 操作 = 7.5 mW/操作
  • 能效提升:6.7倍

现实世界应用

科学计算

python 复制代码
# NumPy自动向量化示例
import numpy as np

# 创建1000万元素数组
x = np.random.rand(10_000_000)
y = np.random.rand(10_000_000)

# 这行代码会被编译为向量化指令
# CPU会自动使用AVX指令一次处理多个数据
z = x * 1.5 + y * 2.0

多媒体处理

视频编码/解码:一次处理多个像素

音频处理:一次处理多个采样点

图像滤镜:一次处理多个像素

机器学习

python 复制代码
# 矩阵乘法 - 高度向量化
# 假设A和B是大型矩阵
C = np.dot(A, B)  # 底层使用高度优化的向量化BLAS库

# 神经网络推理
output = relu(W @ input + b)  # @操作符使用向量化矩阵乘法

游戏引擎

cpp 复制代码
// 一次处理多个顶点
// 使用SIMD指令进行3D变换
__m128 vertex1, vertex2, vertex3, vertex4;
__m128 transformed1 = _mm_mul_ps(vertex1, transformation_matrix);
__m128 transformed2 = _mm_mul_ps(vertex2, transformation_matrix);
// ... 四个顶点同时变换

向量化的挑战

数据对齐

cpp 复制代码
// 未对齐数据 - 较慢
__m512 data = _mm512_loadu_ps(pointer);  // "u"表示unaligned

// 对齐数据 - 较快
__m512 data = _mm512_load_ps(pointer);   // 要求16字节对齐

条件分支

cpp 复制代码
// 标量代码中的if语句难以向量化
for (int i = 0; i < N; i++) {
    if (data[i] > threshold) {
        result[i] = data[i] * 2;
    } else {
        result[i] = data[i];
    }
}

// 向量化版本:使用掩码
__mmask16 mask = _mm512_cmp_ps_mask(data, threshold, _CMP_GT_OS);
result = _mm512_mask_mul_ps(data, mask, data, _mm512_set1_ps(2.0f));

数据依赖

cpp 复制代码
// 难以向量化:每个迭代依赖前一个结果
for (int i = 1; i < N; i++) {
    data[i] = data[i] + data[i-1];  // 串行依赖
}

CUDA向量化处理案例:大规模矩阵乘法优化

概述

通过float4 数据类型 和共享内存优化矩阵乘法

优化矩阵乘法 (GEMM)

问题描述

计算 C = A * B , 其中:

  • A: M * K 矩阵
  • B: K * N 矩阵
  • C: M * N 矩阵

基础CUDA实现(非向量化)

c 复制代码
// 基础版本:每个线程计算一个元素
__global__ void matrixMulBasic(float* C, const float* A, const float* B, 
                               int M, int N, int K) {
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (row < M && col < N) {
        float sum = 0.0f;
        for (int k = 0; k < K; ++k) {
            sum += A[row * K + k] * B[k * N + col];
        }
        C[row * N + col] = sum;
    }
}

问题:

  • 每个线程只计算1个浮点数
  • 全局内存访问未合并,效率低
  • 没有利用向量化加载/存储

向量化优化版本(使用float4)

c 复制代码
// 向量化版本:每个线程使用float4一次处理4个元素
#include <cuda_fp16.h>

// 定义向量类型别名
using float4 = float4;
using float2 = float2;

// 内核:使用float4向量化,每个线程计算4×4分块
__global__ void matrixMulVectorized(float* C, const float* A, const float* B,
                                    int M, int N, int K) {
    // 线程块计算的分块大小:BLOCK_SIZE × BLOCK_SIZE
    const int BLOCK_SIZE = 32;
    
    // 每个线程计算4×4的子矩阵
    const int THREAD_TILE_M = 4;
    const int THREAD_TILE_N = 4;
    
    // 线程索引
    int threadRow = threadIdx.y;
    int threadCol = threadIdx.x;
    
    // 线程块在输出矩阵C中的位置
    int blockRow = blockIdx.y * BLOCK_SIZE;
    int blockCol = blockIdx.x * BLOCK_SIZE;
    
    // 声明寄存器用于累加
    float accum[THREAD_TILE_M][THREAD_TILE_N] = {0.0f};
    
    // 循环遍历K维度
    for (int k = 0; k < K; k += 4) {  // 每次处理4个元素
        // 声明共享内存
        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
        
        // 使用float4加载数据到共享内存(向量化加载)
        // 每个线程加载4个float到共享内存
        
        // 加载矩阵A的分块(float4向量化)
        if (threadRow < BLOCK_SIZE && (k + threadCol) < K) {
            int A_row = blockRow + threadRow;
            int A_col = k + threadCol;
            
            if (A_row < M && (A_col + 3) < K) {
                // 向量化加载:一次加载4个float
                float4 A_vec = *reinterpret_cast<const float4*>(&A[A_row * K + A_col]);
                As[threadRow][threadCol] = A_vec.x;
                As[threadRow][threadCol + 1] = A_vec.y;
                As[threadRow][threadCol + 2] = A_vec.z;
                As[threadRow][threadCol + 3] = A_vec.w;
            } else {
                // 边界处理:非向量化加载
                for (int i = 0; i < 4; i++) {
                    if ((A_col + i) < K) {
                        As[threadRow][threadCol + i] = A[A_row * K + A_col + i];
                    }
                }
            }
        }
        
        // 加载矩阵B的分块(float4向量化)
        if (threadCol < BLOCK_SIZE && (k + threadRow) < K) {
            int B_row = k + threadRow;
            int B_col = blockCol + threadCol;
            
            if ((B_row + 3) < K && B_col < N) {
                // 向量化加载:一次加载4个float
                // 注意:需要处理B的列主序访问模式
                float4 B_vec = make_float4(
                    B[B_row * N + B_col],
                    B[(B_row + 1) * N + B_col],
                    B[(B_row + 2) * N + B_col],
                    B[(B_row + 3) * N + B_col]
                );
                Bs[threadRow][threadCol] = B_vec.x;
                Bs[threadRow + 1][threadCol] = B_vec.y;
                Bs[threadRow + 2][threadCol] = B_vec.z;
                Bs[threadRow + 3][threadCol] = B_vec.w;
            } else {
                // 边界处理
                for (int i = 0; i < 4; i++) {
                    if ((B_row + i) < K) {
                        Bs[threadRow + i][threadCol] = B[(B_row + i) * N + B_col];
                    }
                }
            }
        }
        
        __syncthreads();  // 等待共享内存加载完成
        
        // 计算分块矩阵乘法(4×4 × 4×4)
        for (int kk = 0; kk < 4; kk++) {
            // 从共享内存读取(标量)
            float A_val = As[threadRow * THREAD_TILE_M][kk];
            float B_val = Bs[kk][threadCol * THREAD_TILE_N];
            
            // 展开循环,手动计算4×4
            accum[0][0] += A_val * B_val;
            accum[0][1] += A_val * Bs[kk][threadCol * THREAD_TILE_N + 1];
            accum[0][2] += A_val * Bs[kk][threadCol * THREAD_TILE_N + 2];
            accum[0][3] += A_val * Bs[kk][threadCol * THREAD_TILE_N + 3];
            
            // 重复其他行...
        }
        
        __syncthreads();  // 等待所有线程完成计算
    }
    
    // 将结果写回全局内存(使用float4向量化存储)
    for (int i = 0; i < THREAD_TILE_M; i++) {
        int row = blockRow + threadRow * THREAD_TILE_M + i;
        if (row < M) {
            for (int j = 0; j < THREAD_TILE_N; j += 4) {
                int col = blockCol + threadCol * THREAD_TILE_N + j;
                if (col + 3 < N) {
                    // 向量化存储:一次存储4个float
                    float4 C_vec;
                    C_vec.x = accum[i][j];
                    C_vec.y = accum[i][j + 1];
                    C_vec.z = accum[i][j + 2];
                    C_vec.w = accum[i][j + 3];
                    *reinterpret_cast<float4*>(&C[row * N + col]) = C_vec;
                } else {
                    // 边界处理:标量存储
                    for (int jj = 0; jj < 4 && (col + jj) < N; jj++) {
                        C[row * N + col + jj] = accum[i][j + jj];
                    }
                }
            }
        }
    }
}

更高级的向量化版本(使用LDG.128和Tensor Core)

c 复制代码
// 使用CUDA 11+的特性:LDG.128加载和mma指令
#include <cuda_bf16.h>
#include <cuda_fp16.h>
#include <cuda_pipeline.h>

// 使用half2(2个半精度浮点数)进行向量化
__global__ void matrixMulHalf2(half* C, const half* A, const half* B,
                               int M, int N, int K) {
    // 每个线程使用half2处理两个半精度数
    const int TILE_M = 16;
    const int TILE_N = 16;
    const int TILE_K = 16;
    
    // 线程块和线程索引
    int bx = blockIdx.x, by = blockIdx.y;
    int tx = threadIdx.x, ty = threadIdx.y;
    
    // 声明共享内存
    __shared__ half As[TILE_M][TILE_K];
    __shared__ half Bs[TILE_K][TILE_N];
    
    // 计算线程块处理的子矩阵位置
    int row = by * TILE_M + ty;
    int col = bx * TILE_N + tx * 2;  // 每个线程处理2个元素
    
    // 寄存器累加器
    half2 accum[2][2] = {{make_half2(0.0f, 0.0f), make_half2(0.0f, 0.0f)},
                         {make_half2(0.0f, 0.0f), make_half2(0.0f, 0.0f)}};
    
    // 循环K维度
    for (int k = 0; k < K; k += TILE_K) {
        // 使用向量化加载到共享内存
        // 每个线程加载2个half(即一个half2)
        if (row < M && (k + tx) < K) {
            half2 A_vec = *reinterpret_cast<const half2*>(&A[row * K + k + tx * 2]);
            As[ty][tx * 2] = __low2half(A_vec);
            As[ty][tx * 2 + 1] = __high2half(A_vec);
        }
        
        if ((k + ty) < K && col < N) {
            // 注意B的访问模式:需要转置思维
            half2 B_vec0 = make_half2(B[(k + ty) * N + col],
                                      B[(k + ty) * N + col + 1]);
            Bs[ty][tx * 2] = __low2half(B_vec0);
            Bs[ty][tx * 2 + 1] = __high2half(B_vec0);
        }
        
        __syncthreads();
        
        // 计算分块乘法(使用half2向量化计算)
        for (int kk = 0; kk < TILE_K; kk++) {
            half2 A_val0 = make_half2(As[ty * 2][kk], As[ty * 2 + 1][kk]);
            half2 A_val1 = make_half2(As[ty * 2 + 2][kk], As[ty * 2 + 3][kk]);
            
            half2 B_val0 = make_half2(Bs[kk][tx * 2], Bs[kk][tx * 2 + 1]);
            half2 B_val1 = make_half2(Bs[kk][tx * 2 + 2], Bs[kk][tx * 2 + 3]);
            
            // half2乘加:__hfma2(a, b, c) 计算 a*b + c
            accum[0][0] = __hfma2(A_val0, __half2half2(B_val0.x), accum[0][0]);
            accum[0][1] = __hfma2(A_val0, __half2half2(B_val0.y), accum[0][1]);
            accum[1][0] = __hfma2(A_val1, __half2half2(B_val1.x), accum[1][0]);
            accum[1][1] = __hfma2(A_val1, __half2half2(B_val1.y), accum[1][1]);
        }
        
        __syncthreads();
    }
    
    // 存储结果(向量化存储)
    if (row < M && col < N) {
        int c_index = row * N + col;
        *reinterpret_cast<half2*>(&C[c_index]) = accum[0][0];
        if (col + 2 < N) {
            *reinterpret_cast<half2*>(&C[c_index + 2]) = accum[0][1];
        }
        if (row + 2 < M) {
            *reinterpret_cast<half2*>(&C[c_index + N * 2]) = accum[1][0];
            if (col + 2 < N) {
                *reinterpret_cast<half2*>(&C[c_index + N * 2 + 2]) = accum[1][1];
            }
        }
    }
}

完整的性能测试程序

c 复制代码
#include <iostream>
#include <chrono>
#include <cstdlib>

// 主机端验证函数
void verifyMatrixMultiplication(const float* C_cpu, const float* C_gpu, 
                                int M, int N, float tolerance = 1e-4f) {
    int errors = 0;
    for (int i = 0; i < M * N; i++) {
        if (fabs(C_cpu[i] - C_gpu[i]) > tolerance) {
            errors++;
            if (errors < 10) {
                std::cout << "Mismatch at index " << i << ": CPU=" 
                          << C_cpu[i] << ", GPU=" << C_gpu[i] << std::endl;
            }
        }
    }
    if (errors == 0) {
        std::cout << "Verification PASSED!" << std::endl;
    } else {
        std::cout << "Verification FAILED! " << errors << " errors found." << std::endl;
    }
}

// 主机端矩阵乘法(参考实现)
void matrixMulCPU(float* C, const float* A, const float* B, 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;
        }
    }
}

// 主函数:性能对比测试
int main() {
    // 矩阵尺寸
    const int M = 1024;
    const int N = 1024;
    const int K = 1024;
    
    // 分配主机内存
    float* h_A = new float[M * K];
    float* h_B = new float[K * N];
    float* h_C_basic = new float[M * N];
    float* h_C_vectorized = new float[M * N];
    float* h_C_cpu = new float[M * N];
    
    // 初始化数据
    for (int i = 0; i < M * K; i++) h_A[i] = static_cast<float>(rand()) / RAND_MAX;
    for (int i = 0; i < K * N; i++) h_B[i] = static_cast<float>(rand()) / RAND_MAX;
    
    // 分配设备内存
    float *d_A, *d_B, *d_C_basic, *d_C_vectorized;
    cudaMalloc(&d_A, M * K * sizeof(float));
    cudaMalloc(&d_B, K * N * sizeof(float));
    cudaMalloc(&d_C_basic, M * N * sizeof(float));
    cudaMalloc(&d_C_vectorized, M * N * sizeof(float));
    
    // 拷贝数据到设备
    cudaMemcpy(d_A, h_A, M * K * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_B, h_B, K * N * sizeof(float), cudaMemcpyHostToDevice);
    
    // 定义线程块和网格大小
    dim3 blockSizeBasic(16, 16);
    dim3 gridSizeBasic((N + blockSizeBasic.x - 1) / blockSizeBasic.x,
                       (M + blockSizeBasic.y - 1) / blockSizeBasic.y);
    
    dim3 blockSizeVec(8, 8);  // 每个线程计算4×4,所以块大小减半
    dim3 gridSizeVec((N / 4 + blockSizeVec.x - 1) / blockSizeVec.x,
                     (M / 4 + blockSizeVec.y - 1) / blockSizeVec.y);
    
    // 创建CUDA事件用于计时
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    float milliseconds = 0.0f;
    
    // 测试基础版本
    std::cout << "=== Testing Basic Implementation ===" << std::endl;
    cudaEventRecord(start);
    for (int i = 0; i < 10; i++) {
        matrixMulBasic<<<gridSizeBasic, blockSizeBasic>>>(d_C_basic, d_A, d_B, M, N, K);
    }
    cudaEventRecord(stop);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&milliseconds, start, stop);
    std::cout << "Basic kernel average time: " << milliseconds / 10.0f << " ms" << std::endl;
    
    // 测试向量化版本
    std::cout << "\n=== Testing Vectorized Implementation ===" << std::endl;
    cudaEventRecord(start);
    for (int i = 0; i < 10; i++) {
        matrixMulVectorized<<<gridSizeVec, blockSizeVec>>>(d_C_vectorized, d_A, d_B, M, N, K);
    }
    cudaEventRecord(stop);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&milliseconds, start, stop);
    std::cout << "Vectorized kernel average time: " << milliseconds / 10.0f << " ms" << std::endl;
    
    // 拷贝结果回主机
    cudaMemcpy(h_C_basic, d_C_basic, M * N * sizeof(float), cudaMemcpyDeviceToHost);
    cudaMemcpy(h_C_vectorized, d_C_vectorized, M * N * sizeof(float), cudaMemcpyDeviceToHost);
    
    // CPU参考计算
    std::cout << "\n=== Running CPU Reference ===" << std::endl;
    auto cpu_start = std::chrono::high_resolution_clock::now();
    matrixMulCPU(h_C_cpu, h_A, h_B, M, N, K);
    auto cpu_end = std::chrono::high_resolution_clock::now();
    auto cpu_duration = std::chrono::duration_cast<std::chrono::milliseconds>(cpu_end - cpu_start);
    std::cout << "CPU time: " << cpu_duration.count() << " ms" << std::endl;
    
    // 验证结果
    std::cout << "\n=== Verification ===" << std::endl;
    std::cout << "Verifying basic kernel: ";
    verifyMatrixMultiplication(h_C_cpu, h_C_basic, M, N);
    
    std::cout << "Verifying vectorized kernel: ";
    verifyMatrixMultiplication(h_C_cpu, h_C_vectorized, M, N);
    
    // 计算性能提升
    cudaEventRecord(start);
    matrixMulBasic<<<gridSizeBasic, blockSizeBasic>>>(d_C_basic, d_A, d_B, M, N, K);
    cudaEventRecord(stop);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&milliseconds, start, stop);
    float basic_time = milliseconds;
    
    cudaEventRecord(start);
    matrixMulVectorized<<<gridSizeVec, blockSizeVec>>>(d_C_vectorized, d_A, d_B, M, N, K);
    cudaEventRecord(stop);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&milliseconds, start, stop);
    float vectorized_time = milliseconds;
    
    std::cout << "\n=== Performance Summary ===" << std::endl;
    std::cout << "Speedup (Vectorized vs Basic): " << basic_time / vectorized_time << "x" << std::endl;
    std::cout << "Speedup (Vectorized vs CPU): " << cpu_duration.count() / vectorized_time << "x" << std::endl;
    
    // 计算理论带宽利用率
    // 矩阵乘法计算量:2 * M * N * K FLOPS
    double total_flops = 2.0 * M * N * K;
    double vectorized_gflops = (total_flops / (vectorized_time * 1e-3)) / 1e9;
    double basic_gflops = (total_flops / (basic_time * 1e-3)) / 1e9;
    
    std::cout << "\n=== GFLOPS ===" << std::endl;
    std::cout << "Basic kernel: " << basic_gflops << " GFLOPS" << std::endl;
    std::cout << "Vectorized kernel: " << vectorized_gflops << " GFLOPS" << std::endl;
    std::cout << "Performance improvement: " << vectorized_gflops / basic_gflops << "x" << std::endl;
    
    // 清理资源
    delete[] h_A;
    delete[] h_B;
    delete[] h_C_basic;
    delete[] h_C_vectorized;
    delete[] h_C_cpu;
    
    cudaFree(d_A);
    cudaFree(d_B);
    cudaFree(d_C_basic);
    cudaFree(d_C_vectorized);
    
    cudaEventDestroy(start);
    cudaEventDestroy(stop);
    
    return 0;
}

向量化优化的关键技巧

数据对齐

c 复制代码
// 使用对齐的内存分配
cudaMalloc(&d_A, M * K * sizeof(float));  // 默认可能不对齐
cudaMalloc(&d_A, M * K * sizeof(float) + 16);  // 手动对齐
float* d_A_aligned = (float*)(((size_t)d_A + 15) & ~15);

使用向量化数据类型

c 复制代码
// CUDA内置的向量化类型
float1, float2, float4  // 1, 2, 4个float
int2, int4              // 2, 4个int
double2                 // 2个double
ushort4                 // 4个unsigned short

向量化内存访问模式

c 复制代码
// 好的访问模式:连续、对齐
float4 val = *reinterpret_cast<float4*>(&global_mem[index]);

// 坏的访问模式:分散访问
float4 val;
val.x = global_mem[index1];
val.y = global_mem[index2];  // 不连续!

编译指导

c 复制代码
// 使用编译指导提示向量化
#pragma unroll 4  // 告诉编译器展开循环4次
for (int i = 0; i < N; i += 4) {
    // 向量化操作
}
相关推荐
一切尽在,你来2 小时前
C++ 零基础教程 - 第 7 讲 bool运算符和选择结构教程
c++
ffqws_2 小时前
进阶搜索:迭代加深搜索(IDS)埃及分数题解
算法·迭代加深
1104.北光c°2 小时前
【黑马点评项目笔记 | 商户查询缓存篇】基于Redis解决缓存穿透、雪崩、击穿三剑客
java·开发语言·数据库·redis·笔记·spring·缓存
格林威2 小时前
相机的“对焦”和“变焦”,这二者有什么区别?
开发语言·人工智能·数码相机·opencv·算法·计算机视觉·视觉检测
LXS_3572 小时前
常用算法(下)---拷贝、替换、算术生成、集合算法
开发语言·c++·算法·学习方法
忘忧记2 小时前
用 Python 30 分钟做出自己的记事本
开发语言·python
历程里程碑2 小时前
Linux19 实现shell基本功能
linux·运维·服务器·算法·elasticsearch·搜索引擎·哈希算法
阿萨德528号2 小时前
MyBatis OGNL 表达式陷阱:Integer类型字段使用“xxx!= ‘‘”时判断失效
java·开发语言·mybatis
鲨辣椒100862 小时前
算法也能降低时间复杂度???—————算法延伸
数据结构·算法·排序算法