Apple Engine

Apple, iPhone, iOS, その周辺のことについて

ARKit や SceneKit で使用している simd について - 概要編 -

ARKit や SceneKit で使用している Accelerate フレームワークの simd ライブラリについて書いてみる。

元々は Accelerate フレームワークで vImage、vDSP や BLAS、LAPACK などを使用する際に、ハードウェアに近い部分での計算を行うために追加されたものだと予想される。

 

iOS 11 の SceneKit から SIMD 命令が使用できるようになっており、
以前の Apple のサンプルではサンプルのソースに simd で計算するためのカスタム関数使っていたが、 直接値を渡すことのできるプロパティが追加された。

ちなみに Apple の公式ドキュメントでは simd 単体と Accelerate のフレームワークとの中に simd のドキュメントがあるので注意。
特に simd 単体の方は説明が「No overview available」になっており、何も書かれていない。

 

ざっくり説明

以前もこのブログで記事にしているが、雑に説明すると複数のデータ(レーン)を1回の処理で実行する並列化の形。

通常は SISD の形をとっており変数単位で計算を行うが、
SIMD は画像のように [X,Y,Z,W] の計算を1度で行う事ができ高速かつ記述が簡単である。

f:id:x67x6fx74x6f:20180515183839p:plain

 

コードではこの様な感じ。SCNVector 系では計算ができずビルドエラーになる。

var f1 = simd_float3(1,1,1)
var f2 = simd_float3(2,2,2)

var calc1 = f2 + f1 // 結果:float3(3.0, 3.0, 3.0)

var sv3_1 = SCNVector3(1,1,1)
var sv3_2 = SCNVector3(2,2,2)

var calc0 = sv3_2 + sv3_1 // SCNVector3 では計算できないためビルドエラー

 

GPU においては SIMT の実装もされており、
雑に説明すると1レーンに X だけの要素を設定し並列化する。
(厳密に言うとこれは SIMT の説明ではないので注意)

近年の SIMD では4つの値を固定するため、状況によっては Z や W などを使用しない場合があり無駄ができるが、[X,X,X,X], [Y,Y,Y,Y], [Z,Z,Z,Z] などの様に格納されていれば演算時に無駄が少なくなる。

 

注意点

simd の処理は SCNVector 系と同様に行列が列ベクトルで設定され処理されるので注意。
数学で使用する行列の表記と、simd でデータを入れるは異なる場合がある。

 

非公式情報

非公式情報だが iOS 11 Beta 時の SceneKit での simd 関連のヘッダーファイルには、 値が3つ以下のものは GPU、4つのものは CPU (多分 NEON) を使用していると書かれていた。
現在ではその記述がないため夢だったかも。

とはいえ、もし GPU 処理が SIMT で 3-way であるのなら、2つや3つのパラメーターの処理は効率的だろうし、状況によっては他のプロセッサを使用しているのかもしれない。

 

今後の Swift での SIMD

現時点では SIMD は simd.h から持ってきているが今後は Swift 内で実装される可能性がある。

github.com

 

simd ヘッダーファイルで定義されている変数とパラメーター

使用できる変数とパラメーターの数を列挙してみた。
× は過去にあったもの。

1 2 3 4 8 16 32 64
simd_char × × × × × × ×
simd_uchar × × × × × × ×
simd_short × × × × × ×
simd_ushort × × × × × ×
simd_int × ×
simd_uint × ×
simd_float × ×
simd_long × × × ×
simd_ulong × × × ×
simd_double ×

 

SceneKit、ARKit でパラメーターとして渡せるのは float3、float4 や float4x4 で 4 つまで。 正の整数のみ Unsigned はマイナスの値が使用できなくなるため使用機会は少ない。

 

simd のプレフィックスがつく変数は、Swift の場合は全て Type Alias あり、構造体である。

最初の文字が大文字のものは Swift のライブラリ Math で定義されており、小文字のものは simd の vector_types で定義されている。
以下で型をまとめており、列挙しているためわかりづらいが、大文字で定義されている変数はパラメーターが1つしかないものである。

Collection、CustomDebugStringConvertible、Equatable、ExpressibleByArrayLiteral のプロトコルに適応しているため、Array や Dictionary、デバグ用の文字出力や同じ型の simd 同士の演算を行うことができる。

 

simd での定義 元の型
simd_uchar1 Int8
simd_short1 Int16
simd_ushort1 UInt16
simd_int1 Int32
simd_int2 int2
simd_int3 int3
simd_int4 int4
simd_uint1 UInt32
simd_uint2 uint2
simd_uint3 uint3
simd_uint4 uint4
simd_float1 Float
simd_float2 float2
simd_float3 float3
simd_float4 float4
simd_long1 Int
simd_ulong1 UInt
simd_double1 Double
simd_double2 double2
simd_double3 double3
simd_double4 double4

 

過去に C/ObjectiveーC や OpenCL 用に vector というプレフィックスがあったが廃止されている。
以下のものは現状使用できるが廃止されるため、今から使うことはないだろう。

  • vector_int2 = simd_int2
  • vector_int3 = simd_int3
  • vector_int4 = simd_int4
  • vector_uint2 = simd_uint2
  • vector_uint3 = simd_uint3
  • vector_uint4 = simd_uint4
  • vector_float2 = simd_float2
  • vector_float3 = simd_float3
  • vector_float4 = simd_float4
  • vector_long1 = simd_long1
  • vector_ulong1 = simd_ulong1
  • vector_double2 = simd_double2
  • vector_double3 = simd_double3
  • vector_double4 = simd_double4

 

また、simd_〜 は、C++ の simd::〜 に対応しており、 simd_float4、vector_float4、simd::float4 は同じものと考えられる。

 

行列

simd の types に定義されているのは以下のもの。 simd_float2x2 は simd_float2 が2つ、simd_float3x2 は simd_float3 が2つ定義された行列の型(構造体)。
上記の型と同様のプロトコルが設定されている。

  • simd_float2x2
  • simd_float3x2
  • simd_float4x2
  • simd_float2x3
  • simd_float3x3
  • simd_float4x3
  • simd_float2x4
  • simd_float3x4
  • simd_float4x4
  • simd_double2x2
  • simd_double3x2
  • simd_double4x2
  • simd_double2x3
  • simd_double3x3
  • simd_double4x3
  • simd_double2x4
  • simd_double3x4
  • simd_double4x4

 

simd のプレフィックスを抜いた float4x4 などが存在するが、全て simd のプレフィックスが付いたものの Type Alias である。

以前にあった matrix や vector のプレフィックスがつくものは simd の Type Alias になっており相互的使用が可能である。

 

types にはクォータニオンを使用する用途で simd_quatf、simd_quatd が定義されているが、この説明は今後行う。

 

SceneKit のデータ型と simd との互換性

SceneKit のデータ型は以下の4つで、SCNQuaternion は中身は SCNVector4。

SCNVector 系と simd との互換性がある。

SceneKit での型 simd での型
SCNVector3 float3
double3
simd_float3
simd_double3
SCNVector4,
SCNQuaternion
float4
double4
simd_float4
simd_double4
SCNMatrix4 float4x4
double4x4
simd_float4x4
simd_double4x4

 

Swift では以下のように書くが、Objective-C の場合は呼び出すメソッドが Swift と異なるので注意。

let s3 = simd_float3()
let v3 = SCNVector3(s3)

let v4 = SCNMatrix4()
let s4 = double4x4(v4)

 

SceneKit で増えたプロパティ

SceneKit 処理で simd のものを増やしたということは、
simd での処理の方が速いという事になるだろうし、実際に早く処理ができる。
以下、使用できるメソッド。(複数パラメーターがあるものは省略)

  • simdTransform
  • simdPosition
  • simdRotation
  • simdEulerAngles
  • simdOrientation
  • simdScale
  • simdPivot
  • simdRotate
  • simdLocalTranslate
  • simdLocalRotate
  • simdLook
  • simdLocalRight
  • simdLocalUp
  • simdLocalFront
  • simdWorldRight
  • simdWorldUp
  • simdWorldFront
  • simdWorldTransform
  • simdWorldPosition
  • simdConvertPosition
  • simdConvertTransform
  • simdConvertVector

 

まとめ

ARKit、SceneKit で使用することで処理が速くなり、端末の負荷が減ると思われるので、積極的に使用するべきだろう。
また、計算を頻繁に行うのなら SCNVector 系よりコードが短くなる場合がある。

次回、simd で使用する演算や演算用の関数を調べていきたいと思う。