Ubuntu 19.10のGnome Shell 3.34におけるリアルタイムパフォーマンスの改善

これはUbuntuのデスクトップチームのDaniel Van Vugtが投稿した「[Boosting the Real Time Performance of Gnome Shell 3.34 in Ubuntu 19.10](https://discourse.ubuntu.com/t/boosting-the-real-time-performance-of-gnome-shell-3-34-in-ubuntu-19-10/13095)」を[許可をもらって](https://discourse.ubuntu.com/t/gnome-shell-performance-improvements-in-ubuntu-20-04/15972/10)日本語に翻訳したものです。Ubuntu 19.10で大幅に改善したデスクトップパフォーマンスの裏で、どんな涙ぐましい努力が行われていたかよくわかります。

# Ubuntu 19.10のGnome Shell 3.34におけるリアルタイムパフォーマンスの改善

すでにご存知かもしれませんが、Gnome 3.34ではデスクトップのパフォーマンスが大幅に改善しました。本記事ではCanonicalが貢献した改善点のいくつかと、どのような驚くべき問題が存在したのか、どのように対応したのか、将来的に行われる予定のその他のパフォーマンス改善策などを紹介しましょう。

## 実際のところGnome Shellとはなんなのか？

多くの人は、このことを正確には把握していないがために、不当な批判につながっています。そこで、まずはこの部分をはっきりさせましょう。Gnome ShellはC言語とJavaScriptで記述されたデスクトップ環境であり、Mutterコンポジット・ウィンドウマネージャーの上で動いています。

ロジックのほとんどはMutterプロジェクトにあり、それにはClutterとCoglグラフィックツールキットが含まれています。Mutterは（執筆時点で）1389個ものC言語のソースファイルから構成されています。MutterプロジェクトのほとんどはライブラリとしてGnome Shellとして使われていますが、マウスポインターと壁紙だけで十分な用途のために独立したコンポジッターとしてのmutterコマンドも用意されています。

Gnome Shellプロジェクトはそれらのプロジェクトよりは小さく、Compizの上で動くUnity 7と同じようにデスクトップパネルやランチャーのような装飾を提供しています。Gnome Shellは（執筆時点で）199個のC言語のソースファイルと157個のJavaScriptのソースファイルから構成されています。

ここで重要な点は、ソースコードのほとんどはMutterプロジェクトに存在し、Gnome Shellではないということです。Mutterも考慮したときGnome Shell全体のおよそ10%程度がJavaScriptで記述されていて、残りの90%はC言語です。

## 問題点

Ubuntu 19.04のGnome Shell 3.32はUnityや他のデスクトップに比べると遅いように感じまていました。もし低速なマシンを利用していたのなら、スムーズに動かなかったでしょう。より驚くかもしれませんが、高速なマシンでさえ、完璧にスムーズとは言えない状況だったのです。

### 提案と明らかな解決策

多くのユーザーはGnome ShellがJavaScriptから構成されていること、そしてそれがインタープリター言語であることを知っていたので、それが原因だと言うことは簡単でした。そんな多くのユーザーが知らなかったであろう事実として、JavaScriptが実行されているのはたかだか10%程度であり、残りのほとんどはJavaScriptが動いていないということです。アプリケーションを操作する時間のほとんどは、gnome-shellがC言語によるネイティブのマシンコードを実行している時間でした。

一方で開発者はCPUとGPUの使用率に着目する傾向があります。そのための手法には何十年もの知識があります。典型的なのは、プログラムのプロファイリングを取得し、CPUやGPUを多く利用しているホットスポットを探すことです。

Gnome Shellの場合、直近の最大のパフォーマンス問題はホットスポットではありませんでした。実際には画面をスムーズに更新するのではなくアイドル状態になってしまうという、 *コールドスポット* のような問題だったのです。そのようなコールドスポットは、プログラムの実使用時間（real time usage）を観測していた時にのみ現れ、CPUやGPUの時間は消費しません。

### 実使用時間（real time usage）とは？

ここで言う実使用時間とは `sleep 123` を実行したときに123となる値です。このコマンドはCPU時間やGPU時間を使用しているわけではないものの、完了するまでに時間がかかることはわかります。Gnome Shellのようなシングルスレッドでイベント駆動なプログラムの場合、 `poll()` やその他のブロッキングコードで消費した時間により、システムのほどんとどの時間をアイドル状態にします。これは多くの場合は意図通りの動作です。操作をしていないときは、システムはアイドル状態になり、電力の消費を抑えてほしいからです。

実時間はCPU時間を内包しています。プログラムがCPUに依存している場合も、とっかかりとしては便利な計測値です。実時間を計測しておけば、実時間の問題とCPU時間の問題の双方に気がつけるでしょう。

Gnome ShellとMutterはシングルスレッドのGlibイベントループなアプリです。あらゆる一時停止処理が、次のフレームのドロップや「カクつき」の原因となります。よくある一時停止はディスクIOやGPU IOによるものですが、次のフレームのレンダリングの際の計算ミスなどもgnome-shellには存在しました。

### 実使用時間をどのように計測するのか

複数の方法があり、ここではすべてを言及しませんが、多くの開発者はどれでも使いこなせるわけでもないかもしれません。Ubuntu 19.10では、Google ProfilerとMesa自身の方法を使用しました。

[Google Profiler](https://gperftools.github.io/gperftools/cpuprofile.html)は[gprof](http://manpages.ubuntu.com/manpages/eoan/en/man1/gprof.1.html)や[callgrind](http://valgrind.org/docs/manual/cl-manual.html)のようなプロファイラーです。Googleが自由に使えるようにしたものは、[確率論的](https://en.wikipedia.org/wiki/Stochastic)なことやその他の便利な仕組みがあるという違いがあります。そんな仕組みのひとつが、CPU時間ではなく実使用時間を指定して計測できる点です：

```
env CPUPROFILE_REALTIME=1
```

このオプションにより、どれくらいの電力が消費されたかではなく、どれくらいの時間が消費されたかを知ることができます。

実使用時間問題を知る他の方法として、CPUやGPUを誤って「stalling」してしまった時に[Mesa](https://www.mesa3d.org/)のIntelグラフィックスドライバーが報告してくれる仕組みが存在します。

```
env INTEL_DEBUG=perf myprogram
```

どこに問題があるかはわかりませんが、少なくとも*ありうる*ことは教えてくれるでしょう。OpenGLが直接教えてくれるというのは、かなり説得力があります。

## Gnome 3.34で発覚し、修正された実使用時間問題

#### 1. [次のフレームをレンダリングする際に計算ミスによりドロップしてしまう](https://gitlab.gnome.org/GNOME/mutter/merge_requests/520)

これは常に起きるわけではないため、3.32やそれ以前に同様の問題があったと疑うのも無理はありません。フレームのスケジューリングが何らかの理由で数ミリ秒遅れたときに発生していました。しなしながら修正したあとは、3.34ではフレームレートが一貫して高く、そしてスムーズになりました。

#### 2. [XorgセッションではWaylandセッションに比べるとすべてが1フレーム遅れる](https://gitlab.gnome.org/GNOME/mutter/merge_requests/281)

これは19.04でウィンドウを移動したときに明らかになり、XorgよりもWaylandが好まれる理由のひとつでした。しなしながらMutterの問題かどうかは不明だったのです。単にXorgが遅く、MuterやGnome Shellの不具合ではないだろうとみなせば簡単ではありました。結局のところ、Unityや他のデスクトップにも同様のラグは存在します。

この問題は次のフレームのスケジューリングがはやすぎることが問題でした（前回の問題とは逆になります）。これによりフレームがレンダリングされてから表示されるまでにおよそ16msほどかかってしまい、これが視覚的な遅延を増大させていました。この修正によって3.32に比べるとXorgセッションの全体のレイテンシーが現象しています。

#### 3. [Mutterは異なるフレームタイミングメカニズムのコレクションを肥大化させていました](https://gitlab.gnome.org/GNOME/mutter/merge_requests/363)

これは異なるドライバーに対処するために導入されており、毎フレームごとにその一部もしくはすべてを試していました。その結果、Waylandセッションにおいてカーソルの移動が60Hzに制約されていました。60Hzとか時代遅れの値です。また同じ理由で、Nvidiaシステム上におけるXorgセッションで、CPUの使用率が100%になってしまい、最初のスロットリングメソッドが動かないと判断されたとき、CPUの使用率を下げるために他のスロットリングメソッドがスキップされる状態になっていました。

現在は修正されたので、Waylandにおけるカーソルの移動において（Xorgと同じように）リフレッシュレートに制限がかかることはなく、NvidiaシステムでもCPUを専有することはなくなりました（影響を受けていたのは一部のシステムだけなので、仮に影響を受けても同程度でしょう）。

#### 4. [Mutterはすべての入力イベントをキューイングしていました](https://gitlab.gnome.org/GNOME/mutter/merge_requests/711)

さらに次のフレームが描画されたあとまで、そのイベントはシェルやアプリに渡されていませんでした。フレームは16ミリ秒間隔（1000ミリ秒を60FPSで割った値）です。そして多くの人がその遅延に気づく十分な長さに近い値でもあります。これは少し不安定な棒で画面を操作しているようなものです。

*一部は修正されました。* タッチパッドのスクロール（とボタンとキー）は以前よりも1フレーム分遅延が小さくなりました。現在のThinkPadのような物理的に遅いタッチパッドだと十分な効果があるでしょう。

*一部は修正されていません。* Nvidiaドライバーと同様にGnome Shellの他の部分は問題がおおすぎて、ハードウェアの速度でのマウスの移動を含む[完全な修正](https://gitlab.gnome.org/GNOME/mutter/merge_requests/168)はまだ導入できていません。

ヒント：高解像度のタッチパッドスクロールが使いたければXorg上でChromiumを使いましょう。[(X)waylandセッション](https://bugs.launchpad.net/bugs/1811219)や[Firefox](https://bugs.launchpad.net/bugs/1697122)ではうまく動きません。

#### 5. [（Xorgセッションでの）Nvidiaドライバーは性能に制限がありました](https://gitlab.gnome.org/GNOME/mutter/merge_requests/602)

はい、XorgセッションのNvidiaの話です。CPUとGPUが互いに並列に動作し、次のフレームをスムーズに描画する機能が制限されていました。これはMutterの古いバージョンではNvidiaドライバーが速すぎるために加えられた、以前は必要だった回避策が原因です。今回の開発サイクルにおいて[フレームのスロットリング機能は簡素化され統合化されたため](https://gitlab.gnome.org/GNOME/mutter/merge_requests/363)（前述の#3参照）、新しいMutterでは不要な回避策となっていました。

結果的に、Mutter 3.34においてNvidiaのデスクトップの描画が以前のリリースよりもかなり速くかつスムーズになっています。

#### 6. [「Picking」がOpenGLを使っていた](https://gitlab.gnome.org/GNOME/mutter/merge_requests/189)

「Picking」とはマウスを移動したり画面が変化したときにカーソルの下に何があるかを把握することです。

これには、マウスを移動するたびにCPUとGPUを同期させるために、パイプラインをブロックしてストールさせる必要がありました。利用可能なUIデザインをすべてカバーするために、歴史的にClutterの設計がそうなっていたのです。しかしながら予想されるように、OpenGLを呼び出すために多くのCPUを使用していました。

PickingをGPUではなくCPUで行うようにしたため、カーソルのCPU使用量が減りました（GPUはゼロになりました）。同時に、カーソルの下にあるデスクトップ全体（やアプリやゲーム）のレンダリングをストールさせることもなくなりました。CPUの使用量を減らすために始めた試みは、[偶然にもリアルタイムパフォーマンスを向上させる](https://gitlab.gnome.org/GNOME/mutter/issues/691)ことにもなったのです。

## Gnome 3.34ではまだ解消されていない実時間の不具合

#### 1. [Waylandセッションにおけるマルチモニターの描画において、ランダムで一定時間（平均で50%ほど）ブロックされたり、スリープしたり、画面の描画ができなかったり、応答できなかったりすることがあります](https://gitlab.gnome.org/GNOME/mutter/issues/3)

これはWayland（実際には「EGLネイティブ」）バックエンドの中でブロックしているのが原因です。現在のところマルチモニター環境でGnomeを使うなら、次のふたつの次善策のどちらかを選ぶ必要があります：

* Wayland：ブロックやスタッタリングがしばしば起きますが、ティアリングは起きません
* Xorg：DRI2のためにひとつを残した他のすべてでティアリングが起きます（DRI3で修正されたようです）。しかしながらブロックやスタッタリングは発生しません

Ubuntu 20.04 LTSのGnome 3.36で修正される見込みです。最終的な修正が行われたら、Waylandセッションが滑らかさとティアリングが発生しないという完璧にバランスがとれた構成になるでしょう。

#### 2. [いくつかのケースでMutterが次のフレームを時間どおりにスケジューリングできない](https://gitlab.gnome.org/GNOME/mutter/merge_requests/719)

つまり、CPUの使用率が中程度でも、高くなかったとしても、です。現在提案されている修正は、Waylandバックエンドがクラッシュしたりデッドロックするという副作用があります。これらは異なる問題ではありますが、先に修正されている必要があります。Ubuntu 20.04 LTSよりも前に修正したいところです。

## ここに至るまでの過ち（これらを避けるようにしてください）

人生は学びです。我々がGnome Shellのグラフィックパフォーマンスを調査する際に起こした次のような過ちからぜひ学びを得てください……

* 中程度のCPU使用率が、グラフィックが滑らかでない理由であると仮定すること。はい、i7プロセッサーの50%の使用量というのは、多くの処理が動いているということです。しかしながらそれはデスクトップがスムーズでない理由にはなりえません。その場合は100%に近い値になるでしょう（ `top` において単一のCPUコアが飽和状態になります）。中程度のCPU使用率は問題ではありません。まだ。
* 単純な描画時間の計測によってGPUの使用率が低いことがわかったからと言って、GPUのプロファイリングを行わないこと。少なくともそれは単純な数字ではありますが、それについて知るすべをまだもっていない状態です。これを解決するための[方法を提案しています](https://gitlab.gnome.org/GNOME/mutter/merge_requests/154)。
* JavaScriptはすべてにおいてC言語よりも遅いと仮定すること。
* とにかくJavaScriptが使われていると仮定すること。 *一般的に* アプリケーションウィンドウとの相互作用のみを考慮するなら、それはJavaScriptを使っていません。純粋にC言語です。
* すべてのものがCPUやGPUの可能な限りの速度で動いている（動きはじめの遅延がない）と仮定すること。プログラムによるミスや意図的な設計上の判断によって、ときどきそうではないことがあります。
* 高リフレッシュレートのモニターですぐにテストしようとしないこと。60Hzのディスプレイに繋がれたあるマシンが、あるものに対して30Hzしか達成できなかったとします。同じマシンを120Hzのディスプレイに繋げば、同じものに対して60Hzを達成できるかもしれません。同じCPUで、同じGPUだとします。そうするとCPUやGPUの能力は問題ではないということになります。ここでの教訓は、これによりハードウェアの制限ではなく、何かフレームをドロップしてしまう原因となるアルゴリズム上の不具合が存在すると結論付けられるということです。
* CPUの使用量の増大はすべて不具合だと信じること。たとえば、50%の時間ブロックしているとして、CPUの使用率が40%で30FPSを達成しているとしましょう。ここでそのブロックしている部分を取り除けば、80%のCPU使用率で60FPSになると考えられます。CPUの使用率は高くなりましたが、フルフレームレートに到達するための望ましい一歩です。コードの変更によってCPU使用率があがったからといって、すぐさま何か間違いを犯したと短絡的に考えないでください。
* ウィンドウの移動が、デスクトップシェルの他の部分のパフォーマンスにも関係すると仮定すること。必ずしもそうとは限りません。[ウィンドウの理由はその特殊な要因により](https://gitlab.gnome.org/GNOME/mutter/merge_requests/568)、他の何ものよりも遅いということがわかりました。
* アイコンスプリングのアニメーションを見て、それがデスクトップシェルの他の部分のパフォーマンスにも関係すると仮定すること。そうではありません。明らかにこれは多くのJavaScriptが関わっているひとつで、[過去に遅かった理由は](https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/253)GPUやグラフィックスドライバーは関係ありませんでした。単にCPUの使用率が跳ね上がっていたのです。
* 十分な時間を使ってプロファイラーの結果を理解しようとしないこと。プロファイラーは、人間の脳がうまく処理できるよりも多くの情報を常に生成します。それが何を伝えようとしているか理解するために、フィルタリングし、解析し、十分な時間を費やすことが重要です。
* 他のOSと比較しようとしないこと。これを読んでいるのはUbuntuが好きだからと知っていますし、それは素晴らしいことです。単に言いたいのは、macOSやChromeOS、Windowsなどの経験が少しでもないと、競合との違いを知ることができないということです。
* 計測せずに推測すること。
* 新規に計測せずに、過去の経験をもとに推測すること。開発者は何が速くて何が遅いか知っていると考えがちです。しかしながら、ときどきその先入観をなくすべきです。自分は本当に何も知らないと言い聞かせ、慎重な計測によって、驚きとひらめきを得られるようにしましょう。習慣的に行っている最適化が、全体を通してみると実は取るにたらないものだということに気づくことも多いでしょう。

## Gnome Shellのパフォーマンス改善のための壮大な計画

ここまで見てきたように、多くの問題が修正されましたが、まだいくつもの油断のならない問題が未修正です。プロジェクトトラッキングに興味があるかもしれないので、Ubuntuで使っているリンクを提示しておきます：[カクつき](https://trello.com/c/Q6JYXPPs) | [遅延](https://trello.com/c/EswljRbG) | [CPU](https://trello.com/c/pe5mRmx7)

最初に何から取り組むべきでしょうか？ 多くの問題を管理可能にするために、UbuntuにおけるGnome Shellデスクトップにふたつの目標を設けています：

1. より新しく速いマシンで高速化する
2. より古く遅いマシンで高速化する

「高速化する」とはカクつくことなく、モニターのフルフレームレートを維持し続けるということです。「速いマシン」はUnityやGnomeデスクトップが普段使いできるほどのものを意味します。しかしこれは若干主観的だと思います。

#### より新しく速いマシンで高速化する

まずはじめにすることは、すべてのリアルタイムの遅延問題を修正することです。なぜなら無限に高速なCPUやGPUであっても、同じ問題が起こるからです。ハードウェアをアップグレードしたら、いくつかの改善を実際に経験できるべきです。

良いニュースは、Ubuntu 19.10のMutter 3.34でほとんど解決しているというこｔです。Ubuntu 20.04 LTSのMutter 3.36で完全に解消することを目指しています。この目標に向けて、残っているのは次のような問題です。

* フレームのドロップが行われないよう、[mutter!719 ](https://gitlab.gnome.org/GNOME/mutter/merge_requests/719)を再検討し、書き直すことです。これには複数の段階が予想されています。いくつかはすでに作業中です。（[1](https://gitlab.gnome.org/GNOME/mutter/merge_requests/762)、[2](https://gitlab.gnome.org/GNOME/mutter/merge_requests/770)）。
* Waylandにおけるハイパフォーマンスなマルチモニターを実現するために[mutter!73 ](https://gitlab.gnome.org/GNOME/mutter/merge_requests/73)を再検討・再実装することです。アップストリームのGnome開発者がすでにこれを行うための計画を提案してくれています。すばらしい！

最後に、ディスクIOをブロックする問題を見つけ修正する必要があります。アップストリームのGNOME開発者はすでにこの問題に着手しています（[1](https://gitlab.gnome.org/GNOME/gnome-shell/issues/815)、[2](https://gitlab.gnome.org/GNOME/gnome-shell/issues/832)）。しかしながら、実際には、それらの問題が起きる時間的コード的場所がわからないときに、どのように検知し計測するのが良いかを議論しアドバイスする必要がありそうです。

#### より古く遅いマシンで高速化する

これはより難しい問題です。なぜならまず最初に、すべてのリアルタイムブロッキング問題が、高速なマシン上では解決していなければならないからです。

次に、この計画は、問題の主要なクラスになったときに、数多くのCPUのホットスポットを計測し、計測し、計測し、修正し続けることだからです。さらに計測を行い、GPUのボトルネックが存在したらそれも修正します。

2020年には古く遅いマシンでもGnome Shellがより高速に動くことを望んでおり、その第一段階はほぼ完了しています。

#### 最後に

いかがでしたでしょうか。Ubuntu 19.10のGnome Shell 3.34は、以前のリリースに比べて明らかに高速化されており、そこにたどり着くまでの道のりに興味を持ってもらえたらうれしいです。しかしながらこれは壮大な計画の一部でしかありません：

17.10：Gnome ShellをUbuntuで採用
18.04：ちょっとしたパフォーマンスの改善
18.10：ちょっとしたパフォーマンスの改善
19.04：ちょっとしたパフォーマンスの改善
19.10：大掛かりなパフォーマンスの改善 :arrow_left: イマココ
20.04：目標：新しく高速なマシンでの高速化
20.10：目標：古く低速なマシンでの高速化

Gnome Shellの未来は明るく、期待するだけの価値があります。