それぞれの最適化でどれくらい速度が稼げたのか書いてないので非情に不安になる記事 bit.ly/10YcA6G
— トデス子さん (@todesking) 2013年5月2日
速度最適化するならプロファイル(測定)してホットスポットを叩く、の繰り返しが鉄則だろうけどそういうサイクルっぽい描写じゃなかった感。省略しただけかもしれんけど。
— Akihiko Koizukaさん (@koizuka) 2013年5月2日
本日見たところ、清水さん自身が追記をなされていましたがちょっと雑感を書いておこうと思います。
パフォーマンスの最適化については目的別に二種類あって、遅い処理を高速化することと、遅くちゃいけない処理を速くすること、です。
前者は非常に解り易い話、一般に最適化といったらこちらです。
こちらの場合は、まずはプロファイラなどを用いて本当に遅い箇所を見付け、対処します。
殆どの場合は何万回も同じコードが使われる場所があって、これをボトルネックと呼びますが、そこを最適化すると全体のパフォーマンスが大幅に向上することも少なくありません。
パレートの法則に倣い、コード中の20%を改善すればパフォーマンスが80%改善するさえ言われているものです。
最適化の鉄の掟は二つあります。
ボトルネックから優先的に行うこと
遅そうだ、速そうだという感覚でやらないこと
つまり必ず前後で測定しましょうということ。
こちらのほうが速そうだと思って書き換えたらなぜか遅くなったーーこういうことはよくあるものです。
ですので、上に引用したようなツイートは指摘としては至極真っ当なものです。
その一方、(別に引用しませんが)「そんなのコンパイラに任せていい」など的外れなツイートもありました。
よくある勘違いとしては、「近年の最適化コンパイラの吐くコードは素晴らしく、アセンブリレベルでは人間の手が入る余地がない」というものです。
前半はとても正しい。
確かに近年の最適化コンパイラの吐くコードはとても素晴らしい。だからといって人間の手を入れる余地がないとは限りません。
コンパイラが古い命令セットしか知らない場合もありますし、そこまで極端な場合でなくともコンパイラの吐くコードを見て初めて問題に気付くということもあるものです。
コンパイラは仕様に反するコードは吐かないから、プログラマが完全に言語と処理系の仕様を知らないと最適化には足枷がつくものなのです。
(まぁそれもこれも"最適化"コンパイラの話。 JIT などのコンパイラはコンパイル時間そのものもパフォーマンス要求として考えられますので、最適化なんてものは殆ど行われないのです。少し前と比べりゃほんの少しマシになったかも知れませんが、今のところはまだ最適化というより「いくつかのケースでは最悪を避けられるようになった」と考えるべきです)
いくつか実例を交えて説明することもできますが——今回のエントリのメインはその一般的な最適化の話ではなく、後者の「遅くちゃいけない処理を速くすること」のほうなので脱線する前に話を戻しておきましょう。
まず「遅くちゃいけない処理」とはどういうことか?
これは例えば実時間に関連した処理のことです。
- ユーザーのインプットを受けて、次の画面の更新までに処理を終える
- 50ms以内にPCMサンプルを1000サンプル処理する
仮に十分とか、そういう時間の単位で見た場合は予想時間内に収まるとしても、ある瞬間を見た場合に追いつけなければ、ユーザーがイライラしたり、音が切れて不愉快なノイズがスピーカーから出たり、マウスのポインターがカクカクしたり、そういう処理のことです。
こういうのをまとめてリアルタイム処理といいます。
リアルタイム世界では「1秒あたり100回処理するということは、一分間で6000回処理できればいいのね」というのは正しくありません。仮に一分後に6000回処理できているとしても毎秒100回の処理が50回になったり150回になっては困るのです。
過ぎた時間は戻らない。統計のトリックでは誤摩化せない。それがリアルタイムの世界。
enchantMOON はユーザーの入力を決して取りこぼさないよう、優れた書き味のために最適化を施しているとされますので、文脈からいって件の記事はリアルタイム処理を前提としたものだと考えていいと思います。
リアルタイム処理をリアルタイムで処理できないということはどういう結果を齎すか、幸いというべきか皆さんが日頃から体感できることですので別のエントリであげつらうとしてここではリアルタイムの最適化とそうでない最適化では話が違うということを解ってもらえれば充分です。
最も強調したいのは、こういう遅延はプロファイラでは観測しにくいものだってことです。
だってユーザーの入力が一秒間に何万回もあるはずはありません。サウンドのデータが一秒間に何百万サンプルにも増えるというのは(今のところは)ありません。
普通にプロファイラをかけて発見できるようなのはもっと長い時間単位での支配的な処理ですので、 vector の操作だったり、 list のつなぎかえだったり、システムコールの呼び出しだったりするものです。
だからリアルタイムの最適化ではプログラマの勘と経験がモノをいうことも多々あるのです。
そうした事情を踏まえてこそ件の清水さんの記事は面白く読めるのであって、「こんなの意味あんの?」と思って読むのはズレていると思う。
(もちろん、そういう遅延を発見するためのプロファイラの使い方テクニックはあるのですけどもね)
清水さんの記事には追記があって、最適化前後のデータが数値で表されているわけですけども、どこで誰が試しても23msが6msになるというわけではないから、数値として意味があるわけではないでしょう。これはそういう前提に立てない人向けに信憑性を高める意味であって、そういう前提に立てない人がこの数値を見たところでやっぱり本質的なところは理解できず、「なるほど速くなったね。でもそれって意味あるの?」とか言う気がする。
リアルタイムの処理では23msと6msは雲泥の差です。支配的なボトルネックでないなら、全体が4倍になるということはあり得ないけど、それでもこの17msが惜しいということはあるんです。
僕も20ms以上かかっていた処理を6msまで最適化したことがあるけども、これがどれくらいの差かというと BBC Internet Blog の Anthony Rose の記事で言及されるくらい。
——「でもそんなの、CPU が速くなれば問題ないでしょう?」
7-8年くらい前なら僕もそれに同意できたかも知れません。
でも、ここ数年 CPU の進化は止まっています。CMOS の低電圧化が限界になり、プロセスルールも頭打ちするなかでクロックは上がらなくなっています。ここ三年ほどは特に顕著に出ています。おそらく次の三年も状況は大きく変わらないでしょう。
と、前提を踏まえたところでリアルタイム処理のための最適化に関する細かい技術の話はまた別のエントリで、と致しましょう。
以下余談です。
CPU のクロックが鈍化し、遂には殆ど止まってしまったような昨今ですが反対にソフトウェアは仮想化が進んで、ネイティブコード以外の配布形態も多くなりました。
Android は Dalvik VM をアプリのランタイムにしているし、enchantMOON なんて JavaScript の VM をミドルウェアに据えているわけです。
ハードはそれほど速くなってないのにソフトだけどんどん重くなっちゃったわけですね。
背後にはモバイルプロセッサの飛躍があるわけですが、バッテリーに技術革新がなければそれも頭打ちです。
ソフトウェアの生産性のためには VM が素晴らしいのですけども、そのソフトウェアの生産性ってやつがユーザーの要求にマッチしなくなれば結局廃れる運命にあるでしょう。
JavaScript でアプリが書けるんだぜぇ〜と山ほど生まれて来た有象無象の web アプリで生き残っているのってなんでしょうか?結局開発者と投資家が喜んで、ユーザーは喜んでないのではないかという疑念はここ数年ずっと晴れてません。
僕だってユーザーだが、PC をアップグレードする理由は自分の仕事を速く済ませるためであって開発者に楽させるためでは断じてあり得ないわけです。
そのユーザーが「 Facebook アプリはネイティブで(僕らのいうネイティブとは違うけど)提供しろ」と言ったわけで、この意味は大きいと思うのですが……。
Twitter は web に回帰しつつあるけど、成功するかは解らない。
ソフトの生産性重視、実行時パフォーマンスの度外視といったトレンドはもうそろそろ古い常識になるかも知れないなぁ、とここ数年は考えるのでした。
0 件のコメント:
コメントを投稿