<div dir="auto">I have 2 Xeon 5540s (4 physical and 4 logical per CPU) currently one entire CPU is dedicated to the vm (basically what it says in numa 0 in lscpu) I didn't quite get the guide, what would be the best setup to get the most out of the vm for gaming? Or is that the best configuration I have atm</div><div class="gmail_extra"><br><div class="gmail_quote">On Feb 2, 2017 12:35 PM, "Jan Wiele" <<a href="mailto:jan@wiele.org">jan@wiele.org</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Thomas,<br>
<br>
awesome work! I've changed my (gaming-)setup (2x Xeon E5-2670 (8 real cores per CPU)) to the following:<br>
<br>
VM1 and VM2:<br>
Each gets 4 real cores on CPU0; Emulator-Thread is pinned to the respective Hyper-Threading cores.<br>
<br>
VM3:<br>
6 real cores on CPU1; Emulator-Thread is pinned to the respective Hyper-Threading cores.<br>
<br>
Host:<br>
2 real cores on CPU1; 2 Hyperthreaded cores.<br>
<br>
<br>
I've chosen this layout ("Low latency setup"), since it fits my setup the most. Alternatively I could have pinned the emulator threads to the host ("Balanced setup, emulator with host"), but this would result in some cross-node-traffic, which I wanted to prevent. Additionally some benchmarks show that hyperthreading does not improve the gaming performance [1] by much.<br>
<br>
With my new setup, I ran DPC Latency Checker [2] and saw on all three VMs timings around 1000us. However, LatencyMon [3] showed most of the time much lower values (<100us). Can they be compared?<br>
<br>
LatencyMon also showed me that the USB2 driver has a long ISR. Changing this to USB3 in libvirt fixed that.<br>
<br>
<br>
Cheers,<br>
Jan<br>
<br>
[1] <a href="https://www.techpowerup.com/forums/threads/gaming-benchmarks-core-i7-6700k-hyperthreading-test.219417/" rel="noreferrer" target="_blank">https://www.techpowerup.com/fo<wbr>rums/threads/gaming-benchmarks<wbr>-core-i7-6700k-hyperthreading-<wbr>test.219417/</a><br>
[2] <a href="http://www.thesycon.de/eng/latency_check.shtml" rel="noreferrer" target="_blank">http://www.thesycon.de/eng/lat<wbr>ency_check.shtml</a><br>
[3] <a href="http://www.resplendence.com/latencymon" rel="noreferrer" target="_blank">http://www.resplendence.com/la<wbr>tencymon</a><br>
<br>
Am 01.02.2017 um 16:46 schrieb Thomas Lindroth:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
A while ago there was a conversation on the #vfio-users irc channel about how to<br>
use cpuset/pinning to get the best latency and performance. I said I would run<br>
some tests and eventually did. Writing up the result took a lot of time and<br>
there are some more test I want to run to verify the results but don't have time<br>
to do that now. I'll just post what I've concluded instead. First some theory.<br>
<br>
Latency in a virtual environment have many difference causes.<br>
* There is latency in the hardware/bios like system management interrupts.<br>
* The host operating system introduce some latency. This is often because the<br>
  host won't schedule the VM when it wants to run.<br>
* The emulator got some latency because of things like nested page tables and<br>
  handling of virtual hardware.<br>
* The guest OS introduce it's own latency when the workload wants to run but the<br>
  guest scheduler won't schedule it.<br>
<br>
Point 1 and 4 are latencies you get even on bare metal but point 2 and 3 is<br>
extra latency caused by the virtualisation. This post is mostly about reducing<br>
the latency of point 2.<br>
<br>
I assume you are already familiar with how this is usually done. By using cpuset<br>
you can reserve some cores for exclusive use by the VM and put all system<br>
processes on a separate housekeeping core. This allows the VM to run whenever it<br>
wants which is good for latency but the downside is the VM can't use the<br>
housekeeping core so performance is reduced.<br>
<br>
By running pstree -p when the VM is running you get some output like this:<br>
...<br>
─qemu-system-x86(4995)─┬─{CPU 0/KVM}(5004)<br>
                       ├─{CPU 1/KVM}(5005)<br>
                       ├─{CPU 2/KVM}(5006)<br>
                       ├─{CPU 3/KVM}(5007)<br>
                       ├─{CPU 4/KVM}(5008)<br>
                       ├─{CPU 5/KVM}(5009)<br>
                       ├─{qemu-system-x86}(4996)<br>
                       ├─{qemu-system-x86}(5012)<br>
                       ├─{qemu-system-x86}(5013)<br>
                       ├─{worker}(5765)<br>
                       └─{worker}(5766)<br>
<br>
Qemu spawn a bunch of threads for different things. The "CPU #/KVM" threads runs<br>
the actual guest code and there is one for each virtual cpu. I call them<br>
"VM threads" from here on. The qemu-system-x86 threads are used to emulate<br>
virtual hardware and is called the emulator in libvirt terminology. I call them<br>
"emulator threads". The worker threads are probably what libvirt calls iothreads<br>
but I treat them the same as the emulator threads and refer to them both as<br>
"emulator threads".<br>
<br>
My cpu is a i7-4790K with 4 hyper threaded cores for a total of 8 logical cores.<br>
A lot of people here probably have something similar. Take a look in<br>
/proc/cpuinfo to see how it's laid out. I number my cores like cpuinfo where I<br>
got physical cores 0-3 and logical cores 0-7. pcore 0 corresponds to lcore 0,4<br>
and pcore 1 is lcore 1,5 and so on.<br>
<br>
The goal is to partition the system processes, VM threads and emulator threads<br>
on these 8 lcores to get good latency and acceptable performance but to do that<br>
I need a way to measure latency. Mainline kernel 4.9 got a new latency tracer<br>
called hwlat. It's designed to measure hardware latencies like SMI but if you<br>
run it in a VM you get all latencies below the guest (point 1-3 above). Hwlat<br>
bypasses the normal cpu scheduler so it won't measure any latency from the guest<br>
scheduler (point 4). It basically makes it possible to focus on just the VM<br>
related latencies.<br>
<a href="https://lwn.net/Articles/703129/" rel="noreferrer" target="_blank">https://lwn.net/Articles/70312<wbr>9/</a><br>
<br>
We should perhaps also discuss how much latency is too much. That's up for<br>
debate but the windows DPC latency checker lists 500us as green, 1000us as<br>
yellow and 2000us as red. If a game runs at 60fps it has a deadline of 16.7ms to<br>
render a frame. I'll just decide that 1ms (1000us) is the upper limit for what I<br>
can tolerate.<br>
<br>
One of the consequences of how hwlat works is that it also fails to notice a lot<br>
of the point 3 types of latencies. Most of the latency in point 3 is caused by<br>
vm-exits. That's when the guest do something the hardware virtualisation can't<br>
handle and have to rely on kvm or qemu to emulate the behaviour. This is a lot<br>
slower than real hardware but it mostly only happens when the guest tries to<br>
access hardware resources, so I'll call it IO-latency. The hwlat tracer only<br>
sits and spins in kernel space and never touch any hardware by itself. Since<br>
hwlat don't trigger vm-exits it also can't measure latencies from that so it<br>
would be good to have something else that could. They way I rigged things up is<br>
to set the virtual disk controller to ahci. I know that has to be emulated by<br>
qemu. I then added a ram block device from /dev/ram* to the VM as a virtual<br>
disk. I can then run the fio disk benchmark in the VM on that disk to trigger<br>
vm-exits and get a report on the latency from fio. It's not a good solution but<br>
it's the best I could come up with.<br>
<a href="http://freecode.com/projects/fio" rel="noreferrer" target="_blank">http://freecode.com/projects/f<wbr>io</a><br>
<br>
=== Low latency setup ===<br>
<br>
Let's finally get down to business. The first setup I tried is configured for<br>
minimum latency at the expense of performance.<br>
<br>
The virtual cpu in this setup got 3 cores and no HT. The VM threads are pinned<br>
to lcore 1,2,3. The emulator threads are pinned to lcore 5,6,7. That leaves<br>
pcore 0 which is dedicated to the host using cpuset.<br>
<br>
Here is the layout in libvirt xml<br>
<vcpupin vcpu='0' cpuset='1'/><br>
<vcpupin vcpu='1' cpuset='2'/><br>
<vcpupin vcpu='2' cpuset='3'/><br>
<emulatorpin cpuset='5-7'/><br>
<topology sockets='1' cores='3' threads='1'/><br>
<br>
And here are the result of hwlat (all hwlat test run for 30 min each). I used a<br>
synthetic load to test how the latencies changed under load. I use the program<br>
stress as synthetic load on both guest and host<br>
(stress --vm 1 --io 1 --cpu 8 --hdd 1).<br>
<br>
                         mean     stdev    max(us)<br>
host idle, VM idle:   17.2778   15.6788     70<br>
host load, VM idle:   21.4856   20.1409     72<br>
host idle, VM load:   19.7144   18.9321    103<br>
host load, VM load:   21.8189   21.2839    139<br>
<br>
As you can see the load on the host makes little difference for the latency.<br>
The cpuset isolation works well. The slight decrease of the mean might be<br>
because of reduced memory bandwidth. Putting the VM under load will increase the<br>
latency a bit. This might seem odd since the idea of using hwlat was to bypass<br>
the guest scheduler thereby making the latency independent of what is running in<br>
the guest. What is probably happening is that the "--hdd" part of the stress<br>
access the disk and this makes the emulator threads run. They are pinned to the<br>
HT siblings of the VM threads and thereby slightly impact the latency of them.<br>
Overall the latency is very good in this setup.<br>
<br>
fio (us) min=40, max=1306, avg=52.81, stdev=12.60 iops=18454<br>
Here is the result of the io latency test with fio. Since the emulator treads<br>
are running mostly isolated on their own siblings this result must be considered<br>
good.<br>
<br>
=== Low latency setup, with realtime ===<br>
<br>
In an older post to the mailing list I said "The NO_HZ_FULL scheduler mode only<br>
works if a single process wants to run on a core. When the VM thread runs as<br>
realtime priority it can starve the kernel threads for long period of time and<br>
the scheduler will turn off NO_HZ_FULL when that happens since several processes<br>
wants to run. To get the full advantage of NO_HZ_FULL don't use realtime<br>
priority."<br>
<br>
Let's see how much impact this really has. The idea behind realtime pri is to<br>
always give your preferred workload priority over unimportant workloads. But to<br>
make any difference there has to be an unimportant workload to preempt. Cpuset<br>
is a great way to move unimportant processes to a housekeeping cpu but<br>
unfortunately the kernel got some pesky kthreads that refuse to migrate. By<br>
using realtime pri on the VM threads I should be able to out-preempt the kernel<br>
threads and get lower latency. In this test I used  the same setup as above but<br>
I used schedtool to set round-robin pri 1 on all VM related threads.<br>
<br>
                         mean     stdev    max(us)<br>
host idle, VM idle:   17.6511   15.3028     61<br>
host load, VM idle:   20.2400   19.6558     57<br>
host idle, VM load:   18.9244   18.8119    108<br>
host load, VM load:   20.4228   21.0749    122<br>
<br>
The result is mostly the same. Those few remaining kthreads that I can't disable<br>
or migrate apparently doesn't make much difference on latency.<br>
<br>
=== Balanced setup, emulator with VM threads ===<br>
<br>
3 cores isn't a lot these days and some games like Mad max and Rise of the tomb<br>
raider max out the cpu in the low latency setup. This results in big frame drops<br>
when that happens. The setup below with a virtual 2 core HT cpu would probably<br>
give ok latency but the addition of hyper threading usually only give 25-50%<br>
extra performance for real world workloads so this setup would generally be<br>
slower than the low latency setup. I didn't bother to test it.<br>
<vcpupin vcpu='0' cpuset='2'/><br>
<vcpupin vcpu='1' cpuset='6'/><br>
<vcpupin vcpu='2' cpuset='3'/><br>
<vcpupin vcpu='3' cpuset='7'/><br>
<emulatorpin cpuset='1,5'/><br>
<topology sockets='1' cores='2' threads='2'/><br>
<br>
To get better performance I need at least a virtual 3 core HT cpu but if the<br>
host use pcore 0 and the VM threads use pcore 1-3 where will the emulator<br>
threads run? I could overallocate the system by having the emulator threads<br>
compete with the VM threads or I could overallocate the system by having the<br>
emulator threads compete with the host processes. Lets try to run the emulator<br>
with the VM treads first.<br>
<br>
<vcpupin vcpu='0' cpuset='1'/><br>
<vcpupin vcpu='1' cpuset='5'/><br>
<vcpupin vcpu='2' cpuset='2'/><br>
<vcpupin vcpu='3' cpuset='6'/><br>
<vcpupin vcpu='4' cpuset='3'/><br>
<vcpupin vcpu='5' cpuset='7'/><br>
<emulatorpin cpuset='1-3,5-7'/><br>
<topology sockets='1' cores='3' threads='2'/><br>
<br>
The odd ordering for vcpupin is done because Intel cpus lay out HT siblings as<br>
lcore[01234567] = pcore[01230123] but qemu lays out the virtual cpu as<br>
lcore[012345] = pcore[001122]. To get a 1:1 mapping I have to order them like<br>
that.<br>
<br>
                         mean     stdev    max(us)<br>
host idle, VM idle:   17.4906   15.1180     89<br>
host load, VM idle:   22.7317   19.5327     95<br>
host idle, VM load:   82.3694  329.6875   9458<br>
host load, VM load:  141.2461 1170.5207  20757<br>
<br>
The result is really bad. It works ok as long as the VM is idle but as soon as<br>
it's under load I get bad latencies. The reason is likely that the stressor<br>
accesses the disk which activates the emulator and in this setup the emulator<br>
can preempt the VM threads. We can check if this is the case by running the<br>
stress without "--hdd".<br>
<br>
                                       mean     stdev    max(us)<br>
host load, VM load(but no --hdd):   57.4728  138.8211   1345<br>
<br>
The latency is reduced quite a bit but it's still high. It's likely still the<br>
emulator threads preempting the VM threads. Accessing the disk is just one of<br>
many things the VM can do to activate the emulator.<br>
<br>
fio (us) min=41, max=7348, avg=62.17, stdev=14.99 iops=15715<br>
io latency is also a lot worse compared to the low latency setup. The reason is<br>
the VM threads can preempt the emulator threads while they are emulating the<br>
disk drive.<br>
<br>
=== Balanced setup, emulator with host ===<br>
<br>
Pairing up the emulator threads and VM threads was a bad idea so lets try<br>
running the emulator on the core reserved for the host. Since the VM threads run<br>
by themselves in this setup we would expect to get good hwlat latency but the<br>
emulator threads can be preempted by host processes so io latency might suffer.<br>
Lets start by looking at the io latency.<br>
<br>
fio (us) min=40, max=46852, avg=61.55, stdev=250.90 iops=15893<br>
<br>
Yup, massive io latency. Here is a situation were realtime pri could help.<br>
If the emulator threads get realtime pri they can out-preempt the host<br>
processes. Lets try that.<br>
<br>
fio (us) min=38, max=2640, avg=53.72, stdev=13.61  iops=18140<br>
<br>
That's better but it's not as good as the low latency setup where the emulator<br>
threads got their own lcore. To reduce the latency even more we could try to<br>
split pcore 0 in two and run host processes on lcore 0 and the emulator threads<br>
on lcore 4. But this doesn't leave much cpu for the emulator (or the host).<br>
<br>
fio (us) min=44, max=1192, avg=56.07, stdev=8.52 iops=17377<br>
<br>
The max io latency now decreased to the same level as the low latency setup.<br>
Unfortunately the number of iops also decreased a bit (down 5.8% compared to the<br>
low latency setup). I'm guessing this is because the emulator threads don't get<br>
as much cpu power in this setup.<br>
<br>
                         mean     stdev    max(us)<br>
host idle, VM idle:   18.3933   15.5901    106<br>
host load, VM idle:   20.2006   18.8932     77<br>
host idle, VM load:   23.1694   22.4301    110<br>
host load, VM load:   23.2572   23.7288    120<br>
<br>
Hwlat latency is comparable to the low latency setup so this setup gives a good<br>
latency / performance trade-off<br>
<br>
=== Max performance setup ===<br>
<br>
If 3 cores with HT isn't enough I suggest you give up but for comparison let's<br>
see what happens if we mirror the host cpu in the VM. Now we have no room at all<br>
for the emulator or the host processes so I let them schedule free.<br>
<vcpupin vcpu='0' cpuset='0'/><br>
<vcpupin vcpu='1' cpuset='4'/><br>
<vcpupin vcpu='2' cpuset='1'/><br>
<vcpupin vcpu='3' cpuset='5'/><br>
<vcpupin vcpu='4' cpuset='2'/><br>
<vcpupin vcpu='5' cpuset='6'/><br>
<vcpupin vcpu='6' cpuset='3'/><br>
<vcpupin vcpu='7' cpuset='7'/><br>
<emulatorpin cpuset='0-7'/><br>
<topology sockets='1' cores='4' threads='2'/><br>
<br>
                           mean      stdev    max(us)<br>
host idle, VM idle:    185.4200   839.7908   6311<br>
host load, VM idle:   3835.9333  7836.5902  97234<br>
host idle, VM load:   1891.4300  3873.9165  31015<br>
host load, VM load:   8459.2550  6437.6621  51665<br>
<br>
fio (us) min=48, max=112484, avg=90.41, stdev=355.10 iops=10845<br>
<br>
I only ran these tests for 10 min each. That's all that was needed. As you can<br>
see it's terrible. I'm afraid that many people probably run a setup similar to<br>
this. I ran like this myself for a while until I switched to libvirt and started<br>
looking into pinning. Realtime pri would probably help a lot here but realtime<br>
in this configuration is potentially dangerous. Workloads on the guest could<br>
starve the host and depending on how the guest gets its input a reset using the<br>
hardware reset button could be needed to get the system back.<br>
<br>
=== Testing with games ===<br>
<br>
I want low latency for gaming so it would make sense to test the setups with<br>
games. This turns out to be kind of tricky. Games are complicated and<br>
interpreting the results can be hard. <a href="https://i.imgur.com/NIrXnkt.png" rel="noreferrer" target="_blank">https://i.imgur.com/NIrXnkt.pn<wbr>g</a> as an<br>
example here is a percentile plot of the frametimes in the built in benchmark of<br>
rise of the tomb raider taken with fraps. The performance and balanced setups<br>
looks about the same at lower percentiles but the low latency setup is a lot<br>
lower. This means that the low latency setup, with is the weakest in terms of<br>
cpu power, got higher frame rate for some parts of the benchmark. This doesn't<br>
make sense at first. It only starts to make sense if I pay attention to the<br>
benchmark while it's running. Rise of the tomb raider loads in a lot of geometry<br>
dynamically and the low latency setup can't keep up. It has bad pop-in of<br>
textures and objects so the scene the gpu renders is less complicated than the<br>
other setups. Less complicated scene results in higher frame rate. An odd<br>
counter intuitive result.<br>
<br>
Overall the performance and balanced setups have the same percentile curve for<br>
lower percentiles in every game I tested. This tells me that the balanced setup<br>
got enough cpu power for all games I've tried. They only differ at higher<br>
percentile due to latency induced framedrops. The performance setup always have<br>
the worst max frametime in every game so there is no reason to use it over the<br>
balanced setup. The performance setup also have crackling sound in several games<br>
over hdmi audio even with MSI enabled. Which setup got the lowest max framtime<br>
depends on the workload. If the game max out the cpu of the low latency setup<br>
the max framtime will be worse than the balanced setup, if not the low latency<br>
setup got the best latency.<br>
<br>
=== Conclusion ===<br>
<br>
The balanced setup (emulators with host) doesn't have the best latency in every<br>
workload but I haven't found any workload where it performs poorly in regards to<br>
max latency, io latency or available cpu power. Even in those workloads where<br>
another setup performed better the balanced setup was always close. If you are<br>
too lazy to switch setups depending on the workload use the balanced setup as<br>
the default configuration. If your cpu isn't a 4 core with HT finding the best<br>
setup for your cpu is left as an exercise for the reader.<br>
<br>
=== Future work ===<br>
<br>
<a href="https://vfio.blogspot.se/2016/10/how-to-improve-performance-in-windows-7.html" rel="noreferrer" target="_blank">https://vfio.blogspot.se/2016/<wbr>10/how-to-improve-performance-<wbr>in-windows-7.html</a><br>
This was a nice trick for forcing win7 to use TSC. Just one problem, turns out<br>
it doesn't work if hyper threading is enabled. Any time I use a virtual cpu with<br>
threads='2' win7 will revert to using the acpi_pm. I've spent a lot of time<br>
trying to work around the problem but failed. I don't even know why hyper<br>
threading would make a difference for TSC. Microsoft's documentation is<br>
amazingly unhelpful. But even when the guest is hammering the acpi_pm timer the<br>
balanced setup gives better performance than the low latency setup but I'm<br>
afraid the reduced resolution and extra indeterminism of the acpi_pm timer might<br>
result in other problems. This is only a problem in win7 because modern versions<br>
if windows should use hypervclock. I've read somewhere that it might be possible<br>
to modify OVMF to work around the bug in win7 that prevents hyperv from working.<br>
With that modification it might be possible to use hypervclock in win7.<br>
Perhaps I'll look into that in the future. In the mean time I'll stick with the<br>
balanced setup despite the use of acpi_pm.<br>
<br>
______________________________<wbr>_________________<br>
vfio-users mailing list<br>
<a href="mailto:vfio-users@redhat.com" target="_blank">vfio-users@redhat.com</a><br>
<a href="https://www.redhat.com/mailman/listinfo/vfio-users" rel="noreferrer" target="_blank">https://www.redhat.com/mailman<wbr>/listinfo/vfio-users</a><br>
<br>
</blockquote>
<br>
______________________________<wbr>_________________<br>
vfio-users mailing list<br>
<a href="mailto:vfio-users@redhat.com" target="_blank">vfio-users@redhat.com</a><br>
<a href="https://www.redhat.com/mailman/listinfo/vfio-users" rel="noreferrer" target="_blank">https://www.redhat.com/mailman<wbr>/listinfo/vfio-users</a><br>
</blockquote></div></div>