cpuaffinityの設定と効果

APU1Cに対応したlightmpdから「cpuaffinity」という設定項目がもうけられています。
この設定が音質改善に効果があるのか否かはかなり微妙で、作者であるデジファイさん自身も「cpuaffinity機能は実験的な実装なので、効果が見られない時は廃止する場合があります。」と断っています。
ただし、lightmpdは導入の敷居(NASのマウントに伴う「remotedir=・・・」だけが鬼門)を下げた見返りとして、大部分がブラックボックス化しているので、ユーザーサイドから見ればチューニングできる幅が限定されています。ですから、こういう形でチューニングできるポイントが増えるのは悪い話ではありません。
lightmpd掲示板でも32ビット版と64ビット版の音質の違いは結構報告されていますが、cpuaffinityの設定に伴う音質報告は現時点(2014年8月21日)では皆無です。世間の反応が全くなくて「廃止」というのは困りますので、微力ながら音質レポートをしたいと思います。

そもそも「cpuaffinity」って何なんでしょう。

「cpuaffinity」の設定は、いくつかのスレッドを特定のCPUに括りつける機能を持っています。「cpuaffinity type=0」の設定がこのデフォルト状態です。

マルチコアのCPUでは、それぞれのスレッドがどのCPUで実行されるのかは自動的に割り振られます。
実行されたプロセスは一般的にキャッシュメモリとしてプロセッサ内に残ります。そのような状態で、次に同じプロセスの実行が必要となったときは、当たり前の話ですが同じプロセッサで動作させた方が効率的です。ですから、このデフォルト状態でそれぞれの割り込み状態を見てみると以下のようになるのが一般的です。

# cat /proc/interrupts
CPU0 CPU1
0: 222 0 IO-APIC-edge timer
4: 1 55 IO-APIC-edge serial
8: 0 1 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
17: 124 34312 IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2, ehci_hcd:usb3
18: 0 0 IO-APIC-fasteoi ohci_hcd:usb4, ohci_hcd:usb5, ohci_hcd:usb6, ohci_hcd:usb7
40: 128 57233 PCI-MSI-edge eth0
NMI: 2 1 Non-maskable interrupts
LOC: 23669 988 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 2 1 Performance monitoring interrupts
IWI: 0 0 IRQ work interrupts
RTR: 0 0 APIC ICR read retries
RES: 85872 528 Rescheduling interrupts
CAL: 0 14 Function call interrupts
TLB: 0 0 TLB shootdowns
ERR: 0
MIS: 0

見れば分かるように、それぞれのスレッドはどちらかのプロセッサにおおむね括りつけられてます。
lightmpdの場合は、timerはCPU0に、USBとeth0はCPU1に括りつけられているように見えます。
つまり、マルチコアのCPUであっても、それぞれのCPUに満遍なくプロセスが割り振られるわけではなく、基本的にはそれぞれのCPUには特定のプロセスが割り振られるような仕様になっていることが分かります。

しかしながら、これもまた見れば分かることですが、それぞれのプロセスは完全に割り振られているわけではなく、USBやeth0関連のプロセスは時々はCPU0に割り込んでいることが分かります。考えてみれば、CPU1にはそれぞれのプロセスのキャッシュメモリが残っているのですから、それを無視してCPU0に外注するのは無駄なように思えます。何故ならば、こういう形で変更されると、(おそらく)せっかくメモリに保存していたキャッシュにデータを入れなおすというペナルティが生じてしまうからです。

しかし、OSは時にはこういう形で割り込み先を変更する方がシステム全体としては「効率的」だと判断しているようです。
しかしながら、システム全体を考えれば「効率的」であっても、そのシステムを音楽再生だけに特化して使用しているユーザーから見れば本当に望ましいかどうかは疑問です。

そこで登場するのが「cpuaffinityの設定」だというわけです。

デジファイさんはあまり詳しくは述べていないのですが、その目指すところは、おそらく、・・・

音楽再生に必要なプロセスを特定のCPUに括り付けることができるならば、それは同時に音楽再生に直接関係しないプロセスをそのCPUから除外することができる。結果として、音楽再生に必要なプロセスの実行速度を最大にすることができ、音質改善に寄与する

と言うことではないでしょうか?
と言うことで、そのあたりの理屈を少しふまえた上で実際に設定を変更して音質を確認してみます。

「cpuaffinity type=1」の設定

「cpuaffinity=1」に設定すると、USB関係のプロセスがCPU1に括りつけられ、それ以外のプロセスはCPU0に括りつけられます。つまり、USB関係のプロセスは独占してCPU1を使うことが出来る設定です。
では、この状態でのそれぞれの割り込み状態を確認してみます。

# cat /proc/interrupts
CPU0 CPU1
0: 222 0 IO-APIC-edge timer
4: 124 46 IO-APIC-edge serial
8: 0 1 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
17: 0 34305 IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2, ehci_hcd:usb3
18: 0 0 IO-APIC-fasteoi ohci_hcd:usb4, ohci_hcd:usb5, ohci_hcd:usb6, ohci_hcd:usb7
40: 60217 40 PCI-MSI-edge eth0
NMI: 2 0 Non-maskable interrupts
LOC: 15829 7858 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 2 0 Performance monitoring interrupts
IWI: 0 0 IRQ work interrupts
RTR: 0 0 APIC ICR read retries
RES: 17865 583 Rescheduling interrupts
CAL: 0 14 Function call interrupts
TLB: 0 0 TLB shootdowns
ERR: 0

太字で示したように、USB関係のプロセスがCPU1に括りつけられているのが分かります。また、デフォルト状態ではCPU1を主に使っていたeth0がCPU0に追いやれている事も分かります。
lightmpdにおける音楽再生という一連のプロセスは「①NASからデータを読み込む(eth0)→②mpdで再生(mpd)→③USB機器にデータを送り出し(USB)」という3ステップから成り立っていますから、これでとりあえず、①と③のステップがそれぞれ二つのCPUに割り振って固定された事が確認できます。

「cpuaffinity type=2」の設定

しかし、気になるのが②のmpdです。
このままではその他大勢の一員としてCPU0に括りつけられているこることです。おそらく、再生の中核を占めるmpdもその他大勢の一員としてCPU0に関連づけられるよりは、USBだけが独占的に使用しているCPU1に括りつけた方が良さげな気がします。ですから、mpdをそのように関連づける設定が「cpuaffinity type=2の設定です。

この設定では「cpuaffinity type=1」の設定に加えて、mpdのoutptuスレッドがCPU1に固定される仕組みになっています。

# cat /proc/interrupts
CPU0 CPU1
0: 222 0 IO-APIC-edge timer
4: 128 48 IO-APIC-edge serial
8: 0 1 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
17: 0 34306 IO-APIC-fasteoi ehci_hcd:usb1, ehci_hcd:usb2, ehci_hcd:usb3
18: 0 0 IO-APIC-fasteoi ohci_hcd:usb4, ohci_hcd:usb5, ohci_hcd:usb6, ohci_hcd:usb7
40: 60931 43 PCI-MSI-edge eth0
NMI: 2 0 Non-maskable interrupts
LOC: 13848 10475 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 2 0 Performance monitoring interrupts
IWI: 0 0 IRQ work interrupts
RTR: 0 0 APIC ICR read retries
RES: 19575 5866 Rescheduling interrupts
CAL: 0 15 Function call interrupts
TLB: 1 4 TLB shootdowns
ERR: 0
MIS: 0

これを見ただけでは「cpuaffinity type=1」の時と違いは分かりません。
それから、おわかりとは思うのですが、「34306」のような具体的な数値はあまり意味を持ちません。lightmpdを長く稼働させれば当然のことながら割り込み回数は増えていきますからこの数値は大きくなります。ですから、ここで注目すべきは割り込み回数がCPU0とCPU1の間でどのように割り振られているかです。
「/proc/interrupts」を見ればUSBとeth0の割り込み状況は分かりますが、mpdに関しては確認出来ません。

「/proc/softirqs」を見れば何か分かるか、とも思ったのですが・・・、

「cpuaffinity type=0」の設定

# cat /proc/softirqs
CPU0 CPU1
HI: 0 0
TIMER: 10039 484
NET_TX: 79 0
NET_RX: 56795 0
BLOCK: 119 0
BLOCK_IOPOLL: 0 0
TASKLET: 34628 0
SCHED: 0 0
HRTIMER: 3985 0
RCU: 0 0

「cpuaffinity type=1」の設定

# cat /proc/softirqs

CPU0 CPU1
HI: 0 0
TIMER: 9875 539
NET_TX: 88 0
NET_RX: 60209 0
BLOCK: 76 0
BLOCK_IOPOLL: 0 0
TASKLET: 559 33976
SCHED: 0 0
HRTIMER: 74 3901
RCU: 0 0

「cpuaffinity type=2」の設定

# cat /proc/softirqs
CPU0 CPU1
HI: 0 0
TIMER: 8889 2396
NET_TX: 80 1
NET_RX: 61532 0
BLOCK: 76 0
BLOCK_IOPOLL: 0 0
TASKLET: 573 33976
SCHED: 0 0
HRTIMER: 75 3901
RCU: 0 0

「type=0」のデフォルト状態が「type=1」や「type=2」とは違うと言うことは分かりますが、mpdをCPU1に括りつけた「type=2」と括りつけていない「type=①」の違いがパターンからは確認できません。ただし、「/proc/softirqs」の見方もいまいちよく分からなかったのですが、雰囲気的にはデフォルト状態でのCPUの使われ方はどう見ても効率がいいとは見えません。

結局、音質的にはどうなのよ?

訳の分からないことを長々と報告してしまいました。しかし、「cpuaffinity」の設定を変えることでCPUの使われ方が随分変わると言うことは分かっていただけたと思います。そして、何を変えても音が変わるのがオーディオの世界であって、その本質はPCオーディオであっても同様です。ましてや、コンピューターの頭脳とも言うべきCPUの使われ方が変わるわけですから、これで音が変わらなければ嘘です。

しかしながら、かなり時間をかけて聞き比べたのですが、結果はかなり微妙でした。

もちろん、違いがないわけではありませんが、その差はきわめて小さいです。以下、その違いについて報告はしますが、ブラインドで聞き比べをさせられればその違いを指摘する自信はありません。かなりの高性能で、上流部の違いを敏感に描き分けるようなシステムでなければその違いを聞き分けるのは難しいようです。
「エレクタ・アマトール」のような一時代も二時代も前のスピーカーでは、その違いにあらかじめ耳を集中させていないと聞き分けるのは難しかったです。

まずは、「cpuaffinity type=0」といういわゆるデフォルト状態で音楽を再生しても、それだけを聞く限りではほとんど不満を感じることはありません。
Linuxのカーネルはデフォルト状態でも十分賢くCPUに仕事を割り振っています。そして、APU1Cのような高性能なボードであれば、音楽再生をこなす上で必要にして十分な演算能力とメモリの領域をもっていますから、このデフォルト状態でも立派な音を聞かせてくれます。

ただし、この後に「type=1」や「type=2」の音を聞いてから再びこのデフォルト状態の音を聞くと少しばかりの不満が指摘できるかと思います。
なお、話が前後しましたが、今回も聞き比べに使ったのは「スコットランド幻想曲:(Vn)ハイフェッツ サー・マルコム・サージェント指揮 ロンドン新交響楽団 1961年5月15日&22日録音」です。聞き比べに使う音源はあまりあれこれと変えずに固定した方が違いを聞き分けやすいと思います。

一つめの不満は、「type=0」のデフォルト状態ではヴァイオリンの高音域がやや神経質に響く場面があることです。
ただし、その違いは気になる部分にあらかじめ耳を集中させれば聞き分けなれるかな・・・(^^;・・・?と言う程度です。しかしながら、ブレーズ指揮のペトルーシュカ(ニューヨーク・フィルハーモニック/録音:1971年)なんかを聞いてみても、たとえば管楽器の高音域がやや神経質に響くような雰囲気があります。
デフォルト状態の「type=0」では音がやや細身になる傾向があり、その傾向は「type=1」や「type=2」では少し緩和されるような気がします。ただし、「type=1」と「type=2」の違いはほとんど聞き分けられませんが、それでもほんのわずかですが「type=2」の方が好ましく聞こえるかもしれません。

次にデフォルト状態で気になったのは、何となく音楽が全体的にこぢんまりとしてしまうことです。それは、音場が狭くなると言うだけでなく、何となく音楽全体に「勢い」みたいなものがほんの少しですがスポイルされるような雰囲気があります。そして、この傾向は「type=1」や「type=2」ではかなり改善されるのですが、これもまたほんの少しですが「type=2」のほうが雄大に音楽が響くような気がします。

ただし、この2点はあえて違いを言葉にすればと言うことなので、現実の音の違いはそれほど大きくはありません。当然のことながら、それぞれの環境によって評価は異なってもおかしくはありません。実際、32ビット版と64ビット版でも全く異なった評価がでてますから、「cpuaffinity」の設定ならば、さらに評価が異なる可能性はあります。
ですから、最終的にはそれぞれの環境で自分の「耳」を信じて選択するのが筋かと思います。

とりあえず私は「cpuaffinity type=2」の設定を採用することにしました。