Skip to content

Repository files navigation

Mandelbrot Compute Lab

一个围绕 Mandelbrot 集合构建的异构并行计算与连续渲染实验。项目把同一个逐像素递推任务映射到单线程 CPU、OpenMP、多 Web Worker、OpenCL、CUDA 和 WebGPU,用可视化结果与可复现的原生 kernel benchmark 观察不同执行模型的吞吐、精度和调度特征。

在线体验:https://so-cean.github.io/MandelbrotSet-OpenCL/

项目背景与意义

Mandelbrot 集合由复数递推定义:

[ z_{n+1}=z_n^2+c,\qquad z_0=0 ]

图像中的每个像素对应一个不同的参数 c。像素之间没有数据依赖,单个像素内部则存在连续迭代,因此它是一个很适合研究并行计算的 workload:

  • 可以直接把二维图像映射为线程网格;
  • 计算以乘加和比较为主,内存流量较低;
  • 不同像素逃逸时间不同,会产生真实的 SIMD/SIMT 分支发散;
  • 分辨率和迭代上限可以连续调节工作量;
  • 计算结果能够立即可视化,容易发现坐标、精度或并行实现错误。

项目最初比较 C++ 单线程、OpenMP 和 OpenCL,随后加入 CUDA 原生实现与 WebGPU 连续渲染。Web 版本让访问者无需安装程序,直接在自己的 CPU/GPU 上观察实时结果;原生 benchmark 则隔离浏览器刷新、颜色映射和显示开销,测量 accelerator kernel 本身。

flowchart LR
    W["Shared Mandelbrot workload"] --> C["CPU execution"]
    W --> G["GPU execution"]
    C --> C1["C++ / OpenMP"]
    C --> C2["Web Workers"]
    G --> G1["CUDA"]
    G --> G2["OpenCL"]
    G --> G3["WebGPU / WGSL"]
    C1 --> V["Correctness + benchmark"]
    C2 --> V
    G1 --> V
    G2 --> V
    G3 --> V
Loading

并行计算实现

CPU

  • 原生 C++ 单线程实现作为基础参考;
  • OpenMP 按二维像素空间并行;
  • Web 端使用独立 Worker 池,按行分片并行计算;
  • CPU 与 GPU 各自维护连续循环,较慢的一方不会阻塞另一方。

CUDA

独立 CUDA benchmark 使用二维 grid/block,每个 CUDA thread 负责一个像素:

const int x = blockIdx.x * blockDim.x + threadIdx.x;
const int y = blockIdx.y * blockDim.y + threadIdx.y;

实现包含完整的设备选择、显存分配、二维 kernel launch、预热、CUDA Event 设备计时、错误检查和最终结果校验。输出按 row-major 顺序连续写入;每帧只提交一个融合 kernel,不在迭代循环中往返主机。

当前 standalone benchmark 位于 benchmarks/native/cuda_benchmark.cu。项目还保留了早期 CUDA/OpenGL 版本,并关联独立仓库 Dylan8527/Mandelbrot-set-GPU

OpenCL

OpenCL 路径采用对应的二维 NDRange:一个 work-item 计算一个像素。独立 benchmark 通过系统 OpenCL.dll 完成平台/设备枚举、运行时编译、command queue 提交和 event profiling,不依赖 Python OpenCL 第三方包。

测试程序会优先选择 NVIDIA GPU,并打印实际 platform、device、driver 与 OpenCL 版本,避免把核显结果误认为独显结果。实现见 benchmarks/native/opencl_benchmark.py

WebGPU

Web 端使用 WebGPU + WGSL,在浏览器选择的真实 GPU 上持续渲染。GPU 路径使用 fragment shader 将像素计算直接写入 presentation canvas,避免完整中间纹理的额外复制;CPU 路径使用多个 Web Worker。两者共享 viewport、迭代上限、配色与时间驱动的 zoom 轨迹,但独立统计完成帧率。

浏览器无法像 CUDA 一样枚举并指定物理 GPU,因此页面会显示实际 adapter。Windows 多 GPU 机器应确认页面选中了目标独显。

深度缩放与数值精度

直接 FP32 在像素间距小于中心坐标附近的 ULP 后会失去相邻像素差异。项目因此把普通渲染和深度路径分开:

  1. 普通尺度直接使用 FP32,保持最高吞吐;
  2. CPU 以任意精度整数生成一条 reference orbit;
  3. GPU 对各像素计算相对 reference 的扰动递推;
  4. 很小的扰动使用“FP32 尾数 + 独立二进制指数”表达,避免 scale 先在普通 FP32 中下溢。

扰动关系为:

[ \delta_{n+1}=2Z_n\delta_n+\delta_n^2+\Delta c ]

这样高精度成本集中在一条参考轨道,大量像素仍由 GPU 的 FP32 单元并行处理,而不是让每个像素执行昂贵的 FP64 或多 limb 大数算术。深度路径、viewport 和 CPU/GPU 对齐测试位于 docs/js/precision/docs/tests/

平滑渲染与交互

  • 连续归一化逃逸值降低整数 iteration 色带;
  • 多套循环调色板由 CPU/GPU 共用同一 LUT;
  • GPU 使用纹理过滤完成平滑采样;
  • 分辨率直接改变 canvas backing size,支持最高 4K;
  • zoom 由实际经过时间决定,不依赖某个 backend 的帧数;
  • Resource budget 通过工作/空闲占空比限制持续提交频率,它是 workload budget,不是操作系统 GPU 利用率保证值。

原生 CPU 多线程 scaling

CPU benchmark 使用持久线程池,不把线程创建计入每帧时间;每个 worker 从原子队列领取 4 行 tile,以平衡 Mandelbrot 不同区域逃逸次数不均造成的负载差异。测试运行在 Intel Core i7-14700(20 核、28 逻辑线程),使用 MSVC /O2 /arch:AVX2 /fp:precise,固定 4K、FP32、256 maximum iterations。

Intel Core i7-14700 native CPU thread scaling

Threads Median frame P95 frame FPS Speedup Efficiency
1 392.093 ms 519.893 ms 2.55 1.00× 100.0%
2 224.990 ms 266.060 ms 4.45 1.74× 87.1%
4 110.857 ms 130.084 ms 9.02 3.54× 88.4%
8 65.349 ms 68.744 ms 15.30 6.00× 75.0%
16 36.891 ms 38.539 ms 27.11 10.63× 66.4%
20 30.404 ms 32.702 ms 32.89 12.90× 64.5%
28 25.864 ms 27.953 ms 38.66 15.16× 54.1%

1→28 threads 将吞吐从 2.55 FPS 提升到 38.66 FPS,获得 15.16× speedup。20→28 threads 仍有收益,但效率下降反映了混合 P/E 核、SMT、调度和有限并行效率。所有线程配置产生相同 checksum,说明动态 tile 组装是确定性的。原始输出见 cpu-scaling-i7-14700-2026-08-06.md

原生 GPU benchmark

以下结果不是网页 FPS。它们来自独立 CUDA/OpenCL 程序,只统计 Mandelbrot kernel 的设备执行时间;初始化、kernel 编译、显存分配和最终校验回读均不计入。Kernel FPS1000 / median kernel ms 得到的吞吐等价值,不受显示器刷新率约束,也不等同于端到端渲染 FPS。

测试环境

  • GPU:NVIDIA GeForce RTX 4060 Ti,Compute Capability 8.9;
  • Driver:610.88;
  • CUDA:13.0 / NVCC 13.0.88,目标 sm_89
  • OpenCL:OpenCL 3.0 CUDA;
  • 数据类型:FP32;
  • viewport:center = 0 + 1iscale = 0.25
  • maximum iterations:256;
  • block/workgroup:16×16;
  • 10 帧预热,100 帧采样;
  • 三档分辨率保持 16:9,确保视域与平均迭代量可比。

结果

RTX 4060 Ti native CUDA and OpenCL Mandelbrot benchmark

Backend Resolution Median kernel P95 kernel Kernel FPS Giter/s
CUDA 1280×720 0.050 ms 0.051 ms 20,000 436.7
OpenCL 1280×720 0.0437 ms 0.0451 ms 22,894 500.5
CUDA 1920×1080 0.094 ms 0.095 ms 10,638 518.4
OpenCL 1920×1080 0.0819 ms 0.0829 ms 12,207 596.5
CUDA 3840×2160 0.339 ms 0.352 ms 2,950 571.5
OpenCL 3840×2160 0.2877 ms 0.3000 ms 3,475 675.1

这组结果支持两个观察:

  1. 分辨率提高后 GPU 并行度更充分,迭代吞吐从约 437–501 Giter/s 上升到约 572–675 Giter/s;
  2. 该 kernel 没有共享内存 tiling、warp primitive 或 CUDA 专用数据复用,CUDA 与 OpenCL 处于同一性能量级。当前 OpenCL 结果快约 14%–18%,说明 API 名称本身并不保证性能,编译器生成代码和具体 kernel 结构更重要。

CUDA/OpenCL 的总 iteration checksum 差异低于 0.006%。两者都使用 FP32,但编译器可能采用不同的 FMA contraction 和浮点指令选择,因此边界附近少量像素的逃逸次数可能不同。程序会输出 checksum 和平均迭代数,便于回归检查。

完整计时口径、环境安装和复现命令见 benchmarks/native/README.md,本次原始输出保存在 benchmarks/native/results/rtx4060ti-2026-08-06.md

Nsight Compute 分析

对 4K CUDA launch 使用 Nsight Compute 2025.4.1 采集硬件计数器:

Metric Value 结论
Compute (SM) throughput 93.08% kernel 活跃期间已充分使用 SM,属于 compute-bound
DRAM throughput 17.66% 不是显存带宽瓶颈
Achieved occupancy 83.10% 理论值 100%;主要损失来自 warp/workload imbalance
Branch efficiency 98.75% early-exit 分支不是当前 viewport 的主要瓶颈
Active threads / warp 29.19 / 32 大部分 lane 在迭代期间保持活跃
Eligible warps / scheduler 5.49 有足够 warps 隐藏执行延迟
Issued warps / scheduler 0.94 接近每 scheduler cycle 发射一个 warp
Registers / thread 20 register pressure 不高

这个结果解释了为什么 Windows 任务管理器可能显示较低或波动的 GPU 百分比:被测 kernel 只有约 0.3–0.4 ms,系统图表会按时间窗口平均,而且可能默认显示 3D engine;在 kernel 真正执行的时间段内,Nsight 测得 SM throughput 为 93.08%。

Nsight 同时发现 11.22M fused 与 43.42M non-fused FP32 instructions,并给出约 19.85% 的局部 FMA 优化机会。该数字是 profiler estimate,不是保证值;显式 FMA 会改变边界附近的 FP32 舍入,因此后续优化必须同时报告性能和像素/checksum 差异。完整分析见 nsight-analysis-rtx4060ti-2026-08-06.md,原始 metrics 位于 nsight-details.csv

工程重点

方向 项目中的实现
Workload decomposition 将二维像素域映射到 OpenMP iteration、Worker tile、OpenCL NDRange 和 CUDA grid/block
Kernel design 每像素独立递推、单次融合 dispatch、连续 row-major 输出,并分析边界区域的控制流发散
Performance measurement 设备 event 计时、预热、median/P95、kernel/wall time 分离、CPU scaling、Mpixel/s 与实际 Giter/s
GPU profiling Nsight Compute 的 SM/DRAM throughput、occupancy、warp scheduler、branch efficiency 与 instruction 分析
Correctness 确定性 workload、checksum、CPU/GPU 离屏逐像素比较、直接算法与 deep perturbation 交叉验证
Numerical precision FP32 精度极限检测、任意精度 reference orbit、尾数/指数分离和 GPU perturbation
Reproducibility 固定 CUDA/driver/workload 元数据、Conda 工具链、自动构建脚本、原始 benchmark 记录和测试
Deployment 同一数学任务同时提供原生计算基线与 GitHub Pages WebGPU 实时演示

构建与运行

Web Demo

需要支持 WebGPU 的当前 Chrome 或 Edge:

npm test
npm run serve

打开 http://localhost:8080/。Windows 上可用下面的命令启动独立 Chrome profile 并请求高性能 GPU:

npm run chrome:gpu

Native OpenCL benchmark

python benchmarks/native/opencl_benchmark.py --frames 100

Native CPU scaling benchmark

.\benchmarks\native\build_cpu.ps1
.\benchmarks\native\cpu_scaling_benchmark.exe 30 5

Native CUDA benchmark

Windows 需要 CUDA Toolkit/NVCC 和 Visual Studio C++ Build Tools。仓库提供自动定位 Conda CUDA 环境与 MSVC 的构建脚本:

conda create -y -n mandelbrot-cuda -c nvidia cuda-nvcc=13.0.88 cuda-cudart-dev=13.0.96
.\benchmarks\native\build_cuda.ps1
.\benchmarks\native\cuda_benchmark.exe 100

管理员 PowerShell 中可运行 Nsight Compute profile:

.\benchmarks\native\profile_cuda.ps1

早期原生应用

原始 C++/OpenMP/OpenCL/OpenGL 程序使用 CMake + vcpkg:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

项目结构

MandelbrotSet-OpenCL/
├── main.cpp / main_openmp.cpp / main_opencl.cpp
│   └── 原生 CPU、OpenMP 与 OpenCL 实现
├── dist/*.cu
│   └── CUDA/OpenGL 实验实现
├── benchmarks/native/
│   ├── cpu_scaling_benchmark.cpp
│   ├── cuda_benchmark.cu
│   ├── opencl_benchmark.py
│   ├── build_cpu.ps1 / build_cuda.ps1
│   └── profile_cuda.ps1 / results/
├── docs/
│   ├── js/ CPU/GPU backend 与 Worker 实现
│   ├── js/precision 深度缩放实现
│   ├── shaders WebGPU/WGSL shader
│   └── tests CPU/GPU、viewport 与精度测试
├── tools/run-chrome-high-performance.ps1
└── .github/workflows/pages.yml

演示动画

Mandelbrot animation 1 Mandelbrot animation 2 Mandelbrot animation 3

References

部署

推送到 main 后,GitHub Actions 会运行测试并部署 docs/ 到 GitHub Pages。

About

Mandelbrot Set Visualization and Benchmark

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages