5. 软件设置
这段是本文重点。关闭超线程,打开或关闭turbo boost,pbo等等设置可以在BIOS里设置,也可以在软件设置,这里就不介绍了。这主要写一些行之有效又很少看到人说的。下面的设置中,假定有四个cpu核心/线程,树莓派啊,j3455到现在的j6412赛扬,各种各样的amd/intel标压cpu之类的烂大街设备都满足。cpu0留给系统使用,cpu1给网卡,cpu2给解码,cpu3给naa或者roon raat。这样的好处,是每次执行一个任务的时候,如果是网卡有关的就直接找cpu1,cpu1也肯定正闲着。如果是解码有关的事都找cpu2,也正闲着。主程序就自己独占了cpu3,也没别人和他抢了(我个人不建议一台机器上同时开着naa和raat,抢DAC时候会print好些日志,强迫症忍不了)。这种原始又粗暴的方法可以有效降低音频应用的延迟。当然,如果你机器发热厉害,或者只有双核,也可以使用下面的方法,把一个核心隔离出来分配给naa/roon。
5.1 rt内核启动参数
确切的说,是在你当前的内核启动参数基础上加上这些东西。有些东西比如threadirqs不加其实也是可以的,写在这里是为了保持逻辑完整。我们的目的,是让系统除了cpu0之外的CPU(也就是cpu1-3)都留着不分配,刚启动时候所有程序都运行在cpu0上。如果你是尊敬的线程撕裂者用户,也可以只保留三个给音频,其他的都分配出去。
如果你用的是grub启动,需要修改/etc/default/grub,在原有GRUB_CMDLINE_LINUX_DEFAULT基础上加上:
- GRUB_CMDLINE_LINUX_DEFAULT="threadirqs skew_tick=1 rcu_nocb_poll rcu_nocbs=1-3 nohz=on nohz_full=1-3 kthread_cpus=0 irqaffinity=0 isolcpus=managed_irq,domain,1-3 intel_pstate=disable nosoftlockup tsc=nowatchdog"
复制代码
如果你用的是树莓派,这段东西要写在/boot/cmdline.txt。
5.2 修改音乐播放软件的优先级和程序类型
下面是我在Gentoo openrc网桥上roon ready启动脚本的一部分,意思是让这个程序运行在NICE=-19,RT_PRI=-96,分配给CPU3,也就是最后一个CPU。非Gentoo用户具体修改方法可参考我在本坛的帖子《Roon系统硬核安装笔记》【1】。
【1】http://erji.net/forum.php?mod=vi ... =2253401&extra=
- export SSD_NICELEVEL="-19"
- user="root:root"
- logfile="/tmp/roon.log"
- command="taskset"
- command_args="
- -c 3 chrt -r 95 /opt/RoonReady/raat_app /etc/raat.conf > $logfile 2>&1
- "
复制代码
5.3 修改不同线程的优先级
- CHRT_LIST="xhci_hcd ksoftirqd ktimer ahci rtc acpi"
- chrt_process() {
- PRI1=89
- for NAME in ${CHRT_LIST}
- do
- PIDS=`ps -eLf | grep "${NAME}" | awk '{print $4}'`
- for PID in ${PIDS}
- do
- if [[ ! -f /proc/${PID}/status ]] || chrt -p -f ${PRI1} ${PID}
- then
- echo "chrt -p -f pid=${PID} prio=${PRI1}: OK."
- else
- echo "chrt -p -f pid=${PID} prio=${PRI1}: FAILED."
- fi
- done
- PRI1=$((${PRI1} - 5))
- done
- }
- chrt_process
复制代码
上面代码中,CHRT_LIST里的字符串来自htop或者ps aux,执行一下这两个命令之一就明白了。其中第一项是你的解码对应的线程,一般系统上都用xhci_hcd(usb3驱动),如果你的主板不支持usb3,或者你不用usb解码/界面,可以不动或者改成你的设备对应的PID。非full preempt系统没有ktimer这条,不过其实不必理会,直接运行这段代码即可,已经考虑了兼容性。
5.4 分配解码和网卡的中断
- eth_cpu() {
- NAME="eth"
- echo "eth->cpu"
- IDS=`cat /proc/interrupts | grep "${NAME}" | awk '{print $1}'`
- for ID in ${IDS}
- do
- ID="${ID/:/}"
- echo ${ID}, 2
- [[ ! -f /proc/irq/${ID}/smp_affinity ]] || echo "2" > /proc/irq/${ID}/smp_affinity
- done
- }
- eth_cpu
复制代码
- usb_cpu() {
- NAME="dwc2_hsotg"
- echo "usb->cpu"
- IDS=`cat /proc/interrupts | grep "${NAME}" | awk '{print $1}'`
- for ID in ${IDS}
- do
- ID="${ID/:/}"
- echo ${ID}, 4
- [[ ! -f /proc/irq/${ID}/smp_affinity ]] || echo "4" > /proc/irq/${ID}/smp_affinity
- done
- }
- usb_cpu
复制代码
上面NAME里的字符串来自/proc/interrupts,注意如果你用的是光纤网卡,那么eth需要改为对应的字符串;如果你用的不是holo red,那么dwc2_hsotg要改为你的usb驱动xhci_hcd,或者iis卡fe804000之类的。怎么识别出来呢,有个小技巧。如果你还没配置过cmdline.txt启动参数,cat /proc/interrupts的结果应该是如下面网图1这种乱七八糟到处都可能有数字的。
图1,普通系统的中断
如果你按我写的配置了内核,并修改了启动参数,那应该几乎所有行的数字都写在最左边,也就是cpu0下,如下图2所示。这时候你打开roon或者hq放点声音,前面的数字就会增加了。放音乐时候增加的那个就是要修改的网卡,解码器,或者硬盘。如图所示,ahci[0000:00:12.0]这行就是硬盘了,我们回头会做成内存系统,不必理会。eth0这行按名字就知道是网卡了,你在eth_cpu这填eth或者eth0。xhci_hcd有这么多,但可以看到只有 PCI-MSIX-0000:03:00.0 这个设备是激活状态,因为这是我的pcie-usb卡(买的带时钟的国产的就不推荐了),我就可以在NAME这填写。如果你觉得这还有好几个设备也被一起搜进去了,很尴尬啊,那可以把这行改为:
- IDS=`cat /proc/interrupts | grep "${NAME}" | grep "0-edge" | awk '{print $1}'`
复制代码
这就只保留有数据的那个0-edge的通道了。
图2,隔离后刚启动的中断
补充一下,非IO-APIC硬件是不支持动态修改affinity的,这就只能通过内核启动参数限制在cpu0了。
【2】https://cs.uwaterloo.ca/~brecht/servers/apic/SMP-affinity.txt
5.5 PCIE设备的优先级
- setpci -v -d *:* latency_timer=b0
- export PCI_ID=`lspci|grep TUSB73x0|awk '{print $1}'`
- setpci -v -s $PCI_ID latency_timer=ff
复制代码
前面说了我搞了个pcie-usb卡。所有pcie设备都会有个编号,lspci就看到了,还带英文介绍的,我的介绍里这张usb卡是TUSB73x0的,我就把这字符串填写在上面代码里。效果就是,让这张卡的延迟优先级设为ff,高于其他所有卡的b0。如果你有独立网卡或者USB卡,可以写在这。
5.6 内存系统
- process_dir() {
- if [ $# == 1 ]; then
- mount none -t tmpfs -o noatime /mnt/.ramdisk/$1
- rsync -a /$1/ /mnt/.ramdisk/$1 --exclude modules --exclude src --exclude cache --exclude db --exclude firmware --exclude portage
- mount -o bind /mnt/.ramdisk/$1/ /$1/
- fi
- }
- sync && echo 3 > /proc/sys/vm/drop_caches
- umount /boot 2>/dev/null
- rm -r /mnt/.ramdisk 2>/dev/null
- mkdir -p /mnt/.ramdisk/{etc,opt,root,tmp,usr,var}
- for dir in etc opt root tmp usr var
- do
- echo -e "/$dir ..."
- process_dir $dir
- done
- sync && echo 3 > /proc/sys/vm/drop_caches
复制代码
之所以这里要折腾一下内存系统,主要是不想再设置ssd的中断了(其实是四个核心用完了...)。上面的脚本很容易看懂并迁移到你的系统上,只需修改mkdir一行和for一行。大约的意思就是,把硬盘根目录上的文件夹,都同步到tmpfs的内存里,之后再把tmpfs的文件夹bind回原来的硬盘文件夹下,这样想要读原文件夹时候就会访问内存而不是硬盘了。
5.7 看看效果吧
经过上面一通折腾,再放会儿音乐,cat /proc/interrupts看看效果,如下图3。
图3,优化后的中断
可以看到不会再增加ahci也就是硬盘的cpu0占用了,说明内存系统果然生效了。看到eth0在cpu0上的数据也不再涨了,从无到有的是cpu1这一列数据,这是因为前面执行了eth_cpu函数把网卡绑定到cpu1了。0-edge xhci_hcd的第一列数据也不变了,cpu2这一列增长了不少。至此我们就把桥的cpu分配好了。至于hq和roon core也可类似优化。
|