Après migration d’un cluster Hyper‑V Windows Server 2016 vers 2022, certaines VM montrent une baisse CPU marquée : 4 vCPU ne « valent » plus que 2 cœurs logiques. Voici pourquoi, comment le vérifier, et surtout comment corriger proprement sans sacrifier la sécurité ni vos licences par cœur.
Vue d’ensemble de la question
Vous migrez vos hôtes Hyper‑V de Windows Server 2016 à 2022 (hôtes Intel Xeon Gold avec Hyper‑Threading activé). Après bascule :
- Des tests synthétiques montrent qu’une VM à 4 vCPU n’exploite que l’équivalent de 2 cœurs / 4 threads, pas 4 cœurs « pleins » ; les scores chutent face à l’hôte.
- Des alertes « CPU Wait Time per Dispatch » oscillent entre 50 et 80 µs.
- Vous souhaitez : (1) savoir si c’est normal, (2) vérifier/mettre à jour les Integration Services, (3) forcer une VM donnée à voir 4 vCPU = 4 cœurs (sans SMT).
Diagnostic rapide
- Changement clé depuis 2019 : Hyper‑V utilise par défaut le core scheduler, sensible à la topologie SMT. Une VM « hérite » de l’HT de l’hôte : 4 vCPU sont présentés comme 2 cœurs × 2 threads à l’invité, ce qui biaise les comparaisons vs 2016 et peut pénaliser des charges « par cœur ».
- Correctif ciblé recommandé : désactiver l’SMT virtuel pour les VM critiques/licenciées par cœur :
Set-VMProcessor -HwThreadCountPerCore 1
. L’invité verra alors 4 cœurs / 4 logiques (pas d’HT virtuel). - Les 50–80 µs de « CPU Wait Time per Dispatch » ne sont pas anormaux en soi ; interprétez‑les dans le contexte (charge globale, tendance, files d’attente) plutôt qu’avec un seuil absolu trop bas.
Pourquoi le phénomène apparaît après migration ?
Sur Windows Server 2016, Hyper‑V utilisait par défaut le classic scheduler. À partir de Windows Server 2019 (et donc 2022), le core scheduler devient le comportement par défaut. Son objectif premier : améliorer l’isolation et limiter les risques de canaux auxiliaires entre threads SMT appartenant à des partitions différentes. Conséquences pratiques :
- Le planificateur raisonne au niveau du « cœur physique » (et de ses threads SMT). Quand l’hôte a l’HT activé, la VM hérite d’une topologie SMT : un « cœur » dans l’invité peut comporter 2 threads.
- Si vous allouez 4 vCPU à une VM, l’invité peut les voir comme 2 cœurs × 2 threads (au lieu de 4 cœurs × 1 thread), ce qui peut réduire les performances sur des charges sensibles au débit par cœur (SQL, moteurs analytiques, compression, crypto, etc.).
- La comparaison « hôte vs VM » se trouve faussée si l’hôte est benché « plein cœurs » alors que la VM est benchée « moitié de cœurs avec SMT ».
Effet perceptible
Sur des benchs qui scalent quasi‑linéairement avec des cœurs pleins, la VM à 4 vCPU (2 cœurs × 2 threads) baisse face à la VM 2016 (4 cœurs × 1 thread). Ce n’est pas une régression d’Hyper‑V en soi, mais un changement de topologie offert à l’invité.
Solutions éprouvées (et sûres)
Solution 1 — Forcer 4 vCPU = 4 cœurs (désactiver l’SMT virtuel par VM)
Pour les VM critiques ou licenciées par cœur (SQL Server, Oracle, outils de CAO, etc.), imposez 1 thread par cœur afin que 4 vCPU soient de vrais cœurs côté invité :
# Impose 1 thread par cœur à l'intérieur de la VM
Set-VMProcessor -VMName "<VM>" -HwThreadCountPerCore 1
# Vérification
Get-VMProcessor -VMName "\<VM>" | Select VMName, HwThreadCountPerCore </code></pre>
<ul>
<li><strong>0</strong> = hériter de l’hôte (souvent 2 avec HT). <strong>1</strong> = pas d’SMT virtuel. <strong>2</strong> = 2 threads par cœur exposés à l’invité.</li>
<li>Windows Server 2016 ne gère pas la valeur <code>0</code> (héritage). Sur 2019/2022, elle est fonctionnelle.</li>
<li>Impact attendu : l’invité rapporte « 4 cœurs / 4 logiques ». Les charges « par cœur » retrouvent le niveau attendu, et vos calculs de licence par cœur restent cohérents.</li>
</ul>
<h4>Validation dans l’invité</h4>
<p><strong>Windows :</strong></p>
<pre><code class="language-powershell"># Aperçu rapide
Get-CimInstance Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors
# Perfmon (dans la VM)
# > Processeur \ % d'utilisation du processeur (par logique)
# > Hyper-V Hypervisor Logical Processor \ % Total Run Time (si role invité outillé)
</code></pre>
<p><strong>Linux :</strong></p>
<pre><code class="language-bash">lscpu | egrep 'Core|Thread|Socket'
nproc
# SMT actif ?
cat /sys/devices/system/cpu/smt/active
</code></pre>
<h3>Solution 2 — Option : revenir au « classic scheduler » (compromis sécurité/perf)</h3>
<p>Si vous devez reproduire au plus près le comportement 2016 (à évaluer au cas par cas), vous pouvez rétablir le <em>classic scheduler</em> au niveau de l’hôte :</p>
<pre><code class="language-cmd">bcdedit /set hypervisorschedulertype classic
</code></pre>
<p>Contrôlez le type actif via le journal Hyper‑V :</p>
<pre><code class="language-powershell">Get-WinEvent -FilterHashTable @{ProviderName="Microsoft-Windows-Hyper-V-Hypervisor"; ID=2} -MaxEvents 1
</code></pre>
<p><strong>Attention :</strong> le <em>core scheduler</em> reste la recommandation générale pour l’isolation. Préférez la <strong>Solution 1</strong> (désactiver le SMT virtuel <em>par VM</em>) plutôt qu’un basculement global du scheduler.</p>
<h3>Solution 3 — Mettre à niveau la « version de configuration » des VM</h3>
<p>Les VM importées peuvent rester sur un niveau de configuration ancien (hérité de l’hôte 2016) et ne pas profiter de tous les raffinements du nouvel hôte. Vérifiez et mettez à niveau si nécessaire :</p>
<pre><code class="language-powershell"># Lister versions de config (arrêt de la VM requis pour upgrade)
Get-VM | Format-Table Name, Version
# Mettre à niveau une VM (opération non réversible)
Update-VMVersion -Name "\<VM>" </code></pre>
<p>Bonnes pratiques : capture de sauvegarde, arrêt contrôlé, puis montée de version. Répétez les tests de performance après upgrade.</p>
<h3>Solution 4 — Interpréter « CPU Wait Time per Dispatch »</h3>
<p>Ce compteur (catégorie <em>Hyper‑V Hypervisor Virtual Processor</em>) mesure le <strong>temps moyen d’attente</strong> d’un vCPU avant d’obtenir du temps de CPU. Il est exprimé en <strong>nanosecondes</strong>.</p>
<ul>
<li>Des dizaines de microsecondes (ex. 50–80 µs) peuvent être <strong>normales</strong> sur des hôtes modernes selon la charge et la densité.</li>
<li>Ce qui compte : la <em>tendance</em>, la <em>corrélation</em> avec la saturation CPU et la présence de goulots (Ready Time, Preemption, %Guest Run Time, etc.).</li>
</ul>
<table>
<thead>
<tr>
<th>Plage observée</th>
<th>Lecture recommandée</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>< 30 µs</td>
<td>Latence faible</td>
<td>Aucune ; surveiller</td>
</tr>
<tr>
<td>30–100 µs</td>
<td>Nominal sur hôtes denses</td>
<td>OK si l’appli est fluide</td>
</tr>
<tr>
<td>100–500 µs</td>
<td>Contestion modérée</td>
<td>Examiner consolidation, affinités, NUMA</td>
</tr>
<tr>
<td>> 500 µs</td>
<td>Contestion marquée</td>
<td>Réduire la charge ou réallouer vCPU</td>
</tr>
</tbody>
</table>
<h4>Compteurs utiles à corréler</h4>
<table>
<thead>
<tr>
<th>Objet Perfmon</th>
<th>Compteur</th>
<th>Pourquoi</th>
</tr>
</thead>
<tbody>
<tr>
<td>Hyper‑V Hypervisor Logical Processor</td>
<td>% Total Run Time</td>
<td>Charge réelle des LP de l’hôte</td>
</tr>
<tr>
<td>Hyper‑V Hypervisor Virtual Processor</td>
<td>CPU Wait Time per Dispatch</td>
<td>Latence d’ordonnancement par VP</td>
</tr>
<tr>
<td>Hyper‑V Hypervisor Virtual Processor</td>
<td>Virtual TLB Flush Entries</td>
<td>Pression mémoire/translation</td>
</tr>
<tr>
<td>Processeur</td>
<td>% d’utilisation du processeur</td>
<td>Vue invité (équilibrage interne)</td>
</tr>
</tbody>
</table>
<h3>Solution 5 — Integration Services (IS) : vérification et mise à jour</h3>
<p>Depuis Windows Server 2016, les <strong>Integration Services</strong> sont <strong>intégrés</strong> aux OS invités modernes et se <strong>mettent à jour via Windows Update</strong>. Il n’y a plus d’ISO à monter (sauf très vieux invités).</p>
<ul>
<li>Une valeur <code>IntegrationServicesVersion = 10.0.14393</code> indique une base « 2016 ». L’important est que l’invité soit <strong>à jour</strong>.</li>
<li>Sur l’hôte, listez l’état des services d’intégration par VM :</li>
</ul>
<pre><code class="language-powershell">Get-VMIntegrationService -VMName "<VM>"
</code></pre>
<p>Dans l’invité (Windows) :</p>
<pre><code class="language-cmd">reg query "HKLM\Software\Microsoft\Virtual Machine\Auto" /v IntegrationServicesVersion
</code></pre>
<h3>Solution 6 — Plan d’alimentation & micro‑réglages de performance</h3>
<p>Sur un hôte de virtualisation axé performance, privilégiez une latence plus déterministe :</p>
<ul>
<li><strong>Plan d’alimentation : High performance</strong> (ou « Performances élevées ») ; évite les baisses agressives de fréquence.</li>
<li>Vérifiez BIOS/UEFI (microcodes CPU, C‑states) et pilotes (chipset, NIC, stockage).</li>
</ul>
<pre><code class="language-cmd"># Lister les schémas d'alimentation
powercfg /L
# Activer "Performances élevées" (GUID classique)
powercfg /S 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c </code></pre>
<h3>Solution 7 — NUMA : aligner vCPU et mémoire, limiter le « spanning »</h3>
<p>Visez une topologie où chaque VM tient dans un <strong>seul nœud NUMA physique</strong> (quand c’est possible). Cela limite les latences mémoire inter‑nœuds.</p>
<ul>
<li><strong>vNUMA</strong> : laissez Hyper‑V exposer automatiquement la vNUMA lorsque la VM dépasse un nœud (en vCPU/mémoire). Les SGBD et moteurs gourmands en mémoire en tirent parti.</li>
<li><strong>Spanning</strong> : si vos VM rentrent dans un nœud, désactivez le <em>NUMA spanning</em> au niveau de l’hôte ; cela stabilise la localité.</li>
</ul>
<pre><code class="language-powershell"># Vérifier/désactiver le NUMA spanning côté hôte
Get-VMHost | Select-Object ComputerName, NumaSpanningEnabled
Set-VMHost -NumaSpanningEnabled $false
</code></pre>
<h3>Solution 8 — Vérifications additionnelles utiles</h3>
<ul>
<li><strong>Compatibilité processeur (migration)</strong> : évitez d’activer « <strong>Migrate to a physical computer with a different processor version</strong> » sans nécessité. Ce mode masque des fonctionnalités CPU et peut réduire la performance.</li>
<li><strong>Affinités et réservations</strong> : ne « pincez » pas vos vCPU à un petit sous‑ensemble de LP si l’hôte n’est pas homogène (risque de hotspots).</li>
<li><strong>Méthodologie de bench</strong> : comparez <em>à topologie identique</em> (threads par cœur, sockets virtuels), même plan d’alim, même profil d’IRQ, mêmes versions d’outils et de microcodes.</li>
</ul>
<h2>Que voit l’invité selon votre paramétrage ?</h2>
<table>
<thead>
<tr>
<th>Paramètre VM</th>
<th>Topologie vue par l’invité</th>
<th>Cas d’usage</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-HwThreadCountPerCore 0</code> (hérite, HT actif)</td>
<td>Ex. 4 vCPU → 2 cœurs × 2 threads</td>
<td>Densité maximale, charges tolérant l’SMT</td>
</tr>
<tr>
<td><code>-HwThreadCountPerCore 1</code></td>
<td>4 vCPU → 4 cœurs × 1 thread</td>
<td>Licences par cœur, latence/débit par cœur</td>
</tr>
<tr>
<td><code>-HwThreadCountPerCore 2</code></td>
<td>4 vCPU → 2 cœurs × 2 threads (explicite)</td>
<td>Tests, alignement volontaire avec l’HT</td>
</tr>
</tbody>
</table>
<h2>Comparatif des planificateurs Hyper‑V</h2>
<table>
<thead>
<tr>
<th>Type</th>
<th>Par défaut</th>
<th>Caractéristique</th>
<th>Quand l’utiliser</th>
</tr>
</thead>
<tbody>
<tr>
<td>Classic</td>
<td>Windows Server 2016</td>
<td>Non SMT‑aware, comportement « historique »</td>
<td>Compat hérité ou besoin de reproduire 2016</td>
</tr>
<tr>
<td>Core</td>
<td>2019/2022</td>
<td>SMT‑aware, meilleure isolation inter‑VM</td>
<td>Recommandé par défaut (sécurité + stabilité)</td>
</tr>
</tbody>
</table>
<h2>Méthodologie de benchmark juste (éviter les comparaisons biaisées)</h2>
<ol>
<li><strong>Fixez la topologie</strong> : pour comparer 2016 vs 2022, forcez <code>-HwThreadCountPerCore 1</code> dans la VM 2022 (si 2016 exposait 4 cœurs pleins).</li>
<li><strong>Uniformisez l’énergie</strong> : <em>High performance</em> sur l’hôte, pas d’outils qui changent le P‑state en tâche de fond.</li>
<li><strong>Stabilisez l’environnement</strong> : même pilotes, même microcodes, même version de bench, mode « quiet » (pas de patching ou sauvegarde en arrière‑plan).</li>
<li><strong>Mesurez côté hôte</strong> (LP) et côté invité (VP) pour comprendre d’où vient la latence.</li>
<li><strong>Élargissez l’analyse</strong> : latence mémoire, I/O, NUMA, contention lock des applis (profils CPU).</li>
</ol>
<h2>Scripts d’audit prêts à l’emploi</h2>
<h3>Audit hôte : scheduler, HT, alimentation, NUMA</h3>
<pre><code class="language-powershell">$hostInfo = Get-CimInstance Win32_Processor | Select-Object Name, NumberOfCores, NumberOfLogicalProcessors
$sched = Get-WinEvent -FilterHashTable @{ProviderName="Microsoft-Windows-Hyper-V-Hypervisor"; ID=2} -MaxEvents 1
$power = powercfg /GETACTIVESCHEME
$numa = Get-VMHost | Select-Object ComputerName, NumaSpanningEnabled
\$report = \[pscustomobject]@{
CPU\_Name = \$hostInfo.Name -join '; '
Cores\_Total = (\$hostInfo | Measure-Object NumberOfCores -Sum).Sum
LogicalProcessors\_Total = (\$hostInfo | Measure-Object NumberOfLogicalProcessors -Sum).Sum
Scheduler\_LastEvent = \$sched.Message
PowerPlan\_Active = (\$power -join ' ')
NumaSpanningEnabled = \$numa.NumaSpanningEnabled
}
\$report | Format-List </code></pre>
<h3>Audit VM : topologie SMT virtuelle, version, compatibilité CPU</h3>
<pre><code class="language-powershell">Get-VM | ForEach-Object {
$p = Get-VMProcessor -VMName $_.Name
[pscustomobject]@{
VMName = $_.Name
State = $_.State
vCPU = $p.Count
HwThreadCountPerCore = $p.HwThreadCountPerCore
VMConfigVersion = $_.Version
CPU_CompatibilityMode = (Get-VMProcessor -VMName $_.Name).CompatibilityForMigrationEnabled
}
} | Format-Table -AutoSize
</code></pre>
<h3>Forcer la topologie « cœurs pleins » pour un lot de VM</h3>
<pre><code class="language-powershell">"SQL01","SQL02","ETL01" | ForEach-Object {
Set-VMProcessor -VMName $_ -HwThreadCountPerCore 1
Get-VMProcessor -VMName $_ | Select-Object VMName, Count, HwThreadCountPerCore
}
</code></pre>
<h2>Cas d’usage et recommandations rapides</h2>
<table>
<thead>
<tr>
<th>Type de charge</th>
<th>Objectif</th>
<th>Topologie conseillée</th>
<th>Autres réglages</th>
</tr>
</thead>
<tbody>
<tr>
<td>SGBD licencié par cœur (SQL, Oracle)</td>
<td>Débit par cœur, conformité licence</td>
<td><code>-HwThreadCountPerCore 1</code> (cœurs pleins)</td>
<td>vNUMA alignée, High performance</td>
</tr>
<tr>
<td>Web/app stateless</td>
<td>Densité + élasticité</td>
<td>Héritage (<code>0</code>) si stabilité OK</td>
<td>Autoscaling, monitor Wait Time</td>
</tr>
<tr>
<td>Batch/ETL/Compression</td>
<td>Temps de mur le plus bas</td>
<td><code>1</code> ou <code>0</code> selon profil</td>
<td>Tests A/B avec et sans SMT virtuel</td>
</tr>
<tr>
<td>BI/Analytics en mémoire</td>
<td>Localité mémoire</td>
<td><code>1</code>, vNUMA exposée</td>
<td>Désactiver NUMA spanning si possible</td>
</tr>
</tbody>
</table>
<h2>FAQ express</h2>
<p><strong>Faut‑il désactiver l’Hyper‑Threading sur l’hôte ?</strong><br>
Généralement non : beaucoup de charges en bénéficient, et l’HT reste utile pour la densité. Préférez <strong>désactiver l’SMT virtuel par VM</strong> via <code>-HwThreadCountPerCore 1</code> pour les charges sensibles/licenciées.</p>
<p><strong>Mes alertes « CPU Wait Time per Dispatch » à 50–80 µs signifient‑elles un problème ?</strong><br>
Pas forcément. Cette plage peut être nominale selon la densité et la charge. Observez la tendance, corrélez aux compteurs LP/VP et aux temps de réponse applicatifs.</p>
<p><strong>Comment vérifier la version des Integration Services ?</strong><br>
Depuis 2016, ils sont intégrés à l’OS invité et mis à jour par Windows Update. Contrôlez l’état via <code>Get-VMIntegrationService</code> et, dans l’invité Windows, la valeur <code>IntegrationServicesVersion</code> du registre.</p>
<p><strong>Changer le scheduler en « classic » résoudra‑t‑il tout ?</strong><br>
Il peut reproduire un comportement ancien, mais au prix d’un compromis de sécurité. Dans la majorité des cas, <strong>garder « core »</strong> et <strong>ajuster l’SMT virtuel par VM</strong> donne le meilleur équilibre.</p>
<h2>Procédure guidée (recette rapide)</h2>
<ol>
<li><strong>Coupez l’SMT virtuel</strong> sur les VM sensibles/licenciées :
<pre><code class="language-powershell">Set-VMProcessor -VMName "<VM-critique>" -HwThreadCountPerCore 1
Get-VMProcessor -VMName "<VM-critique>" | Select VMName, HwThreadCountPerCore
</code></pre>
</li>
<li><strong>Confirmez le scheduler</strong> (et laissez « core » par défaut) :
<pre><code class="language-powershell">Get-WinEvent -FilterHashTable @{ProviderName="Microsoft-Windows-Hyper-V-Hypervisor"; ID=2} -MaxEvents 1
# Option (à évaluer) : bcdedit /set hypervisorschedulertype classic
</code></pre>
</li>
<li><strong>Activez « High performance »</strong> sur l’hôte ; mettez à jour firmware/pilotes.</li>
<li><strong>Alignez la vNUMA</strong> : visez 1 nœud NUMA par VM, désactivez le spanning si possible.</li>
<li><strong>Laissez Windows Update</strong> gérer les Integration Services (2016+), vérifiez qu’ils sont actifs.</li>
</ol>
<h2>Contrôles supplémentaires (to‑do technique)</h2>
<ul>
<li><strong>État HT/SMT de l’hôte</strong> :
<pre><code class="language-powershell">Get-CimInstance Win32_Processor | Select Name, NumberOfCores, NumberOfLogicalProcessors
</code></pre>
</li>
<li><strong>Compatibilité CPU</strong> (éviter si non nécessaire) :
<pre><code class="language-powershell">Get-VMProcessor -VMName "<VM>" | Select VMName, CompatibilityForMigrationEnabled
Version de VM :
Get-VM | Format-Table Name, Version
Perfmon côté hôte : surveillez Hyper‑V Hypervisor Logical Processor \ % Total Run Time et les files d’attente hautes.
Exemples concrets d’impact
- SQL Server Standard licencié par cœur, VM à 4 vCPU sur hôte HT : sans réglage, l’invité voit 2 cœurs × 2 threads → la parallélisation « per core » plafonne. Avec
-HwThreadCountPerCore 1
: 4 cœurs pleins, meilleure stabilité du MAXDOP et des temps de réponse sous charge mixte. - Moteur de compression (zstd/xz) : certains profils favorisent des cœurs pleins vs 2 threads SMT concurrentiels ; le débit unitaire progresse avec
1
thread par cœur exposé à l’invité. - Workers web : densité prioritaire ; l’héritage SMT (
0
) reste efficace si la latence P99 est maîtrisée. À contrôler avec la télémétrie applicative.
À retenir
- La « baisse » de performance observée après migration vers 2022 est, la plupart du temps, liée au core scheduler combiné à l’héritage SMT : une VM 4 vCPU devient de fait « 2 cœurs × 2 threads » côté invité.
- Corrigez avec
Set-VMProcessor -HwThreadCountPerCore 1
sur les VM sensibles/licenciées, tout en gardant l’HT actif au niveau de l’hôte pour les autres charges. - Les alertes « CPU Wait Time per Dispatch » à 50–80 µs ne sont pas forcément inquiétantes ; lisez‑les dans leur contexte et surveillez la tendance.
- Consolidez les fondamentaux : High performance sur l’hôte, vNUMA alignée, VM à jour (version de configuration & Integration Services), et compatibilité CPU désactivée si inutile.
Annexe : check‑list opérationnelle
- ✅ Core scheduler confirmé (événement Hyper‑V ID 2)
- ✅ Plan d’alimentation « High performance » actif
- ✅
-HwThreadCountPerCore 1
sur VM critiques/licenciées - ✅ NUMA spanning désactivé si VM tient dans 1 nœud
- ✅ Version de configuration VM mise à jour
- ✅ Intégration Services à jour (Windows Update) / LIS à jour côté Linux
- ✅ Compatibilité CPU non activée par défaut
- ✅ Perfmon : Wait Time, %Run Time LP, Ready/Preemption, tendance OK
Modèle de communication vers les équipes applicatives
« À la suite de la migration Hyper‑V 2016 → 2022, nous avons observé que les VM héritent de la topologie SMT du nouvel hôte (core scheduler). Cela peut présenter 4 vCPU comme 2 cœurs × 2 threads dans l’invité, ce qui impacte certaines charges par cœur. Nous avons corrigé les VM critiques en exposant 1 thread par cœur (-HwThreadCountPerCore 1
), maintenu la sécurité (scheduler : core) et aligné NUMA/énergie. Merci de revalider vos SLA ; nous continuons de monitorer CPU Wait Time per Dispatch et la télémétrie applicative. »
Conclusion
Le passage à Windows Server 2022 ne dégrade pas intrinsèquement les performances CPU des VM : il change leur topologie vue par l’invité. En vous appuyant sur le core scheduler (recommandé), en fixant l’SMT virtuel à 1 pour les VM sensibles, et en appliquant quelques réglages d’hôte (énergie, NUMA, drivers), vous retrouvez des performances conformes aux attentes — en cohérence avec vos licences par cœur et vos exigences de sécurité.