LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

add (2) -水平加算系-

はじめに

水平加算命令

  • 水平加算は、ベクトル内の全要素の和を取る命令
  • 入力は複数だけど、出力は単一の要素 (なので、厳密には SI M Dではない、と誰かが言っていた)
  • 水平加算系というだけあって、いくつかあるので、paddpaddladdvaddlvの4つの命令を紹介する
    • ちなみに、名前からして一見水平加算しそうなhadd命令はhalving addで、和を取ったあと右に1bitシフトする命令。つまり水平加算ではない。
  • 本当にadd命令多すぎである。

昨日示した`add`系の命令の内、水平加算系の命令だけ抜き出したサブセットの図

padd命令

https___qiita-image-store.s3.amazonaws.com_0_12162_fa0b9e39-a1cd-81ff-69c6-dda60f8b2a79.png

  • この図は4年前に使ったやつの再掲だけど、基本的に結果のn番目の要素には、入力レジスタの(n*2番目の要素+n*2+1番目の要素)が格納される。

  • intrinsic関数のシグネチャとしては引数は、2つのベクトルを取り、同じ型を返す。(ex: uint8x16_t vpaddq_u8(uint8x16_t a, uint8x16_t b))

  • 先の図はvpadd_u8(64bit幅の命令)を表した図だが、vpaddq_u8(128bit幅の命令)でも、1つ目のレジスタと2つ目のレジスタをあわせて256bit幅の仮想的なレジスタに対して処理が行われる。入力レジスタのn*2番目の要素とn*2+1番目の要素の和が、出力レジスタのn番目の要素として格納される

    • vpadd命令であれば入力は64bit幅のレジスタ2つで128bit幅。総和を計算するには4回の命令(演算)が必要になる。
    • vpaddq命令であれば同5回の命令(演算)が必要になる
  • SSEでは、一発で水平加算を求める命令があったが、NEONにはこのpadd命令しかなかった。 いままでは

addv命令

vaddv.png

  • NEONは、Arm v8の命令セットに含まれた、と何度も書いたけれど、実はその際、double演算を始めとする、Arm v7時代には存在しなかった命令たちが追加されている
  • 前述の図はvaddvq_u8で128bit幅のレジスタの、8bit整数型の各要素の和を1回で計算している1
  • addv命令もその一つで、こちらは、SSEの水平加算命令よろしく、一発で各要素ごとの和を求めてくれる
  • 戻り値は、各要素の型が使われる(vaddv_u8ならばuint8_t型、vaddv_s32ならint32_t型)

paddl命令

  • paddl命令では加算する際に各要素をbit拡張する。これにより演算結果がオーバーフローすることを防げる。
  • paddl命令は単一のレジスタしか受け付けない。
  • 隣り合う要素ごと加算し、結果を倍の要素に格納して返す。(ex: uint8x16_tを受けてuint16x8_t型を返す) vpaddl.png

addlv命令

  • この流れで行くとなんとなく分かると思うけれど、addv命令の結果を、オーバーフローを防ぐために要素の型より一段広い型で返す
    • ex: vaddlv_u8命令だと、各要素はuint8_tだが、処理結果はuint16_t型で返ってくる
    • こちらも、入力として64bit幅、128bit幅、どちらも受け取る

その他

  • vpadd_f16ならびにvpaddq_f16命令が存在するが、こいつらはArm v8.2拡張命令セットなので、実行時にCPUがサポートしているかチェックが必要

おわりに

  • 本当にadd命令多すぎである
  • 明日も私の担当の予定です

  1. 今気づいたんだけれど、4年前の記事は無駄にpadd命令を何回も計算してたから、addv命令でハミング距離を再計算したら結果が覆る可能性が微レ存? 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2