www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DMD Performance Regression Publisher [GSoC 2026]

reply Abul Hossain Khan <abulkhan19175 gmail.com> writes:
Hi everyone,
I am working on the Performance Regression Publisher project 
under the mentorship of Dennis.

My progress so far: the initial end-to-end pipeline has been 
built and is working on my fork. The bot builds DMD at a PR's 
merge-base and at its head, measures a small set of metrics under 
cachegrind, and posts a single sticky comment with the diff.

**What's done**

The harness is in `tools/perfrunner/` and is written in D (dub 
project),

- `app.d` — CLI, takes the two already-built dmd binaries + 
metadata and writes `results.json`.
- `cachegrind.d` — runs the compile under cachegrind and reads 
the instruction count.
- `metrics.d` — the five metrics below.
- `report.d` / `stats.d` — the schema-v1 JSON and the 
percent-delta math.
- `workloads/hello.d` — the single workload for now will add more 
soon.

Around it, `.github/workflows/perf.yml` runs on every PR (and on 
pushes to master), builds both refs with the 
existing `ci/run.sh`, runs the harness, and hands the result 
to `.github/scripts/perf_comment.py`, which upserts one sticky 
comment so force-pushes don't spam the thread.

**The metrics it reports(PR Comment) right now Looks like this:**

| Metric | Base | PR | delta |
|--------|------|----|-------|
| compile hello.d (instr) | 422.9 M | 457.9 M | +8.27% |
| compile hello.d -O (instr) | 446.1 M | 481.1 M | +7.84% |
| dmd binary size (stripped) | 11.91 MB | 11.91 MB | 0.00% |
| hello binary size | 0.72 MB | 0.72 MB | 0.00% |
| peak RSS (compile hello.d) | 56 MB | 55 MB | -2.17% |


I tested the whole path end to end: On my fork with a deliberate 
busy loop in `compiler/src/main.d`, and the bot reported +8.27% / 
+7.84% instructions consistently across reruns while size stayed 
flat.
Code - https://github.com/abulgit/dmd/pull/39

**what's next -**

1. We are currently building both with DMD 2.112.0 as the host 
compiler. Dennis suggested moving to `ldc2 -O3` with PGO so the 
binary we measure matches a real release build and the optimizer 
doesn't make a harmless PR look like a regression. Also he 
suggested that we should to do this early, in case valgrind has 
any trouble with ldc2.
2. After that, We will add more Realistic Workloads there like 
Phobos etc.
3. And then Building the dashboard that will show the historical 
Data.

That's the plan we have right now, and I'll try to post weekly 
updates here as work progresses.
Feel free to leave any feedback or suggestions!
Jun 12
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Friday, 12 June 2026 at 18:58:03 UTC, Abul Hossain Khan wrote:
 Hi everyone,
 I am working on the Performance Regression Publisher project 
 under the mentorship of Dennis.

 [...]
Have you thought about using Linux’s perf tool it has interesting stats about performance counters. Valgrind is sensible tool but being intrusive it may distort profile a little bit.
Jun 15
parent Abul Hossain Khan <abulkhan19175 gmail.com> writes:
 Have you thought about using Linux’s perf tool it has 
 interesting stats about performance counters. Valgrind is 
 sensible tool but being intrusive it may distort profile a 
 little bit.
We started with Valgrind because it's giving us quite good and stable results so far, but we'll definitely look into perf as well, especially once we switch to ldc2 + PGO and can compare the approaches.
Jun 16
prev sibling next sibling parent reply Abul Hossain Khan <abulkhan19175 gmail.com> writes:
Hi everyone,

Last time I posted, we had the basic pipeline working: the bot 
builds DMD at the base and head commits, runs Cachegrind, and 
posts a sticky comment. At that point, everything was being built 
with DMD 2.112.0 as the host compiler.



Now we are building with `ldc2-1.42.0` as the host compiler, so 
the binary we measure more closely resembles a real release 
build. I got that working, and the numbers improved quite a bit.

| Metric                     | DMD Host |   LDC2 Host | 
Improvement |
| -------------------------- | -------: | ----------: | 
----------: |
| compile hello.d (instr)    |  422.8 M | 257.6 M     |  -39.1%   
   |
| compile hello.d -O (instr) |  445.9 M | 277.9 M     |  -37.7%   
   |
| dmd binary size (stripped) | 11.93 MB | 6.96 MB     |  -41.7%   
   |
| hello binary size          |  0.72 MB | 0.72 MB     |      -    
   |
| peak RSS                   |    56 MB |   49 MB     |  -12.5%   
   |

Switching from DMD as the host compiler to LDC2 reduced 
instruction counts by about 39%, cut the compiler binary size by 
41%, and slightly reduced peak memory usage. The results are also 
very consistent across reruns, and the workflow runs faster as 
well.

Now as my mentor also suggested adding PGO (Profile Guided 
Optimization) with the LDC2 build for even better results. I 
didn't know much about PGO at first, but Dennis helped me 
understand it and also shared his local build script with PGO 
enabled with me. I think I've almost figured it out, so that will 
be the next thing I work on.



* Finish the PGO integration ASAP.
* Add more realistic workloads, like Phobos etc. and then we will 
start working on the dashboard to display historical results.



Yes I will have my semester exams from June 27 to July 3, so I 
will be unavailable for about a week. Hopefully that's okay. I'll 
make up for it afterward if needed, and I will make sure to 
finish the project on time (or even ahead of schedule).

Also, I want to thank my mentor, Dennis. He has been incredibly 
supportive throughout.

As always, feel free to leave any feedback or suggestions!
Jun 25
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Thursday, 25 June 2026 at 17:51:10 UTC, Abul Hossain Khan 
wrote:
 Hi everyone,

 Last time I posted, we had the basic pipeline working: the bot 
 builds DMD at the base and head commits, runs Cachegrind, and 
 posts a sticky comment. At that point, everything was being 
 built with DMD 2.112.0 as the host compiler.

 [...]
binary size of dmd is much less important then compile speed
Jun 25
prev sibling next sibling parent reply FinalEvilution <FinalEvilution gmail.com> writes:
On Thursday, 25 June 2026 at 17:51:10 UTC, Abul Hossain Khan 
wrote:

 Now as my mentor also suggested adding PGO (Profile Guided 
 Optimization) with the LDC2 build for even better results. I 
 didn't know much about PGO at first, but Dennis helped me 
 understand it and also shared his local build script with PGO 
 enabled with me. I think I've almost figured it out, so that 
 will be the next thing I work on.
+1 for PGO. As far as I'm concerned everyone who is seriously optimizing for performance should be using PGO. Modifying a build script to run the benchmark suite (if you don't have one what are you optimizing) and compile a second time is a pretty simple change, but I've seen up to a 30% decrease in run time... For just a makefile edit.
 **Semester exam**
 Yes I will have my semester exams from June 27 to July 3, so I 
 will be unavailable for about a week.
You're going to ace it.
 As always, feel free to leave any feedback or suggestions!
If memory serves perf has the ability to diff the performance of 2 separate profiles on a per function basis. Might be handy.
Jun 26
parent Abul Hossain Khan <abulkhan19175 gmail.com> writes:
Thank u so much for the suggestions.
Exams are finally over now and they went pretty well.
Jul 03
prev sibling parent reply Abul Hossain Khan <abulkhan19175 gmail.com> writes:
Hi everyone,

Last time we had switched the host compiler from DMD to 
ldc2-1.42.0, which gave a nice drop in instruction counts and 
compiler size. Since then I finished my semester exams and got 
back to the work.



  DMD already has a `dmd-pgo` build target that does the full LDC 
PGO cycle, So the first approach was just use that from the 
workflow for base and head separately. Got that working, but the 
training was actually failing silently cause Phobos was building 
after `dmd-pgo`, so the instrumented compiler couldn't import 
stdlib during training, so it barely trained on anything. Then I 
fixed the ordering and it worked!

But then another problem was that it was showing weird result 
after that like ~5.5% delta on sizes and also the Instruction 
Count was showing weird deltas. That's because base and head were 
each training their own PGO profile independently, in parallel, 
the merge data comes out slightly different every single run. So 
the two sides end up with two different profiles even on 
identical source, which shows up as noise in the diff. Fixed it 
by training PGO once and reusing that same profile for both 
builds. After that It was stable across the runs.

Here's how the numbers moved across every stage so far:

| Metric | DMD host | LDC2 host | LDC2 + PGO |
|--------|----------|-----------|------------|
| compile hello.d (instr) | 422.8 M | 257.6 M | 224.1 M |
| compile hello.d -O (instr) | 445.9 M | 277.9 M | 242.7 M |
| dmd binary size (stripped) | 11.93 MB | 6.96 MB | 6.89 MB |
| hello binary size | 0.72 MB | 0.72 MB | 0.72 MB |
| peak RSS | 56 MB | 50 MB | 50 MB |




The system works fine on my forked repo, but a PR that comes from 
a fork repo gets a read-only token, so the comment step would 
just fail there.
Did some research and the usual fix for this is to split it into 
two workflows -
`perf.yml` - does the build + measure and just uploads the 
results as an artifact,
`perf-comment.yml` - runs on `workflow_run` after that finishes, 
in the base repo context, so it gets a writable token, downloads 
the artifact and posts the comment. It should works fine.

Now the thing is, `workflow_run` only triggers off the copy of 
the workflow that's on the default branch. So until this is 
merged to `master`, `perf-comment.yml` just never fires, even 
though `perf.yml` runs fine and uploads the artifact.

PR link - https://github.com/dlang/dmd/pull/23380


- After the PR got Reviewed and merged We can add more workloads 
into it, and start working on the dashboard

Thanks again to Dennis for the amazing mentorship.
As always, feel free to leave any feedback or suggestions!
Jul 10
parent reply Abul Hossain Khan <abulkhan19175 gmail.com> writes:
Hi everyone,

Last time I posted update about we Integrated PGO in our Build 
process, and the PR was waiting for review. Now the PR is merged 
into DMD master.

Here is what we've done since then:



We added Phobos compilation metrics to perfrunner. We reused the 
Phobos checkout already present in CI by passing `--base-phobos` 
and `--head-phobos` into perfrunner and compiling Phobos's 
`std/package.d` with `-i=std -preview=dip1000`. Measuring Phobos 
compilation gives us a much more realistic compiler benchmark (~5 
billion instructions and ~633 MB peak RSS) compared to just 
compiling a hello world program.

Here is what the bot comment output looks like now with Phobos 
included:


| Metric | Base | PR | delta |
|--------|------|----|-------|
| compile hello.d (instr) | 215.6 M | 215.6 M | 0.00% |
| compile hello.d -O (instr) | 234.1 M | 234.1 M | 0.00% |
| compile Phobos (instr) | 5,078.7 M | 5,078.7 M | 0.00% |
| dmd binary size (stripped) | 6.87 MB | 6.87 MB | 0.00% |
| hello binary size | 0.72 MB | 0.72 MB | 0.00% |
| peak RSS (compile hello.d) | 43 MB | 44 MB | +0.33% |
| peak RSS (compile Phobos) | 633 MB | 633 MB | -0.07% |

**PR(Merged)**: https://github.com/dlang/dmd/pull/23432



Getting a PR comment about perf every time after opening a PR can 
be very annoying, especially when there aren't any significant 
deltas. the bot now skips posting if all metric deltas stay 
within defined threshold limits. We defined the initial threshold 
limits as:
```python
THRESHOLDS = {"cachegrind": 0.1, "stat": 0.1, "time -v": 2.0}
```
Also threshold values need to be determined experimentally over 
time by trial and error. So we will observe PRs and can tweak 
them accordingly.

Also fixed a small UI issue where zero deltas were showing up as 
`-0.00%`. So we add an `abs()` guard for near-zero values, in 
comment poster.

**PR(Merged)**: https://github.com/dlang/dmd/pull/23434



- Will see how the bot performs overtime.
- I want to add a couple more real-world workloads, such as 
`vibe.d`. If anyone has suggestions for other D libraries/dub 
packages we should benchmark, please let me know!
- My mentor gave me an awesome idea that instead of just 
displaying raw high-level metrics, we can add stage breakdown 
(using `-ftime-trace`). I don't know much about `-ftime-trace`, 
so it is going to be very interesting for me to implement it.

Thanks again to Dennis for the guidance and reviews!
And as always, feel free to leave any feedback or suggestions.
Jul 20
parent reply libxmoc <libxmoc gmail.com> writes:
This is a much needed project, thanks for the PR!

However, memory usage is not a useful metric.

How long it takes to compile community projects is much better 
one, as others have suggested, the main use of dmd is fast 
iteration time, so we should benchmark as such.
Jul 20
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 20 July 2026 at 17:44:16 UTC, libxmoc wrote:
 However, memory usage is not a useful metric.
When you run out of memory, compile speed increases to infinity.
Jul 20
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Monday, 20 July 2026 at 18:44:39 UTC, Adam D. Ruppe wrote:
 On Monday, 20 July 2026 at 17:44:16 UTC, libxmoc wrote:
 However, memory usage is not a useful metric.
When you run out of memory, compile speed increases to infinity.
I just download more ram
Jul 20
prev sibling parent libxmoc <libxmoc gmail.com> writes:
On Monday, 20 July 2026 at 18:44:39 UTC, Adam D. Ruppe wrote:
 On Monday, 20 July 2026 at 17:44:16 UTC, libxmoc wrote:
 However, memory usage is not a useful metric.
When you run out of memory, compile speed increases to infinity.
I didn't mean it should be dropped as a metric, only that compile time ranks higher in priority.
Jul 22
prev sibling parent user1234 <user1234 12.de> writes:
On Friday, 12 June 2026 at 18:58:03 UTC, Abul Hossain Khan wrote:
 Hi everyone,
 I am working on the Performance Regression Publisher project 
 under the mentorship of Dennis.

 My progress so far: the initial end-to-end pipeline has been 
 built and is working on my fork. The bot builds DMD at a PR's 
 merge-base and at its head, measures a small set of metrics 
 under cachegrind, and posts a single sticky comment with the 
 diff.
This is very good work. the CI aspects have been a bit left aside since a few years, but while the initial impact is awesome (I daily check what's going on on GH for DMD, and others..) I have a few questions. 1. how is this reliable ? Are you sure that the hardware used is always the same, i.e when testing master vs head. 2. How the metric is relevant ? Is a PR always supposed to lower the cost in term of intruction refs ? This project put a bit of pressure in this that a new feature will look like a regression. 3. Ecological cost That pumps energy, is that worth ?
Jul 21