|
1 | | -# mopro msm gpu-acceleration |
| 1 | +# Metal MSM |
2 | 2 |
|
3 | | -We are researching and implementing methods to accelerate multi-scalar multiplication (MSM) on IOS mobile device. |
| 3 | +Metal-MSM v2 executes MSM on [BN254](https://hackmd.io/@jpw/bn254) curve on Apple GPUs using Metal Shading Language (MSL). Unlike v1, which naively split the work into smaller tasks, v2 takes [Tal and Koh’s WebGPU MSM](https://github.com/z-prize/2023-entries/tree/main/prize-2-msm-wasm/webgpu-only/tal-derei-koh-wei-jie) in ZPrize2023 and the cuZK [[LWY+23](https://eprint.iacr.org/2022/1321)] approach as reference. |
4 | 4 |
|
5 | | -## mopro-msm |
| 5 | +By adopting sparse matrices, it improves the Pippenger algorithm [Pip76](https://dl.acm.org/doi/10.1109/SFCS.1976.21) with a more memory-efficient storage format and uses well-studied sparse matrix algorithms, such as sparse matrix–vector multiplication and sparse matrix transposition, in both the preprocessing phase (e.g., radix sort via sparse matrix transpose) and the bucket-accumulation phase to achieve high parallelism. |
6 | 6 |
|
7 | | -This is a of various implementations of MSM functions, which are then integrated in `mopro-core`. |
| 7 | +We took the WebGPU MSM reference and tuned it for all scales by auto-adjusting workgroup sizes for each cuZK shaders with SIMD width and the amount of GPU cores, squeezing out better GPU utilization. Plus, with dynamic window sizes, we speed up small and medium inputs (2^14 – 2^18) by eliminating unused sparse-matrix columns. |
8 | 8 |
|
9 | | -### Run benchmark on the laptop |
10 | | -Currently we support these MSM algorithms on BN254: |
11 | | -- arkworks_pippenger |
12 | | -- bucket_wise_msm |
13 | | -- precompute_msm |
14 | | -- metal::msm (GPU) |
| 9 | +One thing to highlight is that our implementation runs most computations on the GPU, but it’s still slower than the CPU-only solution like [Arkworks](https://github.com/arkworks-rs). However, because we target client-side devices with limited resources, applying a hybrid approach, leveraging both CPU and GPU for MSM tasks and combining the results at the end, can yield an implementation slightly faster than a pure-CPU one. Check the write-up below for estimated speedups with this hybrid method. |
15 | 10 |
|
16 | | -Replace `MSM_ALGO` with one of the algorithms above to get the corresponding benchmarks. |
| 11 | +## How to use |
17 | 12 |
|
18 | | -Benchmarking for <u>single instance size</u>: |
19 | | -```sh |
20 | | -cargo test --release --package mopro-msm --lib -- msm::MSM_ALGO::tests::test_run_benchmark --exact --nocapture |
| 13 | +Metal MSM v2 works with `arkworks v0.4.x`; just include the crate in your `Cargo.toml`. |
| 14 | +```toml |
| 15 | +mopro-msm = { git = "https://github.com/zkmopro/gpu-acceleration.git", tag = "v0.2.0" } |
21 | 16 | ``` |
22 | 17 |
|
23 | | -Benchmarking for <u>multiple instance size</u>: |
24 | | -```sh |
25 | | -cargo test --release --package mopro-msm --lib -- msm::MSM_ALGO::tests::test_run_multi_benchmarks --exact --nocapture |
| 18 | +Next, invoke MSM within your Rust code. |
| 19 | +```rust |
| 20 | +use mopro_msm::msm::metal_msm::{ |
| 21 | + metal_variable_base_msm, |
| 22 | + test_utils::generate_random_bases_and_scalars, // optional |
| 23 | +}; |
| 24 | + |
| 25 | +fn main() { |
| 26 | + let input_size = 1 << 16; |
| 27 | + let (bases, scalars) = generate_random_bases_and_scalars(input_size); |
| 28 | + let msm_result = metal_variable_base_msm(&bases, &scalars); |
| 29 | + |
| 30 | + println!("Result: {:?}", msm_result); |
| 31 | +} |
26 | 32 | ``` |
27 | 33 |
|
28 | | -## gpu-exploration-app |
| 34 | +Because it’s compatible with Arkworks, you can seamlessly swap between Metal MSM and the Arkworks MSM implementation. |
| 35 | +```rust |
| 36 | +#[cfg(test)] |
| 37 | +mod tests { |
| 38 | + use super::*; |
| 39 | + use ark_bn254::{Fr as ScalarField, G1Projective as G}; |
| 40 | + use ark_ec::{CurveGroup, VariableBaseMSM}; |
| 41 | + use ark_std::{UniformRand, test_rng}; |
| 42 | + |
| 43 | + #[test] |
| 44 | + fn test_msm() { |
| 45 | + let input_size = 1 << 10; |
29 | 46 |
|
30 | | -This is a benchmark app to compare the performance of different algorithms on iOS device. |
| 47 | + // Generate random EC points and scalars with Arkworks |
| 48 | + let mut rng = test_rng(); |
| 49 | + let bases = (0..input_size) |
| 50 | + .map(|_| G::rand(&mut rng).into_affine()) |
| 51 | + .collect::<Vec<_>>(); |
| 52 | + let scalars = (0..input_size) |
| 53 | + .map(|_| ScalarField::rand(&mut rng)) |
| 54 | + .collect::<Vec<_>>(); |
31 | 55 |
|
32 | | -You can run the following commands in the root directory of the project to compile the metal library for a given OS: |
33 | | -```sh |
34 | | -# for macOS |
35 | | -bash mopro-msm/src/msm/metal/compile_metal.sh |
| 56 | + let metal_msm_result = metal_variable_base_msm(&bases, &scalars).unwrap(); |
| 57 | + let arkworks_msm_result = G::msm(&bases, &scalars).unwrap(); |
36 | 58 |
|
37 | | -# for iphoneOS |
38 | | -bash mopro-msm/src/msm/metal/compile_metal_iphone.sh |
| 59 | + assert_eq!(metal_msm_result, arkworks_msm_result); // the result is the same |
| 60 | + } |
| 61 | +} |
39 | 62 | ``` |
| 63 | + |
| 64 | +## Benchmark |
| 65 | + |
| 66 | +Benchmarking on BN254 curve ran on a MacBook Air with M3 chips, with test case setup time excluded. |
| 67 | + |
| 68 | +<table> |
| 69 | + <thead> |
| 70 | + <tr> |
| 71 | + <th rowspan="2" style="text-align:center">Scheme</th> |
| 72 | + <th colspan="7" style="text-align:center">Input Size (ms)</th> |
| 73 | + </tr> |
| 74 | + <tr> |
| 75 | + <th style="text-align:center">2<sup>12</sup></th> |
| 76 | + <th style="text-align:center">2<sup>14</sup></th> |
| 77 | + <th style="text-align:center">2<sup>16</sup></th> |
| 78 | + <th style="text-align:center">2<sup>18</sup></th> |
| 79 | + <th style="text-align:center">2<sup>20</sup></th> |
| 80 | + <th style="text-align:center">2<sup>22</sup></th> |
| 81 | + <th style="text-align:center">2<sup>24</sup></th> |
| 82 | + </tr> |
| 83 | + </thead> |
| 84 | + <tbody style="text-align:center"> |
| 85 | + <tr> |
| 86 | + <th style="text-align:center"><a href="https://github.com/arkworks-rs">Arkworks v0.4.x</a><br>(CPU, Baseline)</br></th> |
| 87 | + <td>6</td> |
| 88 | + <td>19</td> |
| 89 | + <td>69</td> |
| 90 | + <td>245</td> |
| 91 | + <td>942</td> |
| 92 | + <td>3,319</td> |
| 93 | + <td>14,061</td> |
| 94 | + </tr> |
| 95 | + <tr> |
| 96 | + <th style="text-align:center"><a href="https://github.com/zkmopro/gpu-acceleration/tree/v0.1.0">Metal MSM v0.1.0</a><br>(GPU)</br></th> |
| 97 | + <td>143<br>(-23.8x)</br></td> |
| 98 | + <td>273<br>(-14.4x)</br></td> |
| 99 | + <td>1,730<br>(-25.1x)</br></td> |
| 100 | + <td>10,277<br>(-41.9x)</br></td> |
| 101 | + <td>41,019<br>(-43.5x)</br></td> |
| 102 | + <td>555,877<br>(-167.5x)</br></td> |
| 103 | + <td>N/A</td> |
| 104 | + </tr> |
| 105 | + <tr> |
| 106 | + <th style="text-align:center"><a href="https://github.com/zkmopro/gpu-acceleration/tree/v0.2.0">Metal MSM v0.2.0</a><br>(GPU)</br></th> |
| 107 | + <td>134<br>(-22.3x)</br></td> |
| 108 | + <td>124<br>(-6.5x)</br></td> |
| 109 | + <td>253<br>(-3.7x)</br></td> |
| 110 | + <td>678<br>(-2.8x)</br></td> |
| 111 | + <td>1,702<br>(-1.8x)</br></td> |
| 112 | + <td>5,390<br>(-1.6x)</br></td> |
| 113 | + <td>22,241<br>(-1.6x)</br></td> |
| 114 | + </tr> |
| 115 | + <tr> |
| 116 | + <th style="text-align:center"><a href="https://github.com/ICME-Lab/msm-webgpu">ICME WebGPU MSM</a><br>(GPU)</br></th> |
| 117 | + <td>N/A</td> |
| 118 | + <td>N/A</td> |
| 119 | + <td>2,719<br>(-39.4x)</br></td> |
| 120 | + <td>5,418<br>(-22.1x)</br></td> |
| 121 | + <td>17,475<br>(-18.6x)</br></td> |
| 122 | + <td>N/A</td> |
| 123 | + <td>N/A</td> |
| 124 | + </tr> |
| 125 | + <tr> |
| 126 | + <th style="text-align:center"><a href="https://github.com/moven0831/icicle/tree/bn254-metal-benchmark">ICICLE-Metal v3.8.0</a><br>(GPU)</br></th> |
| 127 | + <td>59<br>(-9.8x)</br></td> |
| 128 | + <td>54<br>(-2.8x)</br></td> |
| 129 | + <td>89<br>(-1.3x)</br></td> |
| 130 | + <td>149<br>(+1.6x)</br></td> |
| 131 | + <td>421<br>(+2.2x)</br></td> |
| 132 | + <td>1,288<br>(+2.6x)</br></td> |
| 133 | + <td>4,945<br>(+2.8x)</br></td> |
| 134 | + </tr> |
| 135 | + </tbody> |
| 136 | +</table> |
| 137 | + |
| 138 | +> side note: |
| 139 | +> - for ICME WebGPU MSM, input size 2^12 causes M3 chip machines to crash; any sizes not listed on the project’s GitHub page are shown as "N/A" |
| 140 | +> - for Metal MSM v0.1.0, the 2^24 benchmark was abandoned because it exceeded practical runtime |
| 141 | +
|
| 142 | +## Profiling summary (v1 vs v2) |
| 143 | + |
| 144 | +Environment: M1 Pro, macOS 15.2, curve `ark_bn254`, dataset 2^20 unless stated. Medians of 5 runs. |
| 145 | + |
| 146 | +### v2 → v1 |
| 147 | + |
| 148 | +| metric | v1[^1] | v2[^2] | gain | |
| 149 | +|---|---|---|---| |
| 150 | +| end-to-end latency | 10.3 s | **0.42 s** | **×24** | |
| 151 | +| GPU occupancy | 32 % | 76 % | +44 pp | |
| 152 | +| CPU share | 19 % | **<3 %** | –16 pp | |
| 153 | +| peak VRAM | 1.6 GB | **220 MB** | –7.3× | |
| 154 | + |
| 155 | +Key changes: |
| 156 | + |
| 157 | +* single sparse-matrix kernel eliminates most launches and memory thrash |
| 158 | +* CSR buckets keep data on-device → near-zero host↔GPU traffic |
| 159 | +* on-GPU radix sort makes preprocessing parallel |
| 160 | + |
| 161 | +## Future |
| 162 | + |
| 163 | +### Technical Improvements |
| 164 | +- **Modern Dependencies**: Update to `objc2` and `objc2-metal` ([objc2](https://github.com/madsmtm/objc2)) |
| 165 | +- **Metal 4**: Adopt latest [Metal 4](https://developer.apple.com/metal/whats-new/) features |
| 166 | +- **Refactor with SIMD in mind**: |
| 167 | + - Instruction-level parallelism using vector types for faster FMA within SIMD groups |
| 168 | + - Memory coalescing to increase locality (e.g., structure of array instead of array of structure) |
| 169 | + - Optimized input reading patterns (e.g. `[X_i || Y_i]_0^{n-1}` instead of separate arrays) |
| 170 | + - Latency hiding and occupancy fine-tuning |
| 171 | + - Minimize thread divergence |
| 172 | + |
| 173 | +### Algorithm & Integration |
| 174 | +- **CPU-GPU Hybrid**: Research interleaving with CPU MSM crate and update to `arkworks 0.5` |
| 175 | +- **Advanced Algorithms**: |
| 176 | + - Elastic MSM [[ZHY+24](https://eprint.iacr.org/2024/057.pdf)] implementation |
| 177 | + - Faster modular reduction with LogJump ([article by Wei Jie](https://kohweijie.com/articles/25/logjumps.html), [Barret-Montgomery](https://hackmd.io/@Ingonyama/Barret-Montgomery)) |
| 178 | + |
| 179 | +### Platform Expansion |
| 180 | +- **Cross-platform**: WGSL support with native execution environment |
| 181 | +- **Crypto Math Library**: Maintain a Metal/WebGPU crypto math library |
| 182 | + |
| 183 | +## Community |
| 184 | + |
| 185 | +- X account: <a href="https://twitter.com/zkmopro"><img src="https://img.shields.io/twitter/follow/zkmopro?style=flat-square&logo=x&label=zkmopro"></a> |
| 186 | +- Telegram group: <a href="https://t.me/zkmopro"><img src="https://img.shields.io/badge/telegram-@zkmopro-blue.svg?style=flat-square&logo=telegram"></a> |
| 187 | + |
| 188 | +## Acknowledgements |
| 189 | + |
| 190 | +This work was initially sponsored by a joint grant from [PSE](https://pse.dev/) and [0xPARC](https://0xparc.org/). It is currently incubated by PSE. |
| 191 | + |
| 192 | +[^1]: https://hackmd.io/@yaroslav-ya/rJkpqc_Nke |
| 193 | +[^2]: https://hackmd.io/@yaroslav-ya/HyFA7XAQll |
0 commit comments