You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGPU Verlet积分计算着色器性能劣于CPU的优化问询

WebGPU计算着色器vs CPU粒子模拟性能异常问题

我在对比WebGPU计算着色器与CPU等效实现的Verlet积分粒子模拟时,得到了意外结果——CPU速度居然是GPU的10倍,二者模拟结果均正确。

CPU版本实现(Verlet积分)

IntegrateVerlet(dt: number) {
    console.time("verlet");
    const count = this.positions.length / 4;
    /* eslint-disable */
    for (let i = 0; i < count; i++) {
        const x = this.positions[i * 4 + 0];
        const y = this.positions[i * 4 + 1];
        const z = this.positions[i * 4 + 2];
        const dx = x! - this.prevPositions[i * 4 + 0]!;
        const dy = y! - this.prevPositions[i * 4 + 1]!;
        const dz = z! - this.prevPositions[i * 4 + 2]!;
        const ax = this.forces[i * 4 + 0]! / this.mass;
        const ay = this.forces[i * 4 + 1]! / this.mass;
        const az = this.forces[i * 4 + 2]! / this.mass;

        this.prevPositions[i * 4 + 0] = x!;
        this.prevPositions[i * 4 + 1] = y!;
        this.prevPositions[i * 4 + 2] = z!;

        this.positions[i * 4 + 0] = x! + dx + ax! * dt * dt;
        this.positions[i * 4 + 1] = y! + dy + ay! * dt * dt;
        this.positions[i * 4 + 2] = z! + dz + az! * dt * dt;
    }
    /* eslint-enable */
    console.timeEnd("verlet");
}

WebGPU计算着色器实现

@group(0) @binding(0) var<storage, read_write> positions: array<vec3<f32>>;
@group(0) @binding(1) var<storage, read_write> prevPositions: array<vec3<f32>>;
@group(0) @binding(2) var<storage, read> forces: array<vec3<f32>>;
@group(0) @binding(3) var<uniform> mass: f32;
@group(0) @binding(4) var<uniform> dt: f32;

@compute @workgroup_size(${workgroupSize})
fn main(
        @builtin(workgroup_id) workgroup_id : vec3<u32>,
        @builtin(local_invocation_index) local_invocation_index: u32,
        @builtin(num_workgroups) num_workgroups: vec3<u32>
){
    // workgroup_index is similar to local_invocation_index except for
    // workgroups, not threads inside a workgroup.
    // It is not a builtin so we compute it ourselves.

    let workgroup_index =  
       workgroup_id.x +
       workgroup_id.y * num_workgroups.x +
       workgroup_id.z * num_workgroups.x * num_workgroups.y;

    // global_invocation_index is like local_invocation_index
    // except linear across all invocations across all dispatched
    // workgroups. It is not a builtin so we compute it ourselves.

    let i =
       workgroup_index * ${numThreadsPerWorkgroup} +
       local_invocation_index;

    if (i < arrayLength(&positions)) {
        let dPos = positions[i] - prevPositions[i];
        let a = forces[i] / mass;

        prevPositions[i] = positions[i];

        positions[i] = positions[i] + dPos + a * dt * dt;
    }
}

测试环境与结果

  • 粒子数量:2000×2000=400万个3D向量
  • 当前GPU配置:
    workgroupSize = 8,8,4
    dispatchCount = 25,25,25
    
  • 性能对比:
avg cpu integration timeavg gpu integration time
16 ms200 ms
  • 硬件:7800x3D + RTX4090
  • 软件:Microsoft Edge 125.0.2535.92(64位正式版)

性能瓶颈分析

  1. 工作组配置不合理:RTX4090的SM更适配一维、线程数为32倍数的工作组(比如256线程),当前三维工作组会增加索引计算开销,且不利于GPU调度,无法最大化SM占用率。
  2. 线程索引计算冗余:手动计算全局索引的逻辑会占用每个线程的执行周期,WebGPU提供了原生的全局索引内置变量,完全可以替代手动计算。
  3. 内存访问效率低:三维索引映射可能导致线程访问的内存地址不连续,引发存储体冲突,降低内存带宽利用率;而CPU代码是连续线性访问,缓存命中率极高。
  4. 计时精度问题:用console.time统计GPU时间可能包含了JS命令调度、队列等待的额外开销,没有精准测量计算着色器的实际执行时间。

优化调整方案

  • 改用一维工作组:将工作组大小设为@workgroup_size(256),调度数量计算为Math.ceil(4000000 / 256) = 15625,这样能提升GPU SM的占用率,简化索引逻辑。
  • 简化全局索引计算:直接使用内置变量@builtin(global_invocation_id)获取索引,替换手动计算:
    @compute @workgroup_size(256)
    fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {
        let i = global_id.x;
        if (i < arrayLength(&positions)) {
            let dPos = positions[i] - prevPositions[i];
            let a = forces[i] / mass;
            prevPositions[i] = positions[i];
            positions[i] = positions[i] + dPos + a * dt * dt;
        }
    }
    
  • 预计算常量:在CPU端计算dtSquared = dt * dt,作为uniform传入GPU,避免每个线程重复执行乘法运算。
  • 精准GPU计时:使用WebGPU的timestamp-query扩展来测量计算着色器的实际执行时间,排除调度等额外开销。
  • 启用性能调试:用Edge DevTools的WebGPU性能分析工具,查看工作组调度、内存访问、SM占用率等指标,定位具体瓶颈。

内容的提问来源于stack exchange,提问作者Victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:07:02