中間考察 - なぜFury/Fury Xの性能が伸びないのか?

さて、ここまでの結果を基に、筆者なりにR9 Fury/Fury Xの性能が伸びない理由を考えてみたい。理由は大きく分けて2つある。

1つ目は、HBMのLatencyがGDDR5よりだいぶ大きい(20~50cycleほど余分に掛かる)ことだ。ここでいうLatencyは、メモリコントローラがリクエストを出してから、それをメモリチップが受け取って結果を返すまでの時間である。

DDR3などで使われるCL9-9-9-24といった表記に従えば、最初の9にあたる部分がGDDR5よりだいぶ大きい。この結果、煩雑にメモリアクセスの要求が発生する場合には、どんどんLatencyが増えることになる。

2つ目はMemory Granularityの問題である。Granularityは日本語だと「粒度」と訳されるが、要するに1回でアクセスするデータの単位である。例えばDDR3チップの場合、チップそのものは8bit幅でも、Prefetchは8nなので、Granularityは8×8=64bit=8Bytesとなる。DIMMの場合はこれが8個載って64bit幅になっているので、1回メモリアクセスの要求を行うと、連続した64Bytes(8Bytes×8)が出てくる形だ。

さて、GDDDR5の場合、チップのバス幅は32bit、Prefetchは8nなのでGranularityは32Bytesとなる。実際にはR9 290Xの場合、2chのGDDR5が1つのメモリコントローラから出ているから、Granularityは64Bytesとなる。

対してHBMの場合、Prefetchは2nなのだがバス幅が1024bitもあるため、Granularityは256Bytesとなる。つまり1回アクセス要求を出すと、256Bytes分がゴソっと転送されてくることになるのだ。これは小さい量のメモリを細かくアクセスするようなケースでは著しく不利になる。

この2つが主要因であるが、これに関しては回避策がある。それは大容量のキャッシュを挟むことだ。例えば2つ目の要因について、メモリアドレスA・B・Cに連続してアクセスを行うようなケースを想定した場合、それぞれが64Bytesの大きさならGDDR5だと1回ずつアクセスすれば済み、その結果はキャッシュに格納される。

ところがHBMだと、A・B・Cをそれぞれ先頭とする256Bytesの塊がやってくるので、キャッシュには本来必要となる量の4倍のデータがいきなり入る形になる。この転送時間が無駄という話もさることながら、こうした結果として早いタイミングでキャッシュが使われてしまい、無駄にキャッシュミスが多発しやすくなる。

ではどうするか? といえば、その分キャッシュサイズを大きくすれば良い。端的にいえば、L2を4MB(Render Outputあたり256KB)まで増やしていれば、理論上キャッシュミスの比率はGDDR5の場合と同等に収められる。

ところが今回、Fijiコアではこうした対策が一切採用されていない。4MBとはいわないまでも2MBくらいのL3をHBMとの間に挟んでいれば、恐らくもう少しマシな結果になったと思うのだが、これは当然ダイサイズが増えることになるわけで、それを嫌った結果であろう。

もっと根本的なことをいえば、現在のGCNのアーキテクチャがそもそも64Bytes程度のGranularityを前提に設計されているので、HBM世代ではこのGranularityをもっと大きくすれば効率はさらに改善されるはずだ。しかし、これは根本的な設計変更になってしまうので、Fijiの世代では手をつけなかったのだろう。

こう考えると、メモリアクセスの単位が小さくなりがちな1280×720pixelあたりでは性能が低く、よりアクセスの単位が大きくなりそうな4Kの解像度でどんどん効率がよくなるのは理にかなった振る舞いである。あるいは倍精度の浮動小数点演算を行った場合のみ急に性能が落ちるのも、やはりこの延長で考えることができる。

では、この問題がいつ解決されるか? であるが、L2の大容量化、あるいはL3の搭載については、ダイサイズの肥大化を防ぎたいという経済的な要因が解決されればよいので、プロセスを微細化すれば比較的簡単に実装できるだろう。

一方、根本的な解決には、HBMに合わせてGCNの作り直しが必要だが、これは早くても14/16nmプロセスを採用した世代になるのではないかと思われる。