|
| 1 | +# Project: abacus-gemm-opt |
| 2 | + |
| 3 | +Working repo for optimizing the batched-GEMM kernels in ABACUS's Gint module |
| 4 | +(`gemm_nn_vbatch` / `gemm_tn_vbatch` in |
| 5 | +`source/source_lcao/module_gint/kernel/dgemm_vbatch.cu`). |
| 6 | + |
| 7 | +The code under optimization is invoked from `phi_operator_gpu.cu`: |
| 8 | +- `phi_mul_phi` (Hamiltonian build, V_loc) → **`gemm_tn_vbatch`** |
| 9 | +- `phi_mul_dm` (charge density from DM) → **`gemm_nn_vbatch`** |
| 10 | + |
| 11 | +`M` of the vbatch GEMMs scales with `mgrids_num_ ≈ bxyz = bx*by*bz`. |
| 12 | +`N`/`K` scale with the per-element orbital count `nw`. So a meaningful benchmark |
| 13 | +must vary **both** `bxyz` *and* the species mix. |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Builds |
| 18 | + |
| 19 | +| name | binary | purpose | |
| 20 | +|-----------|----------------------------------------------------------|------------------------| |
| 21 | +| baseline | `/home/dzc/abacus/abacus-gemm-opt/build-baseline/abacus_2g` | reference (do not modify) | |
| 22 | +| test | `/home/dzc/abacus/abacus-gemm-opt/build-test/abacus_2g` | the build under optimization | |
| 23 | + |
| 24 | +After editing `dgemm_vbatch.cu`, rebuild **only** the test build: |
| 25 | + |
| 26 | +```bash |
| 27 | +cmake --build /home/dzc/abacus/abacus-gemm-opt/build-test -j |
| 28 | +``` |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## Benchmark suite |
| 33 | + |
| 34 | +Location: `~/abacus/test-examples/ABACUS-test/LCAO_test/gemm_vbatch_bench/` |
| 35 | + |
| 36 | +Six 108-atom cases derived from the `008_Li27Ni9O54Mn9Co9` reference structure. |
| 37 | +Same lattice / k-point / cutoff in every case — only `bx,by,bz` and the species |
| 38 | +mix change, so cross-case deltas isolate to the vbatch GEMM shapes. |
| 39 | + |
| 40 | +| # | Case dir | bx,by,bz | bxyz | ntype | Composition (108 atoms) | |
| 41 | +|---|---------------------------|---------|------|-------|------------------------------------------| |
| 42 | +| 1 | `case1_bxyz27_5species` | 3,3,3 | 27 | 5 | Li27 Ni9 O54 Mn9 Co9 | |
| 43 | +| 2 | `case2_bxyz48_6species` | 4,4,3 | 48 | 6 | Li27 Ni9 O54 Mn6 Fe3 Co9 | |
| 44 | +| 3 | `case3_bxyz64_6species` | 4,4,4 | 64 | 6 | Li27 Ni6 Al3 O54 Mn9 Co9 | |
| 45 | +| 4 | `case4_bxyz80_7species` | 5,4,4 | 80 | 7 | Li27 Ni6 Fe3 O54 Mn6 Al3 Co9 | |
| 46 | +| 5 | `case5_bxyz100_7species` | 5,5,4 | 100 | 7 | Li24 Na3 Ni6 Cu3 O54 Mn9 Co9 | |
| 47 | +| 6 | `case6_bxyz125_8species` | 5,5,5 | 125 | 8 | Li24 Na3 Ni6 Cu3 O54 Mn6 Fe3 Co9 | |
| 48 | + |
| 49 | +All cases run with `scf_nmax=2`, `cal_force=1`, `cal_stress=1`, `nspin=2`, |
| 50 | +`gint_precision=double`, `ks_solver=genelpa`, Γ-only. |
| 51 | +This exercises `cal_gint_vl` four times, `cal_gint_rho` twice, `cal_gint_fvl` |
| 52 | +once per run. |
| 53 | + |
| 54 | +### Running the suite |
| 55 | + |
| 56 | +The runner is **serial** (one case after another, baseline before test): |
| 57 | + |
| 58 | +```bash |
| 59 | +cd ~/abacus/test-examples/ABACUS-test/LCAO_test/gemm_vbatch_bench |
| 60 | +./run_bench.sh |
| 61 | +``` |
| 62 | + |
| 63 | +The launch line, identical for every run, is: |
| 64 | + |
| 65 | +``` |
| 66 | +OMP_NUM_THREADS=7 mpirun -n 1 --bind-to socket <abacus_2g> |
| 67 | +``` |
| 68 | + |
| 69 | +Each invocation produces, inside the case dir: |
| 70 | + |
| 71 | +``` |
| 72 | +run_baseline.log run_test.log # stdout |
| 73 | +wall_baseline.txt wall_test.txt # wall time captured by the harness |
| 74 | +OUT.baseline/ OUT.test/ # ABACUS output dirs (renamed from OUT.ABACUS) |
| 75 | + └── running_scf.log # source of all kernel timings |
| 76 | +``` |
| 77 | + |
| 78 | +### Where to look in the timing table |
| 79 | + |
| 80 | +Inside each `running_scf.log`, the `TIME STATISTICS` section near the end has |
| 81 | +the rows that matter: |
| 82 | + |
| 83 | +| timer | what it covers | which kernel | |
| 84 | +|-----------------------------|-----------------------------------------------|--------------| |
| 85 | +| `Gint cal_gint_vl` | `phi_mul_phi` during H build | `gemm_tn_vbatch` | |
| 86 | +| `Gint cal_gint_rho` | `phi_mul_dm` during `dm2rho` | `gemm_nn_vbatch` | |
| 87 | +| `Gint cal_gint_fvl` | force/stress contribution from V_loc | both flavors | |
| 88 | +| `HamiltLCAO updateHk` | superset of `cal_gint_vl` | (sanity) | |
| 89 | +| `HSolverLCAO solve` | SCF inner loop (includes `cal_gint_rho`) | (sanity) | |
| 90 | +| `Driver atomic_world` | end-to-end | (sanity) | |
| 91 | + |
| 92 | +`updateHk`, `elpa_solve`, `cal_force_stress` should not move under this |
| 93 | +optimization — if they do by more than ±2%, suspect noise or a build mismatch |
| 94 | +before declaring victory. |
| 95 | + |
| 96 | +### Generating the report |
| 97 | + |
| 98 | +`run_bench.sh` only runs the binaries; it does not parse. To produce a Markdown |
| 99 | +report from the existing `OUT.{baseline,test}/running_scf.log` files, the |
| 100 | +parsing recipe is: |
| 101 | + |
| 102 | +1. Read the `TIME STATISTICS` block from each log. |
| 103 | +2. For each case, pull out the 8 timers in the table above for both builds. |
| 104 | +3. Compute `speedup = baseline / test`. |
| 105 | +4. Tabulate per-kernel and end-to-end. |
| 106 | + |
| 107 | +A reference report is at: |
| 108 | + |
| 109 | +``` |
| 110 | +~/abacus/test-examples/ABACUS-test/LCAO_test/gemm_vbatch_bench/REPORT.md |
| 111 | +``` |
| 112 | + |
| 113 | +and a machine-readable parse dump at: |
| 114 | + |
| 115 | +``` |
| 116 | +~/abacus/test-examples/ABACUS-test/LCAO_test/gemm_vbatch_bench/_parsed.json |
| 117 | +``` |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## Known baseline characterization (as of 2026-04-09 sweep) |
| 122 | + |
| 123 | +The "test" build at the time the suite was created had this profile vs |
| 124 | +baseline (single-shot, RTX 3090, GPU0): |
| 125 | + |
| 126 | +- **`gemm_tn_vbatch` (`cal_gint_vl`):** uniformly **+3.4 % to +5.5 %** across all |
| 127 | + 6 cases. Treat this kernel as the *known-good* template. |
| 128 | +- **`gemm_nn_vbatch` (`cal_gint_rho` / `cal_gint_fvl`):** highly shape-sensitive. |
| 129 | + Best case +3.5 %, **worst case −27 % on `cal_gint_rho` and −24 % on `cal_gint_fvl`**. |
| 130 | + The worst regressions hit `bxyz=27` and `bxyz=80`. The two cases that |
| 131 | + *improved* were `bxyz=64` (4³) and `bxyz=125` (5³) — both perfect cubes. |
| 132 | + Strong hint: the optimized `gemm_nn_vbatch` makes a tile/block choice that |
| 133 | + assumes `M = bxyz` is a "nice" multiple of the kernel tile and falls off a |
| 134 | + cliff for awkward sizes. |
| 135 | + |
| 136 | +This characterization will become stale as you optimize. Re-run |
| 137 | +`run_bench.sh` and refresh `REPORT.md` after any non-trivial change to |
| 138 | +`dgemm_vbatch.cu`. |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +## Workflow checklist for each optimization iteration |
| 143 | + |
| 144 | +1. Edit `source/source_lcao/module_gint/kernel/dgemm_vbatch.cu`. |
| 145 | +2. `cmake --build build-test -j` (do **not** rebuild `build-baseline`). |
| 146 | +3. `cd ~/abacus/test-examples/ABACUS-test/LCAO_test/gemm_vbatch_bench && ./run_bench.sh` |
| 147 | +4. Re-parse the 12 `running_scf.log` files and update `REPORT.md`. |
| 148 | +5. **Verify correctness** before claiming a speedup: diff the SCF energies in |
| 149 | + `OUT.baseline/running_scf.log` vs `OUT.test/running_scf.log` for at least |
| 150 | + the cases that improved — a kernel that "speeds up" by skipping work will |
| 151 | + show up as an energy mismatch here. |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +## Notes / gotchas |
| 156 | + |
| 157 | +- The runner deletes `OUT.ABACUS` and renames the result to `OUT.baseline` / |
| 158 | + `OUT.test`. Don't put precious artifacts in `OUT.ABACUS` between runs. |
| 159 | +- All `.upf` / `.orb` files in each case dir are **symlinks** into |
| 160 | + `~/abacus/test-examples/PP_ORB`. If that directory moves, the cases break. |
| 161 | +- GPU 0 is shared across the system. If a number jitters, re-run with |
| 162 | + `CUDA_VISIBLE_DEVICES=0` or pin the run to a known-idle device. |
| 163 | +- `scf_nmax=2` is intentional — the goal is to measure kernels, not converge |
| 164 | + the SCF. Don't bump it without a reason; it changes the call counts of every |
| 165 | + Gint timer in the table above. |
0 commit comments