Skip to content
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions content/posts/2026-06-29-fastest-windows-on-xcp-ng.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,68 @@ image::/assets/images/2026/06/29/05-ipconfig-dhcp.png["A Command Prompt running

_Figure 5. The emulated network card has an inbox driver, so the VM pulls a DHCP lease on first boot, before any paravirtual drivers are installed._

== The flags that template did not set

Added after publication, because a colleague's support question sent me back to look at my own VM.

Building from "Other install media" is the right call for getting this disk to boot, and it has a cost I did not notice at the time. That template is deliberately generic, so it does not apply the link:https://docs.xcp-ng.org/vms/#enabling-viridian-extensions[Viridian settings] that the Windows templates do. Viridian is the platform's name for the Hyper-V enlightenments Windows expects to find, and the documentation is blunt about it: all six parameters should be true for proper operation of Windows.

Put the two templates side by side and the gap is obvious:

[source,bash]
----
xe template-param-get uuid=<Windows Server 2025 template> param-name=platform
xe template-param-get uuid=<Other install media template> param-name=platform
----

The Windows Server 2025 template sets all six. "Other install media" sets `viridian: true` and stops there. My imported VM inherited the short version, so it had been running on one flag out of six the whole time.

Check yours:

[source,bash]
----
xe vm-param-get uuid=<VM> param-name=platform
----

If any of `viridian`, `viridian_time_ref_count`, `viridian_reference_tsc`, `viridian_apic_assist`, `viridian_crash_ctl` or `viridian_stimer` is missing, or present and set to `false`, set them with the VM shut down:

[source,bash]
----
xe vm-param-set \
uuid=<VM> \
platform:viridian=true \
platform:viridian_time_ref_count=true \
platform:viridian_reference_tsc=true \
platform:viridian_apic_assist=true \
platform:viridian_crash_ctl=true \
platform:viridian_stimer=true
----

I did measure it in the end, because a claim like this is worth more than a shrug. Same VM, same host, nothing else running on that host, link:https://github.com/microsoft/diskspd[diskspd] with caching disabled, five iterations per run. I measured at one flag, flipped to six, went back to one, then back to six, so that a reboot could not quietly take the credit. The first iteration of every run is discarded, because it reads off a warm cache and flatters whichever configuration happens to go first.

[cols="3,2,2,1", options="header"]
|===
| 4K random read | 1 of 6 flags | 6 of 6 flags | Change

| Queue depth 1, average latency | 0.312 ms | 0.195 ms | -38%
| Queue depth 1, IOPS | 3,143 | 5,127 | +63%
| Queue depth 8 across 4 threads, IOPS | 45,147 | 71,674 | +59%
|===

Eight measurements in each group, and the two groups do not overlap anywhere: the worst six-flag run beat the best one-flag run on both tests. The spread tightened as well. At one flag the runs wandered by about a quarter, at six they repeated to within a few percent, and I/O that is unpredictable is what people actually notice and complain about.

Caveats, because this is one VM on one host. The storage was local, not iSCSI or NFS, so your numbers will not be these numbers. One of the four runs drifted upward partway through in a way that looks like host-side caching, which is why the table quotes medians rather than peaks. Take the direction seriously and the exact percentages loosely.
Comment thread
gounthar marked this conversation as resolved.
Outdated

The documentation attributes a harder failure to the same missing synthetic timer: a Windows Server 2025 guest link:https://docs.xcp-ng.org/troubleshooting/windows-pv-tools/#windows-server-2025-hangs-randomly-with-0-cpu[hangs randomly at 0% CPU]. The flags are the first thing that page tells you to check, and they only cover the host side of it. If you have ever set Windows boot parameters to disable the synthetic timer, those have to come back out too:

[source,powershell]
----
bcdedit /deletevalue "{current}" useplatformclock
bcdedit /deletevalue "{current}" useplatformtick
----
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The same gap catches anything that did not come from a Windows template: a VM imported from a VHD like this one, or migrated in from VMware.

== Step 5: the guest tools, and the one real trap

The emulated NIC works, but you still want the XCP-ng guest tools. They bring the paravirtual network and storage drivers (`xennet` and `xenvbd`), which are faster than the emulated devices, and the management agent that reports the guest's IP back to the host so link:https://xapi-project.github.io/[XAPI] and Xen Orchestra can show you where the VM actually is on the network. Installing them is where the one true footgun of this whole exercise lives, so slow down for this part.
Expand Down