硬件中断问题和性能优化

自打从硬件方向研究性能优化起,感觉自己入了一个大坑。很多原来看起来都是天经地义的调用个接口就搞定的事儿,现在都有了不得不吐的槽。OK,这次就从硬件中断说起。

首先,啥是硬件中断?作为一个经历过非即插即用时代的老人来说,之前的硬件板卡安装之前都需要配置IRQ跳线来支持,每个硬件在系统中都有一个唯一的IRQ编号用于通讯。打个比方,一块网卡在收到了网络信号之后(仅仅是信号,还不能用”数据包“来称呼),主动发送中断到CPU;而CPU将会立即停下手边的活以便对这个中断信号进行分析。这是一种类似推送的机制。

这是在比较理论的情况下假定的单核CPU,已经明显有了槽点:假设这个系统有着非常繁忙的硬件中断请求,那CPU的性能将会大大的受到影响。如何妥善的解决这个问题?

有个从硬件机制上解决这个问题的方法——DMA,也就是允许硬件在无CPU干预的情况下将数据缓存在指定的内存空间内,在CPU合适的时候才处理(更新,更NB,也更昂贵的DPDK Data Plane Development Kit 可以视作DMA的增强模式)。这个技术实际上早在ATA时代已经广泛的运用于磁盘控制器的中断管理上。然而在网卡方面,由于网络通讯的不确定性,DMA技术仅仅被应用在少数高端网卡上。

另一个方向就是从多任务调度的方向出发。把所谓的”吵闹的邻居“放置在一起统一管理。由于当下多核CPU已经成为了标配,那为何不专门开辟一个核心用来处理硬件中断的请求?这项技术在Linux Kernel中被称为IRQ Affinity,字面翻译为硬件中断关联。

root@litrin-de-xubuntu:/home/litrin# cat /proc/interrupts | head
 CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15 CPU16 CPU17 CPU18 CPU19 CPU20 CPU21 CPU22 CPU23
 0: 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 2-edge timer
 8: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 8-edge rtc0
 9: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 9-fasteoi acpi
 16: 10499 0 0 0 0 0 0 0 0 2309 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 16-fasteoi ehci_hcd:usb2, uhci_hcd:usb6, uhci_hcd:usb7, uhci_hcd:usb8, ioc0
 18: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 18-fasteoi ata_piix
 19: 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 19-fasteoi ehci_hcd:usb1, uhci_hcd:usb3, uhci_hcd:usb4, uhci_hcd:usb5
 21: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 21-fasteoi ata_piix
 25: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 16384-edge aerdrv, PCIe PME
 27: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 49152-edge aerdrv, PCIe PME

第一列为中断号,后面跟的一排数字是硬件在对应的core上发送的中断数量,在这个列表中,我们可以看出一个USB的bridge在core0上有1w多次的中断信号。

我们找到这个IRQ,16号。然后找到对应的配置文件。

root@litrin-de-xubuntu:/home/litrin# cat /proc/irq/16/smp_affinity
ffffff

怎么读呢?0xffffff = ‘0b111111111111111111111111’,每个bit代表了24个core中的一个。全F意味着每个core都有可能会被中断干扰。而这个文件本身是可写的,直接写入一个数值即可简单粗暴的修改中断的绑定信息。

root@litrin-de-xubuntu:/home/litrin# echo  1 >  /proc/irq/16/smp_affinity

该命令会将对应的中断交由core 0 处理。结合cgroup的core binding则我们可以最大程度上减少硬件中断对CPU性能的影响。

最后,附上一段python code,自己写来绑定网卡的。实测在web应用上有5%以上的提升。

#! /usr/bin/env python

def get_irq_list_from_nic(nic):
 irq_list = []
 with open("/proc/interrupts") as fd:
 for line in fd.readlines():
 if line.find(nic) is not -1:
 irq_list.append(line[2:line.find(":")])

 return irq_list


def covert_to_bitway(core_list):
 bitway = 0
 for i in set(map(int, core_list)):
 bitway += 1 << i

 return bitway


def core_bind(irq, cores):
 filename = "/proc/irq/%s/smp_affinity" % irq
 with open(filename, "w") as fd:
 fd.write(str(cores))


def main(nic, core_list):
 core_bitway = covert_to_bitway(core_list)
 for irq in get_irq_list_from_nic(nic):
 core_bind(irq, core_bitway)


if __name__ == "__main__":
 import optparse

 parser = optparse.OptionParser()

 parser.add_option("-n", "--nic", dest="nic",
 help="the NIC needs allocate", default="enp1s0f0")

 parser.add_option("-c", "--core",
 dest="core_list", default="0",
 help="Input a list of core(s), separate by ',' ")

 (options, args) = parser.parse_args()

 nic = options.nic
 core_list = options.core_list.split(",")

 main(nic, core_list)

 

推荐阅读:
之前我们通过几个概念简单的介绍
这一段时间,凡是提及容器技术的

发表评论

电子邮件地址不会被公开。 必填项已用*标注

请补全下列算式: *

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据