「推測するな、計測せよ」という訳はミスリードと言う話

パフォーマンス改善の文脈で良く用いられるフレーズとして、「推測するな、計測せよ」というものがある。これはRob PikeのNotes on Programming in Cからの引用なのだが、原典と少し印象が違う。

Rule 1. You can’t tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you’ve proven that’s where the bottleneck is.

Rule 2. Measure. Don’t tune for speed until you’ve measured, and even then don’t unless one part of the code overwhelms the rest.

訳出する(正直あまり自信がない)と

Rule 1. プログラムが何処で処理時間を費やすのかは分からない。ボトルネックは驚くべき場所で起こる。だからその箇所がボトルネックであるという証明をするまで、当てずっぽう*1で高速化のためのハックを入れようとしてはならない*2

Rule 2. 計測せよ。計測するまで最適化*3をするな。そしてたとえ計測したとしても、ある部分のコードが圧倒的に残りの部分より重い場合でなければ、最適化をしてはならない。

つまりRob Pikeが批判しているのは「合理的な根拠なしに決めつけてハックをすること」であって、「推測」すること自体ではない。

むしろ私は「適切な推測」こそがパフォーマンス改善の肝であると言いたい。

 

そもそも、「適切な計測」は「適切な推測」なしに成り立たせるのが難しい。ある程度ボトルネックを推測し実験(計測)を繰り返しながら仮説を少しずつ強化し、改善策を入れるという作業が実際のパフォーマンス改善のワークロードに近い。

スクリーニングテスト的に一通りツールを試す価値は認めるが(詳解システムパフォーマンスのBrendan Greggの言うUSEメソッドなど)、推測なしに計測したところで一見問題ないように見えてしまうことがある。例えば代表値として平均を用いている場合bimodalやtail latencyは見えない。

他にもベンチマークツールやワークロードについての理解を得ずに計測した場合、計測した値が自分の意図したものとは異なっていることがある。例えばストレージの速度を計測しようとして実際にはページキャッシュ(つまりメモリ)の速度を計測しているといった状況はよくある。

Brendan Greggは計測こそベテランがやれと語っている。

 

また、ボトルネックの原因を理論的に「証明」することは現代の複雑すぎる計算機では実質的に不可能に近い(もしくは非常に煩雑である)場合がある。多くの自然科学が実験によってブラックボックスを解きほぐしていくように、推測を行ってはそれを棄却ないし強化するために実験を繰り返し、最終的にある仮定が正しいと信じられるだけだ。「証明」するというより、推測を積み上げた先に「限りなく正しいと思われる推測」が作られる。

Optimized C++でKurt Guntherothが言うように、必ずしもソフトウェアエンジニアは「証明」に取り組む必要はない。結局のところ、パフォーマンスが十分に改善されてしまえばそれでよく、「証明」自体は労力に対して得られるものが少ないことがあるからだ。必ずしもボトルネックの仮説を十分に強化してから改善に取り組まなければならないわけでもない。ある程度の整合性のある仮説があり、その仮説に則った改善策によって十分改善されるのであれば、その改善自体が仮説を強化出来る。

 

Brendan Greggはパフォーマンスの改善を医者の仕事に喩えているが、私はこのアナロジーが好きだ。医療の世界に擬えると、健康診断はスクリーニングテスト的だし、サットンの法則のような警句は良い指針になる。医者と違って我々は気軽に実験してマシンをリブートすることが出来る、なんと素晴らしいことか。病理医の仕事を描いた漫画の「フラジャイル」はパフォーマンス改善に取り組んでいる時に読むと刺さりまくったのでおすすめしたい。

 

8/28 追記

「そんなミスリードされるやつはおらん」と言う反応が思いの外多くて驚いた。その中には「推測するな、計測せよ」と言う訳/短縮は良くないという主張ではなくて、Rob Pikeの原典自体がミスリードなものという主張として受け取っている人が散見されたのでタイトルを修正しておいた。

私の日本語の感覚だと「根拠なしにハックするな、計測せよ」という主張を「推測するな、計測せよ」と訳すのはなかなか意味が変わっていると思う。実際のところは仮説検証のサイクルが肝要で、これは仮説を立てることが先んじてその検証は計測でなくても良いにも関わらず、measureがguessより大切に見えてしてしまう。

「推測」を「憶測」に変えれば良いとか、「推測だけするな」に変えれば良いだとかはまぁ今よりはマシだと思う。けどこういった短い言葉の捉え方の議論は紛糾しそうなので意図的に避けた。

結局短縮された格言は字面の意味が変わっていることがあるので原典を参照しようねというだけの教訓かも。

 

ベテランの人たちが「そんなことは当然だ」と主張するのはもっともで、あなたたち向けの記事ではないので安心して欲しい。こういった短縮された格言は初学者によく頼られる(また誤解される)ものなので。

 

そういった事件があったのかという指摘はギクっとして、何も仮定を立てずにそれっぽいツールが出してくるそれっぽい数値を参考にパフォーマンス最適化をして沼った経験がある。ベンチマークやワークロードをブラックボックスにしてしまうのって結構やってしまうと思っているんだけど、この正当化に「推測するな、計測せよ」が使われているシーンは何度も見た。つまり計測しているので正しいと主張してしまうケース。

心当たりが無い?本当に?

*1:guessは日本語の「推測」より「合理的な根拠のない推測」というニュアンス。second guessはより当てずっぽうというニュアンスのようだが、考え直すという意味で使われることもあってややこしい。

*2:Don't A until Bは「BするまでAするな」から転じて「Aをして初めてBをしろ」と訳出することが多いが、この文はそう訳出すると「ボトルネックがわかってから当てずっぽうでハックしろ」となってしまい意味が通らないので困る。もしかして原文もちょっとよろしくない?

*3:HackとTuneが対比されていそう。Hackはやっつけ、Tuneは最適化というニュアンス。

僕は勉強が嫌いなのかも知れない

僕は常々自分のことを「目的と手段が倒錯した人間」だと評価していた。何かを作るためにコンピュータを学ぶべきであるはずが、その学ぶ行動自体が楽しいと思う人間であった。何かを作るためにOSの機能を調べ始めたはずが、いつの間にかOSの仕組みを延々と調べ気づけばCPUの仕組みまでレイヤが下がっている。そしてもともと何をやろうとしていたのか思い出せなくなる。

「学ぶことが好き」というのは多くの場合肯定的な文脈で語られる。大抵の人間は学ぶ事を忌避するからだ。 しかし、僕はこの倒錯をよく思っていなかった。「学ぶことが好き」というのは自分自身の興味の強さに依存している。学ぶにつれて目新しさは失われ興味の強さは薄れていき、相対的にその分野の魅力は低減していく。やればやるほど魅力が低減していくというのはある種の絶望があった。 ともかく、「学ぶことが好き」な僕は驚異的に飽き性だった。ほとんどの趣味は半年ほど齧っては飽きるを繰り返していた。

コンピュータに出会ったのは高校に入ってすぐの頃だった。暇を持て余していた*1僕は「パソコン持ってたらエロゲ貸してやるよ」というなんとも男子校らしい誘い文句に釣られてパソコンを買ったのだった。当時その友人に「デスクトップパソコンとノートパソコンってよく聞くけどなにが違うの?」と聞くくらいに僕はコンピュータは門外漢だった。

コンピュータという新しいおもちゃを手に入れた僕は直ぐにそれにハマった。そして当時から飽き性を自覚していた僕は「もしこの趣味が1年続いたらそれを仕事にしよう」と決めていた。果たして1年後、僕はまだコンピュータにハマっていた。授業中にコンピュータを触っていて怒られたり逆アセンブラを紙に印刷して読んだりして遊んでいた。

当時の僕はコンピュータに飽きなかった理由を「分野があまりに多岐に渡っているため」だと分析していた。コンパイラの仕組みを調べるのに飽きたらOSのメモリ管理を調べればいいし、ファイルシステムを深く追っても良い。その広がりが飽きない理由だと思っていた。 たまに「飽きて」は別分野をやるといった事を繰り返していた。

しかし大学3年以降、コンピュータ自体に対する興味が薄れているのを感じた。コンピュータの勉強時間が減り他分野を勉強して遊んでいた*2

就職するときこれは大きな恐怖だった。「コンピュータに飽きるかもしれない」のに僕は他の進路を選ぶには長い時間をコンピュータに捧げすぎた。他のスキルセットは特になく、また性分にも合わないだろうと特に他の選択肢は考えずにIT系の社に入った。学生時代は新しい分野をやりたくなったらカジュアルに乗り換えることが出来たが、就職してからはそうはいかないだろう。飽きた分野をやることは苦痛でしかなく、なにかの拍子に自分のコンピュータに対する興味が尽きたとき、僕はどうすることも出来ない。

入社して5ヶ月経った。自分を客観視する機会があったため、久しぶりにこの恐怖に向き合った。

その結果、実は僕は勉強がずっと嫌いだったのかもしれないと気付いた。逆説的ではあるが、これは「勉強が好き」よりもずっと僕にとって救いだった。好きでやっていた事は嫌いになると苦痛かもしれないが、もともと嫌いだったなら大丈夫だろう。 コンピュータの勉強は何も舗装路だけではなく、歴史的経緯により洗練されていない箇所があったり考古学を強いられたりと煩雑でお世辞にも面白いとは言えない時も多い。

思い返せば僕がコンピュータをやっていて一番楽しいのはコンピュータを触っていないときであるようだった。必要な知識を頭の中にすべて入れたとき、思考するのにコンピュータは要らない。ぼくのかんがえたさいきょうのXXXを散歩しながら考えられる。 僕はそのために勉強しているらしかった。不要とも言えるまでに知識を持つことでコンピュータと向かい合わずとも仮定を棄却し遊ぶ事ができる。

これは思考自体に面白さを見出しており、興味の強さに依存しているわけでは無い。どうやらしばらくはコンピュータで仕事が出来そうだ。

今日も僕は勉強が嫌いだと自覚しながらシグナルハンドラの仕組みを調べる。未来の僕が遊ぶために。

*1:中高一貫の中学を辞めて別の高校に入学したため全ての授業が1年前にやったことだった。あまりに暇すぎてとなりの席の人間がやっていたからという理由だけでスケボーをやるくらいだった。

*2:おかげで釣りや解剖学や車の仕組みに詳しくなったりした。

釣り人のための魚の持ち帰り方

最近友人と釣りを始めた。釣りには、釣れた魚をリリースする釣り自体を楽しむ方法もあるが、私はもっぱら食べるために釣りをしている。であれば、必然鮮度良く魚を持ち帰ることに興味がある。いくつか文献を漁ったので纏めておく。出典は面倒なので書かない(は?)

前提

魚の保存には「こうした方が良い」という目標があり、それに向けて保存法を考えるのだが、相対した主張をする人も居る。そこでまず何を目標にするかを下記に記す。対立意見についても後に纏める。

  • なるだけ死後硬直を遅らせるべき

刺し身にする場合は死後硬直直後が食べごろであるため、これをなるだけ遅らせることが鮮度を長く保つことにつながるとされる。

  • 魚を5-10℃程度に保つ

どうやら0℃付近より多少高いほうが良いようである。おそらく理由は2つある。1つ目は0℃付近であれば死後硬直が早まるため。2つ目は0℃付近であれば魚が氷焼け(部分的に凍る)するため。

  • 魚の深部までなるだけ早く冷やしたほうが良い

魚の体温と保存に適するとされる温度には差があるため、出来るだけ早く適正温度に到達すべきであるとされる。

  • 血抜きをすべき

身に血が回ると生臭くなるとされる。

  • 魚は海水に浸して保存すべき

浸透圧の関係から、魚は真水に当てるべきで無いとされる。

鮮度とは

魚の鮮度を測るにはいくつかのパラメータが存在する。主に使われるのはK値、硬直指数、筋肉破断強度である。

K値は魚の中に存在する物質が時間と共に変化することを利用して、その比率を元に鮮度を表している。一般に単調増加するパラメータであるが、変化途中の物質が旨味であるため、必ずしも低い数値である時に食べごろであるとは言えない。

硬直指数は魚を横にした時にどれだけ垂れ下がるかを表している。一般に生物は死後に死後硬直と呼ばれる硬直現象が起きる。死んだ直後の魚はぐったりとしている(身が活きていると形容される)が、徐々に硬くなっていき、数時間掛けてまた柔らかくなっていく。釣った魚をクーラーボックスに入れていると反り上がるのを見ると思うが、それが死後硬直である。

筋肉破断強度は魚の身を破断するのに必要な力を表している。すなわち、魚の歯ごたえはこれと相関がある。

直感的には硬直指数と筋肉破断強度は相関があるように思われ、実際ある程度の相関はあるのだが、長い時間が経つとその限りではないらしい。

具体的な保存方法

これらの情報を元に私なりに考えた保存法を記しておく。

  • 氷の準備

保冷剤でも良いが、ペットボトルで氷を作っておくと帰ってからすぐに捨てられるので便利。

どれくらいの氷が必要かだが、これは外気温と海水温とクーラーボックスの断熱性能とどれくらいの時間釣りをするのかとクーラーボックスの容量に依存する。クーラーボックスに満杯にした海水を0℃付近まで冷やすことが出来るのを目安にすると良いと思う。シマノのフィクセルライト17Lで大阪の夏に6時間釣りをするなら5L、冬なら3Lあれば十分という見積もりになる。

後述するが、1-2L程度は10%程度の濃度の塩水を凍らせて融点を下げておくと良いと思う。

また、真水を凍らせたペットボトルはぷちぷち梱包材で巻いておくと良い。こうすることにより魚に直接氷が当たらないため氷焼けしない。真水の氷はそもそも比重が水より小さいので海水に浮くが、より浮きやすくなるメリットもある。冷やした水に浮かせてその下に魚を入れておくと落し蓋のようになり便利。

  • 釣り場に到着してからの準備

クーラーボックスの1/3ほどに海水を入れて冷やしておく。塩水を凍らせたものは融点が低いため速く溶ける。海水を5度程度に冷やす専用の氷である。面倒であれば砕いた氷を海水に入れても良いと思う。こちらのほうが速く溶ける。多少海水が薄まるがそこまで気にしなくて良いようだ。温度が下がり次第氷を取り出して血抜き用のバッカンに入れておく。

  • 魚が釣れた時

まず脳の部分にナイフを刺す。もしくは鰓蓋からナイフを入れて脊椎を切る。少なくともこれをやっておかないとクーラーボックス内で暴れるので注意。アジなど小さい魚はいきなりクーラーボックスに入れてもすぐに死ぬので〆る必要が無い。

血抜きはいろいろなやり方があるが、エラの下側をハサミで切るのが簡単かつ効果が高い気がする。脊椎を断ち切った場合はそもそも脊椎に通っている血管が断ち切られるのでエラを切る必要が無い。

エラを切ったらすぐにバッカンに突っ込んで魚を冷やす。魚は死後体温が上がって身が焼けるらしい(多分小さい魚だとそれほど顕著ではないが)。脳が死んでもしばらく心臓は動くので、上手く行けばしばらく放血される。一般に行われる血抜きは心臓の力に頼っているため、釣れてからここまで迅速に行って心臓を傷めないことが寛容である。あまり冷やしすぎると心臓が弱る気がするのでバッカン内の海水は汲みたてかそれより数℃低い程度で良いと思う。夏場は温度が上がりやすいので氷を入れておくと良いが、冬なら何もしないほうが良さそう。

神経締めはある程度大きな魚でないとそれほど効果が無いようなので、ショア釣りしかしない私はまだ手を出していない。

  • 帰る時

魚が浸るくらいに海水を残して捨てると良い。海水を多めに入れるのは魚を迅速に冷やすためと魚を入れた時に温度が上がりすぎないようにするためであり、帰る時にそれをするメリットはない。完全に捨てないのはある程度海水を残すことですぐに温まらないようにするためと、魚に直接圧力を掛けないようにするためである。

氷が溶け切っていたなら海水の上にぷちぷち梱包材で落し蓋をして、その上にコンビニやらで氷を手に入れて載せておくと良い。

本当に5-10℃が適正温度なのか

過度な冷却によって魚は急速に死後硬直をする。これは魚の体温(海水温)と冷やした温度の差が大きいほどに顕著であるらしい。であれば、5-10℃ではなく魚の体温-10℃前後が適正だったりしないだろうか。これは冬場と夏場で適正温度が変わることを意味する。

なお、5-10℃という温度は死後硬直が終わるまでの話であり、捌いてからはチルド室(0℃付近)で保存した方が良い。

本当に魚を真水に当てるべきではないのか?

イカなどは顕著に水っぽくなるらしいが、魚についてはそれほど気にしなくても良いという説がある。そもそも魚は海水より塩分濃度が低いらしいので、海水に氷を入れて若干薄まる程度は問題ないように思う。

本当に血抜きをすべきなのか?

血は旨味であるという主張をする人もおり、焼き魚などであればそれほど血抜きをする意味はないかも知れない。ただ、捌く時に血抜きがしてあれば楽だし、見た目にも綺麗という利点がある。長期に保存する時は必須らしいが、数日程度であれば味というより見た目の差が大きいのではないかと思っている。

破断強度と硬直指数の関係

硬直指数と破断強度は少しズレが有るようだ。私が読んだ資料では硬直指数の高まりに応じて破断強度が上がり、その後硬直指数が高いままにもかかわらず破断強度が下がっていた。これは実際釣りに行った時にも感じた。クーラーボックスから取り出したときは身が反り上がって死後硬直している様に見えるのに、いざ捌くともう身が柔らかくなっている。これは死後硬直後に冷やしすぎて半分凍った状態になっているのではないかと思う。明らかに死後硬直する時間を過ぎているにもかかわらず反り上がっている魚は常温の水に当てるとすぐにくたっとする。

そもそも破断強度は死後硬直と関係なしに単調に下がるらしく、それに死後硬直が合わさることにより単調性を失っている様に見える。

真空断熱のクーラーボックスって必要?

クーラーボックスの断熱性能というのは大凡断熱材に依存しており、発泡スチロール<発泡ウレタン<真空断熱となる。発泡スチロールと発泡ウレタンには実はそれほど差が無いのだが、真空断熱は性能が跳ね上がる(ついでに値段も)。果たしてこれほどの断熱性能が必要なのだろうか。

結論から言えば、外気温と釣りに当てる時間に依存する。長い時間釣りをするなら高性能なクーラーボックスを買う意味があり、そうでないならあまり意味がない。

私の保存方法はまず海水を冷やすことを前提としており、これに必要な負の熱量は外気によって与えられる熱に対して十分大きい。もし海水を冷やさなかったとしても、結局魚はほとんど水を冷やすのと同じであり、夏場に5kg分の魚(25℃程度)を冷やすのに必要な負の熱量は大きい。

しかし、釣った後に一晩泊まるなどであれば話は変わる。発泡スチロールと真空断熱はせいぜい3倍程度の断熱効果の差しか無いが、24時間オーダーであれば魚を冷やす熱量に対して無視できない程度の差になる。

安いクーラーボックスであれば大量の氷を持っていけば良く、労力をとるか値段をとるかどうかという問題である。

また、多くのクーラーボックスは外気温の設定こそあるが直射日光を当てると変わるかも知れない。クーラーボックスに直射日光を当てるとどの程度変わるのか分からないが、真夏に数時間放置するのは無視できない気がする(そもそも照り返しがある分夏のクーラーボックス底面の外気温は50℃とかになるのではないか)。

クソ雑魚ナメクジのためのオーバーウォッチ立ち回りPart2

前回クソ雑魚記事を書いたがその後いくつかノウハウが溜まった。前回の記事ではサッカーで言えば「オフェンスやディフェンスが合ってそれぞれこういう動きをすべきで」みたいな話を書いていたが、もっと基礎的なことが必要だと気がついた。今回は「ボールはこうやって蹴る」的な話を書いていく。

マウスの持ち方

一時期つまみ持ちをしていたがクリックする時にどうしてもぶれてしまうのでかぶせ持ちに落ち着いた。正直持ち方は何でも良いのだが、絶対に崩してはいけないポイントが有る。それは「マウスをまっすぐ持つ」ということだ。僕は最初少し斜めに持っていたために右側の可動域が狭かった。普通ゲーミングマウスならまっすぐ持てばちゃんと左右の可動域が同じくらいになる。また、手首を固定して肘を軸に左右に動かした時ポインタが上下に動かないか確かめると良い。ペイントソフトを使えばチェック出来る。

感度とゲーム内センシ

プロは振り向き15センチあたりが中央値のようだ。他のゲームであれば25センチほどなのでかなり高感度である。これは他ゲーに比べ振り向くことが多いからだと思われる。オーバーウォッチのプロの中でも振り向き25センチを超える人もちらほら居る。初心者ならそのあたりの感度から当てる感覚を掴んでから徐々に感度を上げていくと良いと思う。

肘AIMと手首AIM

一般にこの2つがAIMでよく使われる。僕は両方使うことにした。画面に写っている敵を撃つときは手首AIMで振り向きには肘AIMを使う。また肘AIMは机の上に肘を置くと安定する(それはそう)。机と椅子の高さの相性によっては肘を置きにくい。モニタを左側に置いて体を机に対して斜めにすれば右手肘を比較的楽に置ける。

普段の視点

ちゃんと画面中央を見よう。見ているところと画面中央が合っていないといけない。照準がアイラインにあれば遠くの敵も近くの敵も上下のポインタの動きなしでAIM出来る。自分のプレイの動画を見て照準が床に無いか確かめてみると良い。最初はモニタの位置を上げることで強制的に見ているところと画面中央が合うようにした。多分今ではどの高さでも出来るので必須ではない。

手癖でジャンプをしない

撃ち合いのときはジャンプをするべきではない。敵が当てにくくなる以上に自分が当てづらくなるからだ。特にフラバンなどのコンボの時に手癖でジャンプするのは全く意味がない。なかなか治らないならスペースキーを一時的に取ると治る。

手癖でリロードをしない

一人敵を倒したりチャージショットをした直後によくリロードが入っていた。十分残弾がある時にリロードをするのは射撃機会を逃すだけで意味がない。何発というより後何秒撃ててリロードに何秒掛かるかを把握すると良いと思う。トレーサーなんかは手癖でいい。

アニメーションキャンセル

いくつかの行動のアニメーションは別の行動でキャンセルすることで隙を消す事ができる。例えばゲンジで風斬セカンダリ近接はすべてキャンセルで入る(風切り前にメインセカンダリを使っていると風斬後にセカンダリが入らない)。他にもリロード直後に近接を入れればリロード自体のキャンセルをすることが出来る。このあたりはキャラ毎にとてもたくさんあるので自分の使うキャラはチェックしておきたい。

キャラ毎のテクニック

キャラ毎にこれは出来るようになりたいというテクニックがある。ゲンジであれば風斬セカンダリ近接で一瞬で164ダメ入る。ファラならコンカッシブでの移動。ジャンクラであればメインコンカッションでの200族ワンショットキル。トレモのBotには確実に成功出来るくらいにはしよう。

偏差撃ち

偏差撃ちにはAIMは要らないと宣う人が居るがそんなことはない。よく言われるAIM力と別の能力が少し必要なだけだ。オーバーウォッチではトレーサーとゲンジが5.5m/secであることを除きすべてのキャラが5m/secで移動する。またWikiで各キャラの弾速がわかるので、ある距離で射撃する時にどれだけキャラが動くか計算できる。別に数値で理解しなくてもトレモのBotが等速で動くので適当な距離を開けてヘッショを当てる練習をすればいい。偏差撃ちには2通りの方法がある。1つは「相手がそのままの動きをすると仮定する」ことで、普段はこの方法を採用する。もう1つは「相手の足元に撃つ」ことだ。後者の方法はファラでミリ残りの敵を狩り切る時などに使える。ファラの弾速は35m/secで20mから撃つとほぼ0.5秒で相手まで到達する。0.5秒で移動できる距離は2.5mで、ファラの爆風ダメは2.5mの範囲なので正しく足元に撃てれば必ずダメが入る。

撃ち合いでの移動

撃ち合いでの移動には3種類あると思う。1つ目は左右に動いて相手の射撃を躱すためのもの。2つ目は相手の動きをミラーして相対的にAIMしやすくするためのもの。3つ目は大きくポインタを動かす必要がある時にその方向に移動してポインタの移動量を小さくするためのもの。

右手と左手を協調させる

先に述べた左右に動きながらの射撃は「自分はどちらに動くか分かるが相手は分からない」からこそアドバンテージが生まれる。左右に動きながら一点を撃つ練習をしなければ意味がない。慣れると無意識で出来る。

当たるのを確認する前に撃つ

偏差が必要なキャラを使っている時に一発撃って当たか確認してから撃つのを繰り返していたが、それをやると発射レートが落ちるので確認せず撃つべき。強いて言えばファラは当たると爆風でノックバックが発生して着地狩り出来るのでそれを狙っているならアリ。

キルを取る

パッと見えるキャラを撃つのは全く意味がない。きちんと(一人で)フォーカスして狩り切らないといけない。VCがあればチームレベルでのフォーカスが出来るので強い。特にDPSをプレイする時はキルを取ることが期待される。

フォーカスの順番

基本的には「狩り切る時間の短い順」で良い。これを適用するとヒーラーDPSタンクの順になる。タンクでも体力がミリなら狙うべきだがVCで連携してないと難しいところ(体力がそもそも分からない)。後は真っ先に自分のアンチ(というより自分を狙えるキャラ?ファラであればソルジャー)を狙うのも良いと思う。

最大瞬間火力を覚える

各キャラが出せる持続的なダメージ量と瞬間的なダメージ量は大きく異なる。例えばゲンジが好例で、1秒で200ダメ出せるコンボがある一方でメインのDPSは100/sec程度しか無い。よく使うキャラの瞬間火力とそのコンボの練習はしておこう。特に近接を入れるとお手軽に30ダメ入るので忘れないように。

タンクの意義

200族を一人ずつフォーカスして狩ることを意識すれば分かるが、タンクが居ないと全くゲームにならない。その理由は200族だけだとフォーカスに対して弱すぎる(前線を維持できない)からだ。大抵のDPSの平均ダメージは(命中精度を考えると)100/secにも満たないが、瞬間的に100~200程度に達する。タンクであれば瞬間ダメを食らってもさほど痛く無いのでそのまま居座れるが、200族なら次に同じ瞬間ダメを食らったら死ぬため下がるしか無い。結果前線がじわじわ下げられて維持できない。加えて追撃にも耐えられない。

自分が楽でもチームが辛い事がある

相手にファラが居てジャンクラやリーパーを出している時など、どれだけ自分が刺さっていると思っていても味方がキツイ時がある。きちんとキルログを見てなぜ戦況が悪いか把握しよう。僕はよくファラを使うが、敵がウィンストンを出した時は大抵味方に負担が掛かることに気がついた。個人的にはDVAより相性が悪いと思っている。

メダルは意味がない

先の延長だが、自分がメダルを持っているのは味方に負担がかかっているだけなこともままある。ある時敵のモイラがとても下手に見えた試合があったのだが、彼は5金だった。死にかけの味方をヒールせずにダメージを出していればチーム内順位は上がる。このゲームではキルアシストもキルに数えられるためあまり当てにならない。ファイナルブロウ値はまだ気にする価値があると思う。

弱いキャラを使うべきではない

誤解を恐れずに言えばアナやソンブラといった弱いキャラは使うべきではない。トレーサーも余程上手かったとしても今の環境でキャリーは難しいと思う。Overbuffで各レート毎のキャラの勝率が見られるのでそこで低いキャラは控えたほうが良いと思う。例えばソンブラはプラチナ帯まで全キャラ最下位の勝率だし、アナはサポートの中で最下位である。トレーサーはアタッカーの中ではソンブラに次いで低い。確かに高レートであれば刺さるキャラ達ではあるのだけど、使い方が難しい上に正しく使ってもキャリーがし辛い(味方が上手いことを前提としたキャラなので)。

ピックブールを広くする

各ロール2キャラは使えるべきと言うが、正直もっと無いと厳しい。タンクヒーラーDPSのどれか1つは3キャラ以上使えるようになりたい。選ぶキャラの指針はメインで使うキャラのアンチのアンチ、またそのアンチのアンチである。現状DPSならファラとリーパーをよく使うのだが、その両方のアンチであるソルマクウィドウあたりのアンチはゲンジである。だからゲンジを使えれば強いと思うのだけど難しいんだなぁ……。各ロールで相手のこのキャラが暴れてたらこのキャラ出そうというくらい出来ていると強い。

敵になるだけ近づく

一般に敵より自分の得意レンジが遠い場合、相手のレンジより少しだけ離れた位置で戦うのが良い。当たり前だが近づいた方が当てやすく火力が出るためだ。例えばファラは状況によってはかなり敵の近くまで近づいていい。逆に敵の得意レンジの方が遠い場合、一気に近づけたりタンクを伴って距離を詰められない場合を除いてガン逃げすべき。

裏取りはすべきではない

裏取り戦法はとてもむずかしい上に低レートではあまり刺さらないと思う。というのも、低レートでは1ピックの重みが小さいためだ。高レートであれば人数差がついた瞬間にちゃんと攻めるしきちんと狩りきれる技術が備わっているが、低レートではそうではないため。むしろ裏取りで慢性的に火力差が付く欠点のほうが強く出る。

魔境は存在しない?

魔境というのは英語でElo Hellと呼ばれる。とはいえ、これは下手人間が言い訳として用いているだけという指摘も多い。LoLという別ゲームの話だが、いくつか参考になる。

いかに Elo hell(魔境)から脱出するか | once majigomi, always majigomi

[Learn ****] Why I'm 1800+ and how to get here - League of Legends Community

重要なのは「レートを上げる」というのは「自分がキャリーして勝たないといけない」ということを意識することだ。 Skill separates good players from great players. Understanding separates good players from bad players.は至言だと思う。

クソ雑魚ナメクジのためのオーバーウォッチ立ち回り

解説記事というのは往々にして人間向けであって、ブロンズと言った知性を感じさせないレート帯に向けてのものではない。 ここではブロンズの人間が出来ていないであろう事柄を書いた。

ここに書いてあることは極めて自明なことしか無い。しかしそれを徹底できないからこそクソ雑魚なのだ(自戒)。 なお僕はこれを書いている時点でブロンズです(は?)。有益な指摘を歓迎します。

キルログを見よう

いきなり自明。しかし本当に出来ていますか。任意のタイミングで敵味方誰が残っているか管理出来ていますか。混戦時に出来ていますか。 実際混戦時にキルログを見るのは極めて難しいと思う。VCがあると無敵。

全て管理するのは難しい。特に重要な情報だけでも覚えよう。 敵味方が何人ずつ前線に居るか(枚数差)。 相手、味方のヒーラーが落ちているか。 自分のアンチキャラが落ちているか。 例えばファラのULTは上手いD.Vaが居れば突っ込んできて潰される。

ピックを見よう

最初のピックを見ている人は多いと思う。しかし敵がキャラを変えたのに気がついていますか。敵が大量落ちした時などは特にピックが変わりやすいので確認しよう。

タンクは不可欠

僕は最初タンクが重要という感覚が皆無だった(僕はそもそもFPSどころかゲームをあまりやった事がありません)。

タンクの重要性を理解するにはまずFPSは陣地ゲー的な側面があることに気付く必要があった。 自明なことだが、撃ち合いは囲んだ方が強い(エイムが簡単になる、囲まれている方はフォーカスが合わない)。 例えばソルジャーに高台を取られて横から撃たれている状況下において誰もそれを潰しに行かない事がある。結果はもちろん惨敗する(一方的に瞬間200dpsを出し続けられる)。 よくFPSでは「高台が強い」と言われるようだ。これはタイマン同士で被断面積が減るという問題より、どちらかと言えば囲い込めるというのが理由だと思う。

さて、FPSは陣地ゲーである。この時耐久力の高い(かつ多くの場合近接で強い)タンクキャラは「強い」陣地となる。 つまり、タンクキャラ周辺はちょっとのことでは崩されない。

先の例で言えば「弱い」陣地であるソルジャーが高台を取っているなら「強い」陣地であるD.Vaで突っ込めば終わる話なのだ(そしてその後こちらのDPSキャラが高台を取れば優勢を取れる)。

加えてこのゲームの基本的なルールを思い出そう。陣地をとったり陣地を進める(ペイロード)ゲームである。 陣地ゲーなのに「強い」陣地が居なければゲームにならない。これがタンクが不可欠な理由である。

敵と味方の位置を把握しよう

ぶっちゃけヒーラー、VCアリとかでも無い限り完全に把握することは出来ないと思うけど出来ることは多い。

囲い込まれないようにしよう FPSは陣地ゲーという意識を持とう。俯瞰した時に敵と味方を色分けするとどうなるかを考えると良い。ふと思ったけどスプラトゥーンってこっから生まれたのかな。

目に見えない敵に意識を配ろう 例えば敵マクリーやリーパーが見えない時は裏取りULTを警戒しよう。

味方がついてきているか確認しよう タンクをやる時は必須。単身特攻してヒーラーが付いてこないと文句を言うハメになる。

判断を早くしよう

実際は「予測」をしようというのが適切。 上級者は多分頭の回転が速いというより経験による予測と決め打ちが適切。

2人以上の差が付いたら退却、集合

一般化すると1.5倍以上程度の人数差が付いたら。よほどの能力差が無い限りこの人数差で勝つことは出来ない(そして同レートでそれは無い)。 誰も集合しない?リスポ地点で待って集合ラジチャしたら流石に大体の味方は意図を察してくれる。

攻撃サイドの場合はワンチャン特攻をしよう

僕が以前やっていたゲームはリスポーンがなかった(オーバーウォッチと比べ極めて死亡コストが重い)。その為8割勝てると思った時しか突っ込まなかった。 しかしオーバーウォッチでは何回死んでもいい。3割勝てる勝負を4,5回やれば8割どこかで勝てる。 それで言えば勝率5割と言うのはたった勝率2割の勝負を4回やるより低い。

時間ギリギリになるまで何もしないのをやめよう

先も書いたが3割勝てる勝負を4,5回やればよい(この集団同士の当たりをウェーブという)。 「のこり30秒」アナウンスが鳴るまでマトモにULT使わないのやめよう。さっさと使っていけ。

ULTのタイミング

低レートだと溜めすぎる人が多い気がするので積極的に吐いていこう。

一般にULTは有利/不利な状態で吐いても意味がない。 不利からイーブンにしてULTを無駄にするより集合してイーブンからULTを吐いて有利にすべきである。 これは攻撃/防御のどちらかで変わるかも。防御サイドなら不利でも吐くべきかもしれない。 しかしよく時間がギリギリだからとリスポする味方を待たずにULTを吐いてしまう人がいる。焦らずにみんな揃ってからULTを吐くべき(時間ちゃんと見ような)。 低レートだと有利な状況で吐いて確実に殲滅するのは意味があるかも。殲滅能力が低いので敵のリスポが間に合って泥沼化するよりは良い。

攻撃系ULT

リーパー、ファラなど

確実に2人以上落としたい。自分が死に得ることも多いので3人落とせればサイコー。

協力系ULT

ザリア、ハルトなど

味方が援護してくれる場合でないと意味がない。 ULT連絡をして、吐く前にしっかり後ろをチェック。

支援系ULT

ルシオゼニヤッタなど

敵のカウンター、味方のULTのタイミングに合わせてダメ押しとして使う。 ピックと敵味方のULTゲージを大まか把握しておく。 誰がULTを吐いたら合わせるかを決めておくと良い。 例えば敵ザリア、ハルトULTのカウンター、味方のハルトULTのダメ押し(シャター決める時はそのまま前に出るのでハルトが死ぬシーンがままある)。

異端ULT

メトラ

低レートだと死にやすいのでテレポーターが吉。

上手く行った戦法にこだわらない

上手く行かなかった戦法を続ける人はそう居ないと思う。 しかし、最初に上手く行った戦法を続ける人は居る。

例えば相手が最初盾キャラが多い時にジャンクラリーパーを出していれば多分圧倒できる。しかしその後ファラを出されたら惨敗する。 いつも柳の下にどじょうが居るわけではないのだ。

アンチキャラを知ろう

相手の最も暴れているキャラのアンチキャラを出そう。

変に2-2-2に拘ったりせずに自分で対抗できるピックにしたり柔軟に対応しよう。 そもそも2-2-2と言うのはバランスが良い(敵が最善を尽くした時に負けにくい)編成であって、ピックが壊れやすい低レードだと多少壊れることより誰も猛威を振るう敵キャラに対抗できないほうが不味い。

自分でやろう

ヒーラーが居ない、タンクが居ない、誰もタレット壊さないetc...

お前がやるんだよ!!!!!!!

それでもダメだと思うならパーティ組んだりVC組んだりしよう

vermagicとmodversionsを偽装する方法(Dirty Hack、備忘録)

LinuxではKernelをコンパイルした際、異なるversionのmoduleをインストールして不具合が起きないようにチェック機構を設けている。

これは通常、正しいversionのlinux-headerを指定することで解決するのが筋である。しかし、Raspberry Piで同じことをやろうとしたがなかなかうまく行かなかった。

次善の策としてはmodprobe -fでチェックを無視できるはずなのだがこれも上手く動かない(無視されてるらしい?)

そこで無理やりどうにかすることを考えた。

kernel/module.c

moduleに関する処理はkernel/module.c中にある。これを読んだ。

チェックはsetup_load_info中のcheck_modstruct_versionで行われる

github.com

これはkernel optionでCONFIG_MODVERSIONSが有効であった場合、常に1を返す(チェックが無い)。 CONFIG_MODVERSIONSが無効であった場合、該当するkernel module中から.modinfoと__versionsというsectionを探し正しい値かどうかを確かめる。

そこで.modinfoと__versionsを正しいmoduleから引っ張ってきて差し替える。

具体的にはobjcopy --only-sectionで正しいmoduleから抜き出し、objcopy --remove-section --add-sectionで差し替え、objcopy --set-section-flagsでsectionのflagを調整する。

どう考えてももっと良い手法があるはずだけどRaspberry Piは色々めんどい。

How to use keyboard/mouse for PS4 part4 -challenge response-

How to use keyboard/mouse for PS4 part1 -Sniffing HCI-UART- - aki33524’s blog

How to use keyboard/mouse for PS4 part2 -How does Bluetooth work?- - aki33524’s blog

How to use keyboard/mouse for PS4 part3 -Btstack is GOD- - aki33524’s blog

DS4は非正規製品の流通を防ぐため、認証の機構を備えている。このパートではそれがどのような仕組みなのかを解説する。完全に理解したと思っているのに認証が通ってくれない。USB snifferが欲しい……

General

DS4の認証はChallenge-Responseベースである。1分に1回程度の割合でランダムなデータが送られて来て、それに対する適切なデータを送り返さなければPS4はそのコントローラーからの入力を無視するようになっている。

しかし解析Wikiでは「どうやらChallenge-Responseらしい」程度の情報しか載っておらず、実際に何が行われているのかはわからなかった。何故仕組みが分かっていないのに現在(デバイスを用いるとはいえ)コンバータが存在するかと言えば、「どうやらこのChallenge-ResponseはRelayしてパスすることが出来る」ということが分かっているからだった。

http://www.psdevwiki.com/ps4/DS4-BT#0xF0

http://www.psdevwiki.com/ps4/DS4-BT#0xF1

What are these data?

確かに、ファームウェアも読めないような認証の仕組みを明らかにするのは不可能に思える。しかし、暗号についての幾つかの知見があれば自然な類推で仕組みを明らかにすることが出来る。ここでは僕がどのようにして仕組みを知ったかを順に書いていく。冗長かも知れないが僕の実験の楽しさ(と苦しさ)の片鱗を味わってもらいたい。

さて、最初の取っ掛かりはWikiにあるPacket 0x0d is padded with zeros, except bytes 58 and 60 (both are 0x01).だった。こういう風に書かれると何のことだか分からないが、58-60byteが0x010001になっていると言われると暗号徒であれば察するところがあると思う。そう、0x010001とはRSAのeとして広く用いられている数である。

Challenge-ResponseでRSAが関係すると言われればまず間違い無く署名である。Challengeデータをコントローラーの内部にある秘密鍵で署名してResponseとして返す。これなら筋が通る。

eがResponseの中に入っているからにはもう1つの秘密鍵であるnも入っているはずである。実際にデータを見ると幾つかの連続したデータ列と0-paddingがある。具体的には16byteのデータ列1つ、256byteのデータ列3つ、そしてeがResponseに含まれるデータだった。

長さから考えて256byteのデータのどれかがnである。今度はこれを特定しよう。

まず、何回かのChallenge-Responseを眺めると2つの256byteの列は固定だった。ランダムなデータは間違い無くResponseデータであるし、公開鍵は変化することが無いのでこの内の1つがnである。残る列が何かと言えばこれはnを署名した列だと推測できる(そうでなければ公開鍵ごと偽造すれば簡単に破ることが出来る)。

どちらがnだろうか。これを確かめるのは簡単で、素因数分解をしてみれば良い。nは非常に大きな素数p, qの積からなるため小さい素因数を持たない。実際、片方の列は10000までの素因数を持たないにも関わらず、Miller-Rabin素数判定によって合成数であることが分かった。極めてn的な数である。

n, e, Challengeを手に入れたが、ここからどうやってChallengeを署名しているか特定する必要があった。これは幸いにも一般に署名に使われるPKCS1 PSSだった。Challengeをsha256でハッシュ化した後適切なパディングを施してd乗 mod nをする。

実際に得られたn, e, Challenge, Responseを使ってPKCS1 PSSでverifyすると通った。めでたい。

ところでn, e, nの署名(と思われるデータ)はペアリングをやり直しても不変だった。これはnの署名がLink KeyなどのPS4-DS4間で共有されるデータに依存しないことを意味する。

この考察から、Replayしても認証は上手く動くのである(と思っていた)。

Challenge-Response over USB

しかしここからが大変だった。

普通、先述の認証はBluetooth通信で行われる。そこでBluetooth通信をMITMすることで認証をReplayしようと思っていた。 しかし実際に書くと分かるがBtstackを使って複数のコネクションを管理するのは極めて大変な作業である。加えて1つのBluetoothドングルではPS4とChallenge-Response Oracle用DS4と同時に繋ぐとパケットロスが生じる。ドングルを2つ用意するのも手だが、他の方法を考えた。

実は新しい基板のDS4はUSBでの通信に対応している。そしてその設定をするページには「USBケーブルを使って通信する場合マイクが使えなくなります」とと言った文言がある。これは実はとても示唆に富んでいる。何故なら、DS4において「音声信号は必ずBluetooth上でやり取りされる」からだ。これは@ka1l氏に基板の写真を見てもらった時に指摘されていて、つまりは物理的に不可能なようだ。

僕はこの時までずっとUSBで操作の入力だけやり取りして他の音声や認証情報はBluetoothを使うと信じていた。しかし、音声情報が使えないということは一切Bluetoothを使わない可能性が高いと考えられる。つまり、Challenge-ResponseをUSBで行うことが出来る可能性が高いと考えた。

実際、USBで通信する設定を有効にしたコントローラーのBluetooth通信のログを見ると一切パケットが流れていなかった。

幸いにもGET_REPORT/SET_REPORTはUSBのHIDにもある概念で(と言うよりBluetoothがUSBから輸入した?)、ReportIDなども一致した。少し逸れるがLinuxカーネルには何故かDS4用のドライバが入っており、これを無効にしなければcontrolでの通信が出来なかった(結局必要なかったがデバドラの仕組みを調べたりした)。

果たしてGET_REPORT/SET_REPORTの0xf0, 0xf1, 0xf2のデータを適切にUSBでやり取りすると、きちんと署名されたデータが返ってきた。勝利を確信した瞬間である。

What's wrong?

しかしここからも大変なのだ。

0xf0, 0xf1, 0xf2のデータ以外にもGET_REPORT/SET_REPORTには0x03, 0x04のデータがやり取りされている。PS4からDS4に32byteのデータを送信してDS4が40byte返す。これはランダムなデータで内容も分かっていない。全く重要で内容に見える。

http://www.psdevwiki.com/ps4/DS4-BT#0x03

http://www.psdevwiki.com/ps4/DS4-BT#0x04

ところがこのデータを返さなければChallenge-Response用の通信もロックされてタイムアウトしてしまう。しばらく考えたが結局何のデータなのか分からず、urandomで生成したデータを流したが(少なくともそれを流した瞬間には)問題無いように見えた。

何故このデータもreplayしないのかと思うだろう。それはDS4のUSB通信ではReportID 0x03が存在しないからだ。これに相当するデータがあるのかないのか、あるならどのIDでやり取りしているのか覗いてみたいがUSB Snifferは存外大変らしい(金で殴れば解決する)。

雑な0x03, 0x04のデータを流して今度こそ解決したかと思われた。認証はタイムアウトせず全てのデータが流れきった。しかし最後のデータが渡った瞬間に入力が無視された。明らかに認証でハネられている。

どうやら考え残しているところがあるようだ。

一番怪しいのは0x03, 0x04だが、他にも可能性はある。

コントローラーのBDADDRに偽装してみたがこれもダメだった。

認証時にLink Keyを用い無いと考えていたが、PKCS1 PSSはパディングに乱数値を入れるため、この箇所にLink Keyに依存した情報が入っているのかもしれない。そう考えて同じLink Keyを使っている2つのResponseに入っている乱数値を取り出してみたが異なる値だった。

どれか他のReport IDが0x03, 0x04の代わりをしているかと探ったがこれもダメだった。

かなり煮詰まっているので記事に書いた(記事に書くと何故か進捗が生まれるジンクス、内容を自分で整理出来るからだろうか)。

Demo

Responseを返し切るまでは入力が受理されるところ。

www.youtube.com