Ryuz's tech blog

FPGAなどの技術ブログ

OPS(Operations per second)とは何なのか?

はじめに

今日は OPS(Operations per second) って何だろうという個人的疑問からの駄文です。

スーパーコンピューターやパソコンなどの性能を示す指標として FLOPS(Floating-point Operations Per Second) は昔からよく使われていたかと思いますし、多くの場合それは倍精度浮動小数点演算の能力を示していたかと思います。 また演算能力に対するメモリ帯域の性能指標である B/F (Byte per Flop) を語る際のベースとしても FLOP は重要な指標でした。

一方で、OPS(Operations per second) という単位もあり、これも昔からあるのですが、一般には FLOPS ほどは見かけない指標で、私のような組み込み分野で、浮動小数点演算器を持たないマイコンを使う 人々が良く使うものだった気がします。整数演算のみで性能を語る際に、「このマイコンはXX OPS あるから、このオーディオコーデック移植できそうだな」とかそんな用途で使われることが多かった気がします。

FLOPSに関しては x86なパソコンを例にとっても 8087 をはじめ、486DXで標準搭載されて以降、当たり前に浮動小数点演算ができるので、スパコンからパソコンまで一貫して性能比較に使える標準的な指標として長らく君臨していたように思います。

一方で、深層学習(ディープラーニング)の出現によって、これらの用語が一般の人にも認知されるようになり、さらに使われ方も深層学習の実行時性能を測るのに適した形に大きく姿を変えたものが一般に広まってきているように思います。

深層学習の出現による使われ方の変化

まず FLOPS がいつのまにやら FP64(倍精度浮動小数点) 以外を指すケースの方が見かけることが多くなってきました。

FP32での FLOPS、BF16 での FLOPS、 はては FP8 とか FP4 とかいろいろ現れています。

特に、深層学習の推論で、FP64 などまず使われませんので、GPUやNPU などの性能で謡われる FLOPS は特に何も語られていなければ FP32以下を指すことが多くなった気がします。 当然ですが、FP64 の FLOPS 値と、FP8 の FLOPS値では、計算機の総能力としては全然違うものになります。

加えて、 深層学習の推論では、浮動小数点演算を使わずに整数演算だけでやってしまおうというものを増えています。そうすると FLOPS という単位が使えなくなります。そこで OPS(Operations per second) という単位が急速に市民権を得たように思います。

そして実際 INT64/32/16 などもありますが、量子化技術により INT8/4/3/2/1 などの小さな単位の整数や、果ては b1.58 や バイナリなども現れています。

当然ですが、 INT4 の OPS値と INT8 の OPS 値でも、計算機の総能力は全然違うものになります。

また、深層学習 では、演算の多くは行列乗算の為の積和演算です。これは乗算と加算の組み合わせですが、FLOPS や OPS に換算する場合はこれを個別にカウントします。FMA命令のような複合演算命令もちゃんと2演算として数えます。 まあ深層学習向けにはあまりないと思いますが、乗算命令がなくて加算だけ沢山出来るCPUでも OPS は OPS なので、このあたりも OPS や FLOPS の中身はよく理解してカタログスペックを読み解く必要はあると思います。

もう少し踏み込むと、積和演算時、しばし、乗算よりも和の部分で、Accumulation や Reduction 演算するときは一時的に大きな桁数が必要だったりもしますので、この辺りも少し注意点だったりします(とくに SIMDなど)。

どのように性能を読み解けばいいのか

ここで、「計算効率のいい学習モデルを作ろう」、「効率よく計算機に実装しよう」と言った時に、どのような計算機を調達してきて、どのようなモデルを開発して、どのように実装するのかと言うのは非常に重要です。

FP32 で計算しないと精度が出ないモデルを作ってしまったら、「INT8 で 何十TOPS もある NPU を用意したはずなのに、全然性能が出ない」なんてこともあり得るわけです。

また、逆もしかりで、INT4 で十分な精度が出るのに、INT8 しか計算できない計算機を持ってきた場合、下手すると、「演算器の 3/4 と、メモリバス幅の 1/2 は、調達時の無意味な部品コストと、運用時の無駄な電力消費でしかない」なんてことになりかねません。

ちなみに、乗算器は、私が調べた範囲では深層学習に使う範囲程度なら、おむね桁数の二乗に比例する程度に思っておいて大きな誤差は無さそうです(筆算で掛け算を計算する回路を想像してみてください)。つまり桁数が倍になると4倍程度のトランジスタを使ってしまう可能性があるわけです。

ここでまた、別の罠もあって、深層学習においては、この層は BF16 が欲しいが、この層は INT4 でも十分、などといった事が起こったりもします。

このようなケースでは、「BF16 も計算できるが、モードを切り替えると4倍量の INT4 も計算できる計算機」みたいなものを良く調べて見つけてくる必要があります。

そのうえで、「私の作ったモデルは、この計算機を使えば XXX token/s 出せそうだ」とか見積もっていかないといけないことになるわけです。

逆もしかりで、計算機が先に指定されるケースもあり、「この計算機で要件の XX fps を出すには、INT8 なら XX GOPS、 INT4 なら XXX GOPS まで行ける」とか見定めて、パラメータ数や層数を逆算して学習モデルを作らないといけないケースもあるでしょう。

ようするに、ちゃんと性能を読まないと、モデル設計前に見積もれないということなのですが、正しく見積もらずに設計してる事例って結構あるあるではないかとも思ってしまうわけです。

オレオレ型とオレオレ演算でも OPS を定義できるのでは?

ここで再び OPS の話に戻しますが、実はこれ FLOPS と違って かなりあいまいな表現じゃないかと個人的に思っています。

FLOPS における IEEE 754 のようなある程度同一の基準で比較できるものと比べて、「演算(Operation)」という、とても曖昧なものを扱っているため、物は言いよう といった事も出来てしまします。

FPGA などを活用すれば、オレオレ型おれおれ演算 なんかを定義することが可能ですが、それに対しても、「〇〇 OPS です」と言えちゃうわけです。

ここで仮に オレオレ型として MY3 という 3bit の型を作って、思いつくまま適当に「-1.1, 0, +0.1, +0.7, +1.2, 1.5+0.3i, +π, +∞」 の8種類の値を表します、みたいな好き勝手な定義をしても何も構わないわけです。MY3 と MY3 の間に四則演算以外の演算子を定義することも自由です。

ここで FPGA の 6入力 LUT を考えると、 MY3 を 2つ入力して、1つの MY3 を出力する回路は LUT が 3 個あればどんな演算でも行えますし、一部を複合演算だと言い張ることも自由です。

要するに OPS値盛り放題 と言えなくもありません(笑)。

まあ、冗談は話半分としても、作ろうとしている学習モデルに対して、もっとも最適な型を独自定義 して、それに対する もっとも効率のいい演算を独自定義 して、「それに対するモデルの精度と演算量を OPS値としてカタログスペックにする」などという事も理屈上は可能なわけです。

現在、$100 程度の FPGA でも LUT は 数十k 個は入る時代になっています。 LUT単位の演算であれば 500MHz ぐらいで動かすことも可能ですので、「数万円で 10TOPS のNPUを自作したぞ~」などという事も出来ちゃうという事です。

おわりに

先日ふと、RISC-V などのカスタム命令で馬鹿みたいにOPS値だけ謡えるSIMD命令って追加できないかなみたいなこと考えておりましたら、「そういえば OPS って単位謎だよな? ハックすれば幾らでも盛れるんじゃないのか?」みたいな疑問を抱いて、ちょっとした駄文にしてみました。

もっとも多くの場合は、既存の GPGPU などで使える型と演算をベースに正しく議論されるのだとは思います。

一方で、「今はまだ存在しないデータ型や演算子を定義して、もっと効率よく高精度な学習モデルが作れないか?」というのもまた一つ興味深いテーマな気はしております。

そういったものを、手軽に実験してみるのにもまた FPGA は面白いデバイスなのではないでしょうか?

また普段 FPGA お使いの方も、DSP の数だけで深層学習の性能を決めてしまわずに、オレオレモデルに手を出すことで、もっとLUTでしかできない特殊な演算モデルを考えるのも楽しいと思います。