例子: 图像亮度调整
假设我们有一张黑白照片,需要将每个像素的亮度增加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) {
// 向量化操作
}