Ryuz's tech blog

FPGAなどの技術ブログ

SIMD/SIMTとMIMDと

はじめに

先日Rustのイテレータでの処理順序関連してこんな記事を書きました。

加えて最近少し Elixir を調べていて、Enum, Stream, Flow などの処理を興味深く見ております。

そこで、ALU(arithmetic Logic Unit)視点から見た2種類の計算機のデータ処理パターンを再考しておきたいと思います。 今回、少し大胆ですが、SIMD(Single Instruction Multiple Data)とSIMT(Single Instruction Multiple Thread)は複数のALUが同時に同じ演算を実装するという点で同じカテゴリとし、別々の計算を行うものとしてMIMD(Multiple Instruction Multiple Data)を置いておきます。

SIMD/SIMT は複数のALUに対して命令デコーダが共通ですので少ないハードウェアリソースで多くの演算が行えるのが特徴で、逆にMIMDは複数のALUに対して個別の命令デコーダがあるのでハードウェアリソースを多く利用してしまう反面自由度が高いのが特徴です。

考察

2つのモデルを置いてみる

簡単のため、次の2つを考えます。

  • 4並列のSIMD演算器を備えた1コアのプロセッサ(SIMD)
  • 1命令しか実行できないコアが4つあるマルチコアのプロセッサ(MIMD)

この時どちらも ALU は4個あります。

ここでそれぞれに例えば画像なり行列なり何でもいいのですが 1024x1024 程度の2次元のデータを処理させることを考えてみます。

それぞれ出力するデータに対応する計算の割り当てとして、計算効率を出そうとすると

  • SIMD/SIMT的なデータの割り当て
    • ALU0 : データ0, 4, 8, 12...
    • ALU1 : データ1, 5, 9, 13...
    • ALU2 : データ2, 6, 10, 14...
    • ALU3 : データ3, 7, 11, 15...
  • MIMD的なデータの割り当て
    • ALU0 : データ0, 1, 2, 3, 4...
    • ALU1 : データ1024, 1025, 1026, 1027...
    • ALU2 : データ2048, 2049, 2050, 2051...
    • ALU3 : データ3072, 3073, 3074, 3075...

のようなイメージになりがちです。 前者は SIMD命令を使ったプログラミングやCUDAなどのプログラミングでやりがちな記述であり、後者はマルチプロセッサ環境で OpenMP などで #pragma omp parallel for などを使った際にありがちなパターンかと思います。

SIMD/SIMTに対する考察

SIMD/SIMTはご承知の通り連続するアドレスに対するロードストアが得意です。外部の SDRAM にとっても連続したアドレッシングしか発生しませんので、システム全体として見てもなかなか効率が良いです。

もしSIMD計算機にMIMD的なアドレッシングをさせると、途端に性能が落ちます。例えば「画像を回転させる」ようなタスクの場合、load 時のアドレスは4つバラバラになりますが、一つでもキャッシュミスするとデータを4個揃えて演算するために4つともの演算器がストールしてしまいます。

MIMDに対する考察

こちらは独立したコアが4つあるわけなので、なるべくお互いが干渉しない等に個別に演算を進めるのが理想的となるため、後者のようなメモリ割り当てとなっています。もしSIMDのような演算の割り当てをしてしまうと、各コアが保有しているL1キャッシュに使わないデータまで取り込むことになってしまい性能が出ません。 また、SIMDで効率が良いアクセスパターンは、外部 SDRAM にとってはややランダムなアクセスになるための若干の不利が生じます。

一方で、先に上げた画像を回転させるようなタスクでは、各コアがお互いを待ち合わせることなく担当個所を全力で処理できるため効率が良いことになってきます。

そして何よりMIMDの場合、あるコアはExcel、あるコアは Chrome、あるコアは音楽再生、のように、全く違うアプリケーションが実行できる利点があります。サーバーとして多くのユーザーのリクエストを非同期に処理するなどにも向いています。

実際には

実際には多くのケースで SIMD命令を備えたコアが複数ある というプロセッサを使うことが多くなってるのではないかと思います。 この時、両者のハイブリッド的なアクセスとなるように工夫しながらプログラムを書くわけですが、両者の特性を知っておくことはとても大事ですね。

ここでFPGAを考えてみる

FPGA の場合、たとえば乗算器のような、ALUを構成するのが便利なハードマクロが多数内蔵されていることが多いいです。この多数用意された並列演算器に、DMA などを構成してメモリや外部デバイスなどからデータを送り込んで演算させるというのが基本ですが、この時 SIMD的なアドレッシングもMIMD的なアドレッシングも どちらも出来てしまう というのがFPGAの楽しいところです。

普通にプロセッサを買ってきてプログラミングをする場合、自分の使っているプロセッサで性能が出るように書くしかないわけですが、FPGAの場合どのALUにどのように処理を割り当てるかの自由度がかなりプログラマ側にあります。加えて周波数は低めですので、例えば 1G pixel/sec の帯域で画像処理するような場合嫌でも並列演算することになります。

多くのケースではSIMD的に並列にするのですが、稀に画像にアフィン変換を掛けるような用途だと部分的に MIMD 的にしたりすることもあります。その際のメモリ効率もアクセスパターンやキャッシュの量や仕組みなど工夫の余地は多いように思います。

終わりに

計算機のデータプロセッシングとしての利用側面を考えた場合、沢山の演算器を並べてそこに効率よくデータを流し込む機構を考える ことに他ならないのですが、市販プロセッサでは「SIMDをMIMDにして使う」などと言う自由度はソフトウェアプログラマにはありません。一方でFPGAにはその余地がソフトウェアプログラマに解放されている点でとても面白いと思うわけです。