技術(shù)分享:虛擬化環(huán)境下解析Windows IO性能
隨著云計(jì)算技術(shù)與服務(wù)的發(fā)展和進(jìn)步,越來越多的客戶選擇將業(yè)務(wù)部署到云端。但由于引入了虛擬化層,在業(yè)務(wù)部署過程中經(jīng)常會(huì)遇到IO問題,通常也不易調(diào)試。本文主要介紹利用perf、systemtap等工具,幫助一位托管云客戶調(diào)試IO性能問題,來分析虛擬環(huán)境下Windows IO的性能。
問題出現(xiàn)
有一次,托管云客戶自己搭建了虛擬化環(huán)境,在同一臺(tái)宿主機(jī)上創(chuàng)建windows 2008 R2 和 Centos6.5虛擬機(jī),用fio分別測(cè)試其隨機(jī)讀性能,windows 2008 R2的IOPS大約在18K,而Linux的IOPS卻可以達(dá)到100K左右。
客戶測(cè)試用的fio 配置
- [global]
- ioengine=windowsaio
- direct=1
- iodepth=64
- thread=1
- size=20g
- numjobs=1
- [4k]
- bs=4k
- filename=d:test.img
- rw=randread
測(cè)試結(jié)果
- win_fio1
云主機(jī)IO棧
io stack
云主機(jī)環(huán)境下,整個(gè)IO棧相對(duì)較長(zhǎng),涉及到Guest OS中的應(yīng)用層/文件系統(tǒng)/Block層以及驅(qū)動(dòng)層,虛擬化層,宿主機(jī)OS文件系統(tǒng)/Block層以及驅(qū)動(dòng)層。因?yàn)樯婕懊娑?,所以其中任何一個(gè)環(huán)節(jié)出現(xiàn)問題都會(huì)造成性能下降,也為做IO的Tracing增加了難度。
從這次得到的信息來看,首先排除了宿主機(jī)文件系統(tǒng)和Block層以及驅(qū)動(dòng)層的問題,因?yàn)橥瑯忧闆r的配置,Linux系統(tǒng)并沒有問題。
目前主要集中于兩點(diǎn):
- Guest OS(Windows系統(tǒng))
- fio程序
- 文件系統(tǒng)/Block layer
- VirtIO Block驅(qū)動(dòng)
虛擬機(jī)為Guest OS提供的是Virtio Block設(shè)備:
- QEMU
如何排除QEMU的嫌疑?對(duì)于IOPS的性能問題,很容易想到兩種可能性:
- IO延時(shí)過高
- 設(shè)備支持IO隊(duì)列太短
在隊(duì)列的問題方面,Linux和Windows虛擬機(jī)對(duì)應(yīng)的Virtio Block設(shè)備都是一樣的,那么就需要確認(rèn)延時(shí)問題。
QEMU 完成Block IO花了多長(zhǎng)時(shí)間?
幸運(yùn)的是,Stefan Hajnoczi已經(jīng)為QEMU添加了Tracing的特性,因此可以很方便的統(tǒng)計(jì)出QEMU從接收到一個(gè)IO請(qǐng)求到完成所用的具體時(shí)長(zhǎng)。
從上述統(tǒng)計(jì)來看,平均IO完成時(shí)間在130us,由此暫時(shí)排除QEMU 層造成太高延時(shí)的影響。另外,如果關(guān)注這種動(dòng)態(tài)Tracing的overhead,從測(cè)試觀察上大致接近20%。
排除隊(duì)列和延時(shí)問題,可能造成影響的也只有Guest OS了。
- VirtIO Block驅(qū)動(dòng)的問題?至少更新到***穩(wěn)定版本的Virtio-Win驅(qū)動(dòng),仍然存在同樣的問題。
- Windows 文件系統(tǒng)/Block層的問題?原生Windows系統(tǒng)在確認(rèn)后并沒有做任何配置上的修改。
- fio測(cè)試程序的問題?為什么Linux上fio沒有問題呢。
兩種可能性
在性能排查過程中,總是很容易陷入死局,經(jīng)常會(huì)問到底是哪兒出了問題?因此一切可能影響的因素似乎都沒有做任何變動(dòng)。從經(jīng)驗(yàn)來看,大部分性能問題都可以分成兩種可能:
- on cpu
- off cpu
重新來看這個(gè)問題 ,在基本排除IO延時(shí)問題后,對(duì)應(yīng)的問題還有兩種可能性:
- CPU極其忙碌,但是大部分時(shí)間并不是在做IO處理;
- CPU經(jīng)常處于空閑狀態(tài),那相應(yīng)的也沒有主要在處理IO。
注:之所以說到目前為止并不能排除IO延時(shí)的影響,是因?yàn)橹慌懦薗EMU Block層可能的影響,但是還有Guest OS(這次暫時(shí)忽略Guest OS)。
先看測(cè)試過程中,虛擬機(jī)的CPU消耗情況。
- top -H -p 36256
- win_fio1
從上圖來看,QEMU主線程的cpu負(fù)載已經(jīng)達(dá)到90%以上,似乎符合on cpu類問題。通常來說,解決這類問題***的辦法就是用perf進(jìn)程采樣,然后生成火焰圖,因?yàn)槭紫炔榭碈PU具體消耗在什么地方是一個(gè)不錯(cuò)的選擇。
- perf record -a -g -p 36256 sleep 20
生成火焰圖:
- win2008-bad
可以清楚的看到,cpu大部分消耗都是KVM的操作,其中最主要的消耗是vmx_handle_exit。(真實(shí)的火焰圖是一個(gè)矢量圖,用瀏覽器查看很容易確認(rèn))。這里引起vmx_handle_exit主要有兩點(diǎn):
- 訪問IO Port(handle_pio)
- 訪問 MMIO(handle_apic_access)
既然KVM模塊占了大部分,那就更希望了解測(cè)試時(shí)KVM的真實(shí)行為,通過另一個(gè)工具(kvm_stat)可以達(dá)到。
- kvm_pio
除VM Entry和VM Exit事件外,***的就是kvm_pio和 kvm_mmio,說明Windows確實(shí)有大量IO Port和MMIO操作,這也驗(yàn)證了在火焰圖上所得出的結(jié)論。
在虛擬化里,IO Port或者M(jìn)MIO都可能引起VM Exit,甚至是Heavy Exit。如果需要改善性能,一般都會(huì)盡量避免這種情況,至少避免Heavy Exit.
具體訪問哪些IO Port和MMIO導(dǎo)致的VM Exit?
對(duì)于這個(gè)問題,KVM模塊已經(jīng)加了很多trace event,上面的kvm_stat也是利用這些trace event,只是并沒有把具體trace event信息打印出來。為了獲取trace-event的信息,有很多前端工具,如trace-cmd、perf,都是不錯(cuò)的選擇。
• 查看所有kvm模塊的trace event
- [xs3c@devhost1 ]# trace-cmd list -e | grep kvm
- kvmmmu:kvm_mmu_pagetable_walk
- kvmmmu:kvm_mmu_paging_element
- kvmmmu:kvm_mmu_set_accessed_bit
- kvmmmu:kvm_mmu_set_dirty_bit
- kvmmmu:kvm_mmu_walker_error
- kvmmmu:kvm_mmu_get_page
- kvmmmu:kvm_mmu_sync_page
- kvmmmu:kvm_mmu_unsync_page
- kvmmmu:kvm_mmu_zap_page
- kvm:kvm_entry
- kvm:kvm_hypercall
- kvm:kvm_pio
- kvm:kvm_cpuid
- kvm:kvm_apic
- kvm:kvm_exit
- kvm:kvm_inj_virq
- kvm:kvm_inj_exception
- kvm:kvm_page_fault
- kvm:kvm_msr
- kvm:kvm_cr
- kvm:kvm_pic_set_irq
- kvm:kvm_apic_ipi
- kvm:kvm_apic_accept_irq
- kvm:kvm_eoi
- kvm:kvm_pv_eoi
- kvm:kvm_write_tsc_offset
- kvm:kvm_ple_window
- kvm:kvm_vcpu_wakeup
- kvm:kvm_set_irq
- kvm:kvm_ioapic_set_irq
- kvm:kvm_ioapic_delayed_eoi_inj
- kvm:kvm_msi_set_irq
- kvm:kvm_ack_irq
- kvm:kvm_mmio
KVM模塊添加了許多trace event的點(diǎn),這里只抓起其中兩個(gè)——kvm:kvm_pio和kvm:kvm_mmio。
- trace-cmd-pio-mmio
通過統(tǒng)計(jì)發(fā)現(xiàn)主要訪問的:
- IO Port是0x608和0xc050;
- MMIO是0xFEE003xx
經(jīng)由qemu info mtree命令,可以查看IO Port 608、c050以及FEE003xx分別對(duì)應(yīng)的具體設(shè)備。
• IO Port
- 0000000000000608-000000000000060b (prio 0, RW): acpi-tmr 000000000000c040-000000000000c07f (prio 1, RW): virtio-pci
• MMIO
- 00000000fee00000-00000000feefffff (prio 4096, RW): icc-apic-container
c050可以忽略,這個(gè)被Virtio Block來做VM Exit。
到目前為止,可以判斷出wnidows大量讀取ACPI Power Manager Timer以及訪問APIC寄存器,進(jìn)而導(dǎo)致過多vm exit產(chǎn)生,消耗大量CPU資源,因此就可以具體討論兩個(gè)問題:
- 如何減少讀取ACPI PM Timer寄存器而引起的VM Exit;
- 如何減少訪問APIC MMIO導(dǎo)致的VM Exit。
如何減少讀取ACPI PM Timer而引起的VM Exit?
從虛擬化層優(yōu)化的思路來說,減少IO Port引發(fā)的VM Exit通常會(huì)考慮是否可以利用Para-virtulization替換Full-virtualization 以達(dá)到目的,來看Windows在這方面是如何做的。
從Windows 7開始,微軟為了使Windows 操作系統(tǒng)能夠在HyperV得到更好性能,特意為Windows系統(tǒng)做了很多虛擬化方面的增強(qiáng)工作,其中就包括這里可以利用到的HyperV Timer,這個(gè)特性類似于Linux中的kvmclock。
從當(dāng)前的支持情況來看:
- Windows 7
- Windows 7 SP1
- Windows Server 2008 R2
- Windows Server 2008 R2 SP1/SP2
- Windows 8/8.1/10
- Windows Server 2012
- Windows Server 2012 R2
qemu和libvirt添加了HyperV Timer的支持,所以可以直接通過libvirt使能HyperV Timer。另外,KVM里很早也支持了HyperV Timer,只是客戶的宿主機(jī)內(nèi)核版本并不支持該功能,所以需要為客戶升級(jí)UCloud自己維護(hù)的內(nèi)核版本。
如何減少APIC ACCESS而引起VM Exit?
Intel CPU也已經(jīng)支持apic-v,同樣升級(jí)到UCloud自己維護(hù)的內(nèi)核版本來解決。
最終效果
- win-fio-good
- win-good
總結(jié)
從這個(gè)案例可以看出,跟物理環(huán)境相比,在虛擬化環(huán)境下,WindowsIO性能較差時(shí),并不一定真正是IO路徑出現(xiàn)問題,可能是一些虛擬化性能的問題對(duì)IO性能造成了很大影響。
【本文是51CTO專欄機(jī)構(gòu)作者“大U的技術(shù)課堂”的原創(chuàng)文章,轉(zhuǎn)載請(qǐng)通過微信公眾號(hào)(ucloud2012)聯(lián)系作者】