SlideShare une entreprise Scribd logo
1  sur  114
Télécharger pour lire hors ligne
ARM CPUにおけるSIMDを用いた
高速計算入門
2021年4月28日
株式会社フィックスターズ
今泉 良紀
Copyright © Fixstars Corporation. Some rights reserved.
もくじ
0. はじめに
1. SIMDとはなにか
• フリンの分類
• SIMD演算器とSIMDレジスタ
• SIMD命令の使い方
• SIMDプログラミングの特徴
2. ARMのSIMD(NEON)について
• ARMとは
• ARM NEONとは
• NEONの特徴
• NEONのintrinsicの読み方
• NEONのintrinsicの探し方
3. ケーススタディ
• ループの入れ替え
• ループアンロールとpeel loop
• 計算のSIMD化
• ロード/ストアとインタリーブ
4. まとめ
5. 告知
1
Copyright © Fixstars Corporation. Some rights reserved.
はじめに
• 大前提: ソフトウェアが高速に動作するとうれしい
e.g.) 同じ時間で処理できるデータ量が増える
• 機械学習などで現実的な時間でより良い成果を得られる
e.g.) 同じ処理を早く終わらせられるのでランニングコストが下がる
• 電気代とかクラウドコンピューティングのインスタンス代とか
e.g.) 意思決定のためのシミュレーションが早く終わると
その分早く意思決定できる
e.g.) 株取引で他者より速く売買できればより儲けやすくなる
• SIMD演算を用いることでソフトウェアを高速化できる
• ではそのSIMD演算とはどのようなものなのか、というのが今日の話
2
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>フリンの分類
単一命令(SI) 複数命令(MI)
単
一
デ
ー
タ
(SD)
SISD(Single Instruction Single Data)
命令もデータも単一(並列性なし)
古いシングルコア・シングルプロセッサのマシン
MISD(Multiple Instruction Single Data)
1つのデータに対して複数の命令列
高速化の文脈ではあまり語られない
複
数
デ
ー
タ
(MD)
SIMD(Single Instruction Multiple Data)
複数のデータに対して単一の命令
SIMD演算器やベクトル計算機など
フリンの分類においてはGPUもここに属する
MIMD(Multiple Instruction Multiple Data)
複数のデータに対して複数の命令
マルチコアプロセッサなど
現代の並列計算機は大体MIMD
3
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>フリンの分類
4
• フリンという人が並列処理や並列計算機について4種類に分類した
• モデル: 計算機は命令列とデータ列から命令とデータを取ってきて
命令でデータを処理する
• 計算機が同時に扱う命令は単一か複数か
• 計算機が同時に扱うデータは単一か複数か
• SIMD(Single Instruction Multiple Data)
• 「データ処理のやり方」としてのSIMD
• 大量のデータを一様に処理→データ並列に使う
• 画像や音声などのマルチメディア処理において頻出
• タスク並列には使えない
• 「ハードウェアの分類」としてのSIMD
• データ並列を高速に行うための計算機
• CPUのSIMD演算器やGPU、DSPなど
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD演算器とSIMDレジスタ
• 現代のCPUの多くがSIMD演算器とSIMDレジスタを持っている
• SIMD演算器
• 通常の処理(スカラー演算)を行うための演算器とは別の演算器
• SIMD命令を実行する
• 入出力はSIMDレジスタに対して行う
cf.) スカラー演算器は非SIMDレジスタに対して入出力を行う
• SIMDレジスタ
• 通常のレジスタよりもbit幅の広いレジスタ
e.g.)64bitレジスタのCPUに対して128bitのSIMDレジスタ
• SIMD演算用のレジスタで、複数のデータを1つのレジスタに載せる
e.g.)128bitのレジスタに32bitデータを4つ載せる
→SIMDレジスタ上の複数のデータをSIMD演算器で1命令で
処理するのでスカラー演算器を使うより高速に処理できる
5
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD演算器とSIMD命令
• スカラー演算器とSIMD演算器のイメージ
• スカラー演算器
6
A B
R
F
R
F
• SIMD演算器
A A
A
A B B B B
R R R
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD命令の使い方
• プログラムからSIMD命令を呼び出す方法は大きく5つある
1. アセンブリでSIMD命令を直接呼び出す
2. intrinsicを使う
3. コンパイラ依存のディレクティブを用いる
4. コンパイラのオプティマイザに頼る
5. (SIMDを適用済みのライブラリを使う)
• 今日の本題は2
• 先に他のものから簡単に紹介
7
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD命令の使い方
5. (SIMDを適用済みのライブラリを使う)
• みなさんが日頃使っているライブラリが内部でSIMD命令を
呼び出している
• OpenCVやnumpyなど
• メリット
• なにも気にしなくても速い実装を使うだけでよい
• デメリット
• やりたい処理によってはライブラリだけでは実現できないこともある
• やりたい処理に対して必ずしもライブラリが最速であるとは限らない
• 修正が難しい場合もある
8
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD命令の使い方
4. コンパイラのオプティマイザに頼る
• 普通に書いたループ文が勝手にSIMD化されることを祈る
• メリット
• 何もしなくても勝手に速くなる
• コードも普通にループ文書けばそれで良い
• 可搬性が高い
• 速くなるかどうかはさておき他の環境でも動く
• デメリット
• ちょっと複雑なコードになると大体最適化に失敗する
• 場合によっては簡単なコードでもうまく最適化してくれないことも
• コンパイラの気持ちを察しながらコードを書く必要がある
→可読性が下がりがち
• SIMD化に成功したかどうかはコンパイル結果のアセンブリを眺めたり
する必要がある 9
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD命令の使い方
3. コンパイラ依存のディレクティブを用いる
• ループ文の前に「ループ文の中身をSIMDで計算せよ」という指令を書く
e.g.) OpenMPの #pragma omp simd など
• メリット
• SIMD化の方法についてのヒントを書けるのでコンパイラが
最適化する際に参考にできる
• 可搬性が高い
• ディレクティブなので使えない環境では無視される
• デメリット
• ディレクティブを正しく書く程度の労力は必要
• どのように最適化されるかはコンパイラに依存
• コンパイラの気持ちを汲んでやる必要がある
• 現時点ではそもそも実装が少ないし最適化も貧弱
• 最適化がうまくいかないこともあるし成功したかどうかの確認が面倒な
場合もある(コンパイラに依存)
10
Copyright © Fixstars Corporation. Some rights reserved.
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD命令の使い方
1. アセンブリでSIMD命令を直接呼び出す
• 書いたSIMD命令が呼び出される
• メリット
• 書いた通りに動くことが期待できる
• デメリット
• レジスタの管理を自力でやらねばならない
• 変数と違って名前がない(番号で指定)
• それまでのコードで使っていたら他の番号のレジスタを指定する必要がある
• レジスタの数には上限がある
• レジスタが足りなくなったら一時的にメモリに書き出す必要がある
• 「SIMDのプログラム」を書く必要はある(後述)
• 読み書きに慣れが必要
• 可搬性が低い
• 異なる命令セットや拡張命令を持たないCPUで動作しなくなる 11
SIMDとはなにか>SIMD命令の使い方
2. intrinsicを使う(本題)
• intrinsic
• 特定の命令列に変換される組み込み(builtin)関数
• SIMD命令に変換されるintrinsicがあるのでこれを使う
• メリット
• ある程度書いた通りに動くことが期待できる
• レジスタの管理をしなくて良い
• 変数と関数によるプログラミングが可能
• デメリット
• アセンブリほどではないにせよ読み書きに慣れが必要
• 「SIMDのプログラム」を書く必要はある
• 普通にループ文で配列の各要素毎に処理するコードそのままではない
• マスクによる分岐や端数処理など(後述)を考慮する必要がある
• 可搬性が低い 12
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMD命令の使い方
• プログラムからSIMD命令を呼び出す方法は大きく5つある
1. アセンブリでSIMD命令を直接呼び出す
2. intrinsicを使う
3. コンパイラ依存のディレクティブを用いる
4. コンパイラのオプティマイザに頼る
5. (SIMDを適用済みのライブラリを使う)
• 上のものほど
• コーディングが大変
• 最終的な実行コードの内容をより厳密に記述できる
• 正しく使えば限界まで高速化することができる
• 可読性・保守性・可搬性が低くなる
• もちろんアセンブリやintrinsicも慣れれば読める
13
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMDプログラミングの特徴
• 分岐処理は(分岐としては)書けない
• 「複数のデータに対してまとめて同じ処理をする」ので、
データの中身に応じて処理を分岐することが難しい
• 基本的にはすべてのパターンの計算を行った後、条件によって
レーン毎に2つのSIMDレジスタのうちいずれかの結果を取り出す、
という操作を行う(マスク処理)
• 分岐が多い場合すべてのパターンを計算するのであまり速くならない
14
B
A
Cond
Result
B
A
Result
Select by
Cond
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMDプログラミングの特徴
• 端数処理
• SIMDは複数(N)要素を同時に計算する
e.g.) 32bit4つをまとめて計算(N=4)
• 処理対象の数がNで割り切れないときどうするか
e.g.) 7要素の配列に対して処理
• データをSIMDレジスタに読み込むのもN個ずつなので
素直にやるとアクセス違反を起こす
15
0
1
2
3
4
5
6
0
1
2
3
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMDプログラミングの特徴
• 端数処理
• SIMDは複数(N)要素を同時に計算する
e.g.) 32bit4つをまとめて計算(N=4)
• 処理対象の数がNで割り切れないときどうするか
e.g.) 7要素の配列に対して処理
• データをSIMDレジスタに読み込むのもN個ずつなので
素直にやるとアクセス違反を起こす
→Nで割ったあまり(端数部)については何かしら対処が必要
16
0
1
2
3
4
5
6
0
1
2
3
ここでアクセス違反
Copyright © Fixstars Corporation. Some rights reserved.
SIMDとはなにか>SIMDプログラミングの特徴
• 端数処理
• SIMDは複数(N)要素を同時に計算する
e.g.) 32bit4つをまとめて計算(N=4)
• 処理対象の数がNで割り切れないときどうするか
e.g.) 7要素の配列に対して処理
• データをSIMDレジスタに読み込むのもN個ずつなので
素直にやるとアクセス違反を起こす
→Nで割ったあまり(端数部)については何かしら対処が必要
e.g.)スカラー処理する(peel loop)
• 先頭に入れたり末尾に入れたりする
e.g.)最後だけ開始位置を戻す
• この場合 3,4,5,6 の4要素で処理(3は重複して処理される)
• 1024bit SIMDのようなbit幅の大きいものでよく使う
e.g.)マスク読み書きで3要素だけSIMDで処理
• ARMのSIMDにはマスク読み書きが無いので今日は触れない 17
0
1
2
3
4
5
6
普通にループ文で
1個ずつ処理
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>ARMとは
• Arm
• CPUなどの設計をしているイギリスの会社
• 命令セットアーキテクチャ(ISA)からマイクロアーキテクチャまで
• 設計したアーキテクチャ(の情報)をライセンス販売している
• 買った企業がそれぞれ製造する
• Arm社自身がチップの製造をすることはない
• 近年ARMアーキテクチャのCPUが幅広い環境で採用されている
• 組み込み機器
• 元々低消費電力などを売りにしていたので主戦場
• スマートフォン
• ほぼ寡占状態 iPhoneも殆どのAndroidもARM
• PC(Apple M1、Microsoft SQ2など)
• スパコン(富士通 A64FX)
18
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>ARM NEONとは
• NEON: ARMv7時代のSIMD拡張命令
• 拡張命令なので使えるかどうかは確認する必要があった
• SIMD演算器を載せていないCPUで実行するとエラーになってしまう
• 64bit/128bit SIMDレジスタを扱う
• ARMv8からはSIMD命令が基本命令セットに入った
• つまりARMv8CPUであればSIMD命令が使える
• v7時代のNEONと概ね同じ命令セット
• intrinsicも基本的にはそのまま使える
• この際NEONという呼び名ではなくなっている
• が、「ARMのSIMD」という呼称はややこしいので
ここではARMv8のSIMD命令もNEONと呼ぶことにする
• ARMv8のSIMDでは128bit SIMDレジスタを32本使える
• 64bitでもこのレジスタを1本使うので注意(64本扱いにはならない)
19
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>NEONの特徴
• デインタリーブしながらレジスタロードする/
インタリーブしながらメモリストアする命令がある
• 画像処理で非常に便利
20
R0 G0 B0 R1 G1 B1 R2 G2 B2 R3 G3 B3 R4 G4 B4 R5 G5 B5 R6 G6 B6 R7 G7 B7
R0 R1 R2 R3 R4 R5 R6 R7
G0 G1 G2 G3 G4 G5 G6 G7
B0 B1 B2 B3 B4 B5 B6 B7
R0 G0 B0 R1 G1 B1 R2 G2 B2 R3 G3 B3 R4 G4 B4 R5 G5 B5 R6 G6 B6 R7 G7 B7
vld3_u8
vst3_u8
メモリ
SIMDレジスタ
メモリ
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>NEONの特徴
• SIMDレジスタ用の変数の型がしっかりついているのでintrinsicが
読み書きしやすい
e.g.) uint8x8_t(64bit), float32x4_t(128bit)など
• x86のSIMD(SSEやAVXなど)は整数のSIMDレジスタ型がすべて同じ型
• プログラムを読むのが大変
• 同じSIMDレジスタ変数に対してuint16とuint8向けの操作ができてしまう
• うっかり間違えたとしてもそのままコンパイルされる
• NEONのSIMDレジスタ型は以下の形式
• 𝑇𝑀x𝑁_t
• 𝑇: int, uint, floatなど
• 𝑀: 各要素のサイズ 8, 16, 32, 64など(𝑇によっては指定できない物も)
• 𝑁: レーン数 64/𝑀 または 128/𝑀
e.g.) 倍精度(64bit)浮動小数点数(float)2個は float64x2_t
21
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>NEONの特徴
• SIMDレジスタ用の変数の型がしっかりついているのでintrinsicが
読み書きしやすい
• 複数のSIMDレジスタ型を束ねた型がある
• 𝑇𝑀x𝑁x𝐶_t
• 𝐶: 2, 3, 4
e.g.) uint8x8x3_t (64bitレジスタ3本)
• struct uint8x8x3_t{
uint8x8_t val[3];
};
• vld3_u8の戻り値型がuint8x8x3_t
22
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>intrinsicの読み方
• NEONのintrinsicは基本的に以下の形式
• v𝐼𝑄_𝑇
• 𝐼: 命令 足し算ならadd、引き算ならsubなど
• 𝑄: 64bitならなにもなし 128bitならqを付ける
• 𝑇: 引数の型に応じた接尾辞
• 符号付き整数: sN(Nは要素のbit数) s8, s16, s32, s64
• 符号なし整数: uN(Nは要素のbit数) u8, u16, u32, u64
• 浮動小数点数: fN(Nは要素のbit数) f16, f32, f64
e.g.) vaddq_f32 : float32の足し算(128bit)
• なので (float32x4_t, float32x4_t) -> float32x4_t
e.g.) vminv_u8 : uint8のレジスタ内最小値(64bit)
• なので uint8x8_t -> uint8_t
23
0 1 2 3 20 30 40 50 20 31 42 53
254 140 42 89 51 115 178 60 42
Copyright © Fixstars Corporation. Some rights reserved.
ARMのSIMD(NEON)について>intrinsicの探し方
• Armのreference(Neon Intrinsics Reference)から探す
• https://developer.arm.com/architectures/instruction-sets/simd-
isas/neon/intrinsics
• intrinsicの詳細を知りたい場合やintrinsic自体を探す場合に便利
• 英語でググってStack Overflowのいい感じの質問を探し当てる
• intrinsicを複数組み合わせる操作やユースケースから探す際に便利
e.g.) 同じベクトル内で要素を回転させたい
• rotate命令はNEONにはない
• 「vextを同じベクトルに使えば実現できる」
• <arm_neon.h> を読む
• オフラインでも読めるしgrepやテキストエディタの検索などが使える
• 実装は基本的に読めないので関数が何をするのかはヘッダからは
読み取れないことが多い 24
0 1 2 3 1 2 3 0
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
25
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
26
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
27
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
28
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
29
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
30
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
31
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
32
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
33
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
34
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
35
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• 2次元畳み込み: 画像処理や機械学習などで用いられる操作
• 3x3のカーネルと呼ばれる行列と画素値を
それぞれ乗算→総和
• カーネルによって様々なフィルタが得られる
• 後述
• 𝑂(𝐻𝑊𝐶𝐾2
)
• ナイーブな実装だと5重for文
• 並列化はしやすい
• 各ピクセル間で依存がない
• 結果は 𝑊 − 𝐾 + 1 × 𝐻 − 𝐾 + 1
になる
• 今回は周囲1ピクセルは0埋め
36
𝐻
𝑊
𝐾
𝐾
※この図では𝐶は1だが
ケーススタディでは
𝐶 = 3(RGBの3ch.)
Copyright © Fixstars Corporation. Some rights reserved.
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 3x3の2次元畳み込み
• カーネルによって様々なフィルタが得られる
• ボックスフィルタ(平滑化)
• ソーベルフィルタ(エッジ抽出)
37
1 1 1
1 1 1
1 1 1
-1 -2 -1
0 0 0
1 2 1
ケーススタディ
• ナイーブな実装
• 2560x1440の画像に対して57.8ms
• 実行環境: Amazon EC2 c6g.4xlarge
• vCPU: AWS Graviton2
38
static constexpr std::size_t kernel_size = 3;
static constexpr std::size_t half_kernel_size = kernel_size/2;
for(std::size_t y = half_kernel_size; y < h-half_kernel_size; ++y)
for(std::size_t x = half_kernel_size; x < w-half_kernel_size; ++x)
for(std::size_t c = 0; c < 3; ++c){
float t = 0.f;
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j)
t += src[y-half_kernel_size+i][x-half_kernel_size+j][c]
* kernel[i][j];
dst[y][x][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t), 0u, 255u);
}
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ループの入れ替え
• チャネル(RGB)のループを内側に入れる
• メモリアクセスを連続にする
• RGBRGBRGB...と並んでいるので、それに沿ってアクセス
• 50.2ms(-7.6ms)
39
static constexpr std::size_t kernel_size = 3;
static constexpr std::size_t half_kernel_size = kernel_size/2;
for(std::size_t y = half_kernel_size; y < h-half_kernel_size; ++y)
for(std::size_t x = half_kernel_size; x < w-half_kernel_size; ++x){
float t[3] = {};
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j)
for(std::size_t c = 0; c < 3; ++c)
t[c] += src[y-half_kernel_size+i][x-half_kernel_size+j][c]
* kernel[i][j];
for(std::size_t c = 0; c < 3; ++c)
dst[y][x][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t[c]), 0u, 255u);
}
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• どこをSIMD化するか
• 計算
• 各ピクセルの各色毎にfloatの乗算と加算を9回行っている
• ここを複数ピクセル同時に行う
• float(32bit)なので128/32=4個ずつ同時に扱える
• 読み込み/書き込み
• RGBでインタリーブされたuint8_tのデータ列
• vld3q_u8/vst3q_u8を使えば128/8=16ピクセルずつ同時に読み書き可能
• しかもデインタリーブ/インタリーブは自動で行われる
• まずは計算からSIMD化してみる
40
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=0, j=0
41
1
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=0, j=1
42
2
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=0, j=2
43
3
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=1, j=0
44
4
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=2, j=0
45
5
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=2, j=2
46
9
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=0, j=0
47
10
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 現状は1個ずつ計算している
• i=2, j=2
48
36
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 4個ずつ計算する
• i=0, j=0
49
1
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 4個ずつ計算する
• i=0, j=1
50
2
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 4個ずつ計算する
• i=0, j=2
51
3
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• 計算のSIMD化
• 4個ずつ計算する
• i=2, j=2
52
9
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ループアンロールとpeel loop
• x方向のループを両端と真ん中で分割する
• 真ん中のループをSIMD化する(前後のがpeel loop)
53
//(略)
for(std::size_t y = half_kernel_size; y < h-half_kernel_size; ++y){
std::size_t x = half_kernel_size;
for(; x < 4; ++x){/*同じ処理*/}
const std::std::size_t simd_end = w-half_kernel_size - (w-half_kernel_size)%4;
for(; x < simd_end; ++x){
float t[3] = {};
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j)
for(std::size_t c = 0; c < 3; ++c)
t[c] += src[y-half_kernel_size+i][x-half_kernel_size+j][c]
* kernel[i][j];
for(std::size_t c = 0; c < 3; ++c)
dst[y][x][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t[c]), 0u, 255u);
}
for(; x < w-half_kernel_size; ++x){/*同じ処理*/}
}
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ループアンロールとpeel loop
• 真ん中のループを4でループアンロール
• ひとまずループ全体を4回回すように(zループ/後で入れ替える)
54
//(略)
for(std::size_t y = half_kernel_size; y < h-half_kernel_size; ++y){
std::size_t x = half_kernel_size;
for(; x < 4; ++x){/*同じ処理*/}
const std::std::size_t simd_end = w-half_kernel_size - (w-half_kernel_size)%4;
for(; x < simd_end; x += 4)for(std::size_t z = 0; z < 4; ++z){
float t[3] = {};
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j)
for(std::size_t c = 0; c < 3; ++c)
t[c] += src[y-half_kernel_size+i][x+z-half_kernel_size+j][c]
* kernel[i][j];
for(std::size_t c = 0; c < 3; ++c)
dst[y][x+z][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t[c]), 0u, 255u);
}
for(; x < w-half_kernel_size; ++x){/*同じ処理*/}
}
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• 読み込みと計算を分離する
55
//(略)
for(; x < simd_end; x += 4)for(std::size_t z = 0; z < 4; ++z){
float t[3] = {};
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j){
float s[3] = {};
const float kern = kernel[i][j];
for(std::size_t c = 0; c < 3; ++c)
s[c] = src[y-half_kernel_size+i][x+z-half_kernel_size+j][c];
for(std::size_t c = 0; c < 3; ++c)
t[c] += s[c] * kern;
}
for(std::size_t c = 0; c < 3; ++c)
dst[y][x+z][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t[c]), 0u, 255u);
}
//(略)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• ループを入れ替える
• zループを内側に
56
//(略)
for(; x < simd_end; x += 4){
float t[3][4] = {};
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j){
float s[3][4] = {};
const float kern = kernel[i][j];
for(std::size_t c = 0; c < 3; ++c) for(std::size_t z = 0; z < 4; ++z)
s[c][z] = src[y-half_kernel_size+i][x+z-half_kernel_size+j][c];
for(std::size_t c = 0; c < 3; ++c) for(std::size_t z = 0; z < 4; ++z)
t[c][z] += s[c][z] * kern;
}
for(std::size_t c = 0; c < 3; ++c) for(std::size_t z = 0; z < 4; ++z)
dst[y][x+z][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t[c][z]), 0u, 255u);
}
//(略)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• NEONで演算
• まずSIMDレジスタにデータを乗せる
57
//(略)
for(; x < simd_end; x += 4){
float32x4x3_t vt;
for(std::size_t c = 0; c < 3; ++c)
vt.val[c] = vdupq_n_f32(0.f);
for(std::size_t i = 0; i < kernel_size; ++i)
for(std::size_t j = 0; j < kernel_size; ++j){
float32x4x3_t vs;
const float32x4_t kern = vdupq_n_f32(kernel[i][j]);
for(std::size_t c = 0; c < 3; ++c){
float s[4];
for(std::size_t z = 0; z < 4; ++z)
s[z] = src[y-half_kernel_size+i][x+z-half_kernel_size+j][c];
vs.val[c] = vld1q_f32(s);
}
//続く
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• NEONで演算
• SIMD演算をしてSIMDレジスタからメモリに書き戻す
58
//続き
for(std::size_t c = 0; c < 3; ++c)
vt.val[c] = vfmaq_f32(vt.val[c], vs.val[c], kern);
} //jループの終わり
for(std::size_t c = 0; c < 3; ++c){
float t[4];
vst1q_f32(t, vt.val[c]);
for(std::size_t z = 0; z < 4; ++z)
dst[y][x+z][c] = std::clamp<std::uint8_t>(static_cast<std::uint8_t>(t[z]), 0u, 255u);
}
} //xループの終わり
//(略)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• NEONで演算
• 使ったintrinsic
• vdupq_n_f32
• float -> float32x4_t
• 4つすべて引数の値で初期化する
• vld1q_f32
• const float* -> float32x4_t
• メモリから4要素読み込んでSIMDレジスタに格納
• vst1q_f32
• (float*, float32x4_t) -> void
• SIMDレジスタからメモリに4要素書き込み
• vfmaq_f32
• (float32x4_t, float32x4_t, float32x4_t) -> float32x4_t
• FMA(Fused Multiply Add)
• a+b*cを返す
• 丸めを1度しか行わない(精度が良い)
• 速い
59
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
Q. 速くなりましたか?
A. いいえ
• むしろ遅い 131.9ms(+74.1ms)
• 計算のたびにメモリ読み込みをしているのが原因
• 極力レジスタ上のやりとりで済ませたい
60
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• i=0, j=0
61
1 2 3 4
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• i=0, j=1
62
5 6 7 8
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• i=0, j=2
63
9 10 11 12
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• 求める4ピクセルに対して上下1ピクセルの計12ピクセルは余分に
複数回読み込んでいる
64
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• 求める4ピクセルに対して上下1ピクセルの計12ピクセルは余分に
複数回読み込んでいる
→iループでデータを読み込んでおけばjループ間でデータを共有できる
• i=0
65
1 2 3 4 5 6
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• 求める4ピクセルに対して上下1ピクセルの計12ピクセルは余分に
複数回読み込んでいる
→iループでデータを読み込んでおけばjループ間でデータを共有できる
• i=0
66
2 3 4 5
1 6
1 2 3 4 5 6
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• 求める4ピクセルに対して上下1ピクセルの計12ピクセルは余分に
複数回読み込んでいる
→iループでデータを読み込んでおけばjループ間でデータを共有できる
• i=0
67
2 3 4 5
1 6
1 2 3 4 2 3 4 5 3 4 5 6
ここをSIMDレジスタ
の上でやりたい
1 2 3 4 5 6
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 現状はjループでメモリを1つずつ読んでいる
• 求める4ピクセルに対して上下1ピクセルの計12ピクセルは余分に
複数回読み込んでいる
→iループでデータを読み込んでおけばjループ間でデータを共有できる
• i=0
68
2 3 4 5
1 2 3 4 2 3 4 5 3 4 5 6
ここをSIMDレジスタ
の上でやりたい
1 2 3 4 5 6
1 1 1 1 6 6 6 6
SIMDレジスタとして
この3つを作っておく
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• iループで必要なデータを読み込んでおく
69
//(略)
for(; x < simd_end; x += 4){
float32x4x3_t vt;
for(std::size_t c = 0; c < 3; ++c)
vt.val[c] = vdupq_n_f32(0.f);
for(std::size_t i = 0; i < kernel_size; ++i){
float32x4x3_t vss[3];
for(std::size_t c = 0; c < 3; ++c){
vss[0].val[c] = vdupq_n_f32(src[y-half_kernel_size+i][x-half_kernel_size][c]);
float s[4];
for(std::size_t z = 0; z < 4; ++z)
s[z] = src[y-half_kernel_size+i][x+z-half_kernel_size+j][c];
vss[1].val[c] = vld1q_f32(s);
vss[2].val[c] = vdupq_n_f32(src[y-half_kernel_size+i][x+4][c]);
}
//続く
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• jループでSIMDレジスタ上で値を組み替えて目的のSIMDベクトルを作る
70
//続き
for(std::size_t j = 0; j < kernel_size; ++j){
float32x4x3_t vs;
const float32x4_t kern = vdupq_n_f32(kernel[i][j]);
for(std::size_t c = 0; c < 3; ++c){
switch(j){
case 0: vs.val[c] = vextq_f32(vss[0].val[c], vss[1].val[c], 3); break;
case 1: vs.val[c] = vss[1].val[c]; break;
case 2: vs.val[c] = vextq_f32(vss[1].val[c], vss[2].val[c], 1); break;
}
}
for(std::size_t c = 0; c < 3; ++c)
vt.val[c] = vfmaq_f32(vt.val[c], vs.val[c], kern);
}
//(略)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• 使ったintrinsic
• vextq_f32
• (float32x4_t, float32x4_t, int) -> float32x4_t
• 2つのベクトルをくっつけて真ん中を取る
e.g.) vextq_f32(R, G, 1) e.g.) vextq_f32(R, G, 3)
71
R0 R1 R2 R3 G0 G1 G2 G3
R1 R2 R3 G0
R0 R1 R2 R3 G0 G1 G2 G3
G0 G1 G2
R3
1 3
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>計算のSIMD化
• メモリからのロードを減らす
• これで56.3ms(-1.5ms)
• 遅くはないが速くもない
• 次に読み込みと書き込みをSIMD化する
• 今は画像のデータ領域から1個ずつ読んで配列に4個詰めてから
SIMDレジスタにロードしている ストアも同様
• uint8を4個取り出して4個のfloat32に型変換する必要がある
• uint8x4_tは存在しないので素直にはできない
• 直接画像のデータ領域からSIMDレジスタに読み込みたい
72
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• どこをSIMD化するか(再掲)
• 計算
• 各ピクセルの各色毎にfloatの乗算と加算を9回行っている
• ここを複数ピクセル同時に行う
• float(32bit)なので128/32=4個ずつ同時に扱える
• 読み込み/書き込み
• RGBでインタリーブされたuint8_tのデータ列
• vld3q_u8/vst3q_u8を使えば128/8=16ピクセルずつ同時に読み書き可能
• しかもデインタリーブ/インタリーブは自動で行われる
• 読み込み/書き込みの並列度と計算の並列度が一致しない
→16ピクセル分読み込んで4個ずつの計算を4回行う
73
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 読み書きのSIMD化
• 現状はiループでメモリを1つずつ読んでいる
• 読んだデータはiループ内で使い回す
• i=0
74
1 2 3 4 5 6
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 読み書きのSIMD化
• 現状はiループでメモリを1つずつ読んでいる
• 読んだデータはiループ内で使い回す
• i=0
75
7 8 9 10 11 12
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 読み書きのSIMD化
• 現状はiループでメモリを1つずつ読んでいる
• 読んだデータはiループ内で使い回す
• i=0
76
13 14 15 16 17 18
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 読み書きのSIMD化
• 現状はiループでメモリを1つずつ読んでいる
• 読んだデータはiループ内で使い回す
• i=0
77
19 20 21 22 23 24
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 読み書きのSIMD化
• これをまとめて読みたい
• i=0
78
1 3
2
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 読み書きのSIMD化
• これをまとめて読みたい
• 16要素から4要素×4に分解して利用する
• i=0
79
1 3
1 3
a b c d e f g h i j k l m n o p
2
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ループアンロールを16ずつにする
• 16個ずつ読み書きするので
80
static constexpr std::size_t kernel_size = 3;
static constexpr std::size_t half_kernel_size = kernel_size/2;
for(std::size_t y = half_kernel_size; y < h-half_kernel_size; ++y){
std::size_t x = half_kernel_size;
for(; x < 16; ++x){/*ナイーブな処理*/}
const std::std::size_t simd_end = w-half_kernel_size - (w-half_kernel_size)%16;
for(; x < simd_end; x += 16){
/*次のページ以降で説明*/
}
for(; x < w-half_kernel_size; ++x){/*ナイーブな処理*/}
}
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• uint8を16個読んで4個のfloat32x4_tに組み替える
81
//(略)
for(; x < simd_end; x += 16){
float32x4x3_t vts[4];
for(std::size_t z = 0; z < 4; ++z)
for(std::size_t c = 0; c < 3; ++c)
vts[z].val[c] = vdupq_n_f32(0.f);
for(std::size_t i = 0; i < kernel_size; ++i){
const uint8x16x3_t s = vld3q_u8(src[y-half_kernel_size+i][x]);
float32x4x3_t vss[6]; //6 = 1+4+1
for(std::size_t c = 0; c < 3; ++c){
vss[0].val[c] = vdupq_n_f32(src[y-half_kernel_size+i][x-half_kernel_size][c]);
vss[1].val[c] = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vmovl_u8(vget_low_u8(s.val[c])))));
vss[2].val[c] = vcvtq_f32_u32(vmovl_high_u16( vmovl_u8(vget_low_u8(s.val[c]))));
vss[3].val[c] = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vmovl_high_u8( s.val[c]))));
vss[4].val[c] = vcvtq_f32_u32(vmovl_high_u16( vmovl_high_u8( s.val[c])));
vss[5].val[c] = vdupq_n_f32(src[y-half_kernel_size+i][x+16][c]);
}
//続く
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• uint8を16個読んで4個のfloat32x4_tに組み替える
• 使ったintrinsic
• vld3q_u8
• const uint8_t* -> uint8x16x3_t
• 16ピクセル分データを取ってきてデインタリーブして
Rのuint8x16_t, Gのuint8x16_t, Bのuint8x16_tの3本にする
• vcvtq_f32_u32
• uint32x4_t -> float32x4_t
• u32からf32へのキャスト(128bit)
82
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• uint8を16個読んで4個のfloat32x4_tに組み替える
• 使ったintrinsic
• vget_low_u8
• uint8x16_t -> uint8x8_t
• 前半を取り出す
• vmovl_u8
• uint8x8_t -> uint16x8_t
• 各要素について整数拡張
• vmovl_high_u8
• uint8x16_t -> uint16x8_t
• 後半を取り出して各要素を整数拡張
• vget_low_u16/vmovl_u16/vmovl_high_u16
• uint16x8_tまたはuint16x4_tが引数となる
• 最終的にはuint32x4_tを得る
83
• これらの組み合わせで
vmovl_low_u8相当の処理になる
• uint8x16_t -> uint16x8_t
• 前半を取り出して各要素を整数拡張
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• uint8を16個読んで4個のfloat32x4_tに組み替える
• 以下のようなことをやっている
84
R0 G0 B0 R1 G1 B1 R2 G2 B2 R3 G3 B3 R4 G4 B4 R5 G5 B5 R6 G6 B6 R7 G7 B7
R0 R1 R2 R3 R4 R5 R6 R7
G0 G1 G2 G3 G4 G5 G6 G7
B0 B1 B2 B3 B4 B5 B6 B7
vld3q_u8
メモリ
SIMDレジスタ
R0 R1 R2 R3 R4 R5 R6 R7
vget_low_u8
…
R8 R9 R10 R11 R12 R13 R14 R15
G8 G9 G10 G11 G12 G13 G14 G15
B8 B9 B10 B11 B12 B13 B14 B15
R0 R1 R2 R3 R4 R5 R6 R7
vmovl_u8
vget_low_u16
R0 R1 R2 R3
R0 R1 R2 R3
vmovl_u16
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 4回計算する
• 計算のたびに読み込むベクトルを変える
85
//続き
for(std::size_t j = 0; j < kernel_size; ++j)
for(std::size_t z = 0; z < 4; ++z){
float32x4x3_t vs;
const float32x4_t kern = vdupq_n_f32(kernel[i][j]);
for(std::size_t c = 0; c < 3; ++c){
switch(j){
case 0: vs.val[c] = vextq_f32(vss[z].val[c], vss[z+1].val[c], 3); break;
case 1: vs.val[c] = vss[z+1].val[c]; break;
case 2: vs.val[c] = vextq_f32(vss[z+1].val[c], vss[z+2].val[c], 1); break;
}
}
for(std::size_t c = 0; c < 3; ++c)
vts[z].val[c] = vfmaq_f32(vts[z].val[c], vs.val[c], kern);
}
} //iループの終わり
//続く
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 書き込む
• 4本のfloat32x4_tをuint8x16_tに詰め込んでRGBをまとめて書き込む
86
//続き
uint8x16x3_t vds;
for(std::size_t c = 0; c < 3; ++c)
vds.val[c] = vqmovn_high_u16(
vqmovn_u16(vqmovn_high_u32(vqmovn_u32(vcvtq_u32_f32(vts[0].val[c])),
vcvtq_u32_f32(vts[1].val[c]))),
vqmovn_high_u32(vqmovn_u32(vcvtq_u32_f32(vts[2].val[c])),
vcvtq_u32_f32(vts[3].val[c])));
vst3q_u8(dst[y][x], vds);
} //xループの終わり
//(略)
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 書き込む
• 使ったintrinsic
• vcvtq_u32_f32
• float32x4_t -> uint32x4_t
• 型変換
• vqmovn_u32
• uint32x4_t -> uint16x4_t
• movnはmovlとは逆にbit幅を狭める
• qmovnはオーバーフロー時に最大値でクランプする
• uintは最小値は全部0なので気にしなくて良い
• vqmovn_high_u32
• (uint16x4_t, uint32x4_t) -> uint16x8_t
• 第2引数のuint32x4_tをqmovnしてから第1引数とつなげてuint16x8_tにする
• vqmovn_u16/vqmovn_high_u16
• uint16x8_tやuint8x8_tを受け取る 87
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 書き込む
• 以下のようなことをやっている
88
R0 R1 R2 R3 R4 R5 R6 R7
SIMDレジスタ R8 R9 R10 R11 R12 R13 R14 R15
R0 R1 R2 R3 R4 R5 R6 R7
vqmovn_high_u32
vqmovn_u16
R0 R1 R2 R3
R0 R1 R2 R3
vqmovn_u32
R4 R5 R6 R7
R8 R9 R10 R11 R12 R13 R14 R15
R0 R1 R2 R3 R4 R5 R6 R7
vqmovn_high_u16
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• 書き込む
• 使ったintrinsic
• vst3q_u8
• (uint8_t*, uint8x16x3_t) -> void
• Rのuint8x16_t, Gのuint8x16_t, Bのuint8x16_tの3本をインタリーブして
書き込む
89
R0 G0 B0 R1 G1 B1 R2 G2 B2 R3 G3 B3 R4 G4 B4 R5 G5 B5 R6 G6 B6 R7 G7 B7
R0 R1 R2 R3 R4 R5 R6 R7
G0 G1 G2 G3 G4 G5 G6 G7
B0 B1 B2 B3 B4 B5 B6 B7
vst3q_u8
メモリ
SIMDレジスタ
…
R8 R9 R10 R11 R12 R13 R14 R15
G8 G9 G10 G11 G12 G13 G14 G15
B8 B9 B10 B11 B12 B13 B14 B15
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• y方向のレジスタブロッキング
• 現在はiループ毎にSIMDレジスタにロードしているが、y方向には入力を
共有できるので数行ずつ計算するとその分メモリアクセスが減る
90
1 3
2
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• y方向のレジスタブロッキング
• 現在はiループ毎にSIMDレジスタにロードしているが、y方向には入力を
共有できるので数行ずつ計算するとその分メモリアクセスが減る
91
4 6
5
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• y方向のレジスタブロッキング
• 現在はiループ毎にSIMDレジスタにロードしているが、y方向には入力を
共有できるので数行ずつ計算するとその分メモリアクセスが減る
92
7 9
8
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• y方向のレジスタブロッキング
• 現在はiループ毎にSIMDレジスタにロードしているが、y方向には入力を
共有できるので数行ずつ計算するとその分メモリアクセスが減る
93
10 12
11
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• y方向のレジスタブロッキング
• 現在はiループ毎にSIMDレジスタにロードしているが、y方向には入力を
共有できるので数行ずつ計算するとその分メモリアクセスが減る
94
13 15
14
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• y方向のレジスタブロッキング
• 現在はiループ毎にSIMDレジスタにロードしているが、y方向には入力を
共有できるので数行ずつ計算するとその分メモリアクセスが減る
95
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ>ロード/ストアとインタリーブ
• ここまでやると23.5ms(-34.3ms)
• 2.46倍の高速化
• さらなる高速化
• 両端のメモリアクセスのキャッシング
• 現在はvss[0]やvss[5]の算出に1要素のメモリアクセスとvdupq_n_f32を
使っているが、これらは「前のvss[4]」や「次のvss[1]」としてSIMD
レジスタ上に置いておけるので読みに行かなくて済む
• 実際には先述のようなことをするにはSIMDレジスタが足りない
• 先述のようにARMv8のNEONにはSIMDレジスタが32本しかない
• 常にRGBの3本を扱うので各チャネルあたり10本程度しか使えない
• 現状で結構ギリギリ
96
Copyright © Fixstars Corporation. Some rights reserved.
ケーススタディ
• その他の高速化: y方向のマルチスレッド化
• 先述のように畳み込みは各ピクセルの各チャネル毎の依存がない
• yループに対してもマルチスレッド化が容易に可能
• 「SIMDを用いた高速計算入門」の範囲外なので今回は省略
97
Copyright © Fixstars Corporation. Some rights reserved.
まとめ
• SIMD演算を用いることでソフトウェアを高速化できる
• SIMD
• 1つの命令で複数のデータをまとめて処理する→データ並列に有効
• 最近のCPUはSIMD命令を効率的に処理するハードウェア(SIMD演算器)
を持っている
• ARMのCPUも例にもれない
• ARMv8のCPUにはSIMD命令セットがある
• 慣習的にNEONと呼ばれ(続け)ている
• 64bit/128bit SIMD
• デインタリーブロード/インタリーブストアが可能
• 画像処理に便利
• intrinsicを使った書き方を紹介した
• 型がしっかりしていて読み書きしやすい
98
Copyright © Fixstars Corporation. Some rights reserved.
告知
• 株式会社フィックスターズ https://fixstars.com/ja
• SIMDを使った高速化など、ソフトウェアの高速化が専門の会社
• インターン募集中
• 自己紹介
• 今泉 良紀(いまいずみ よしき)
• 入社3年目 シニアエンジニア
• 専門: プログラミング言語処理
• 趣味: C++ メタプログラミング
• 入社前は高速化の専門家ではなかったが、
業務を通して高速化の専門家に
• 連絡先: yoshiki.imaizumi@fixstars.com 99
Copyright © Fixstars Corporation. Some rights reserved.
この資料について
• この資料は東京大学 次世代知能科学研究センター主催の
講演会資料を一般公開向けに一部修正したものとなります
• この資料は
クリエイティブ・コモンズ表示-継承4.0国際ライセンス
の下に提供されています
• https://creativecommons.org/licenses/by-sa/4.0/deed.ja
• 以降のスライドでは講演当日に行われた質疑応答を載せます
• 一部回答について当日伝えきれなかった部分も補足してあります
• ARM以外のアーキテクチャにおけるSIMDについてや
フィックスターズの高速化業務などについても活発なご質問を
いただきましたので、そうした辺りも余すことなく記載しております
100
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. コンパイラのオプティマイザに比べてintrinsicで書くほうが
やはり速くなるものなのか?
• なんだかんだコンパイラのオプティマイザは強力
• elementwiseで単純な(コンパイラのオプティマイザが最適化しやすい)
コードであればコンパイラに任せたほうが速いこともよくある
• ある程度複雑なコードは既存のコンパイラのオプティマイザでは
SIMD化できない
• ケーススタディで扱った問題など
• こうした場合にintrinsicを用いることで(アセンブリと比べて)比較的
容易かつ保守しやすい形で高速化を図れる
101
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. SIMDプログラミングを支援するようなプロファイラはあるか?
あるいは「レジスタへの読み込みがネックだな」などと感覚で
把握しているのか?
• プロファイラを使うとしたら普通のプロファイラを使う
• SIMDに特化したものは無い
• ARMではないが、Intelのプロファイラは結構細かい情報が
出てくるので参考になるかも
• プロファイラの情報をどう読めばいいのか、といった部分は
職人芸(感覚で把握している)といえるかもしれない
• CPUやプロファイラではないが、一部のDSPではレイテンシを
タイムラインで可視化できるIDEが提供されていたりする
• メモリアクセスについては、プログラムのB/F比などを見ながら
調整
• これも一般的な高速化の範疇であってSIMD特有というわけではない
102
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. データ並列に対するアプローチとしてGPGPUもあるが、やはり
時代はSIMD命令ではなくGPGPU?
また、GPU上でSIMD命令は使えるのか?
• 前者について、エッジコンピューティングなど環境によってはGPUを
使えないことも多く、CPU(やDSP)のSIMDというのは重要な技術
• 後者について
• そもそも昔のGPUはSIMDなプロセッサだった
• これはRGBAやxyzwなどの3~4要素を対象にした計算が多かったため
• 近年のGPUはSIMTと呼ばれる形のものが多い
• ハードウェアとしてどのような実装になるかはさておき、
コード上は4要素を同時に扱うことも依然として多く、
少なくともプログラミングパラダイム上は(4要素程度の)SIMDな
コードを書くことができる
• OpenCLやCUDAにも組み込みのベクトル型が存在する
103
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. どういう環境で開発している?IDEなどは使うのか?
• SIMD化のような案件内容だと基本的には使わない
• ただし高速化が適切に行われているかを確認するためにパイプライン
の様子をタイムラインで可視化する機能を持つ一部のDSP専用のIDEを
併用することもある
• 基本的な開発で使うことは少ない
• 業務で顧客コードがIDEを前提にしていた場合は普通に使うことになる
• IDEを使わない場合は各自好きなエディタを使う
• 私はVim
104
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. 128bit用のintrinsicにはqが付くが、これが256bitになると
どうなるのか?
• Armの中の人ではないのでわからない
• が、おそらくそもそも256bit対応をNEONでは行わないのでは
• 256bit以上についてはSVEで対応するのではないかと考えている
Q. 時代はSVE?
• ARMv9で基本命令セット入りするが、v9が広く出回るのは当分先の話
• そのため、SVEは当面v8の拡張命令という立ち位置のはず
• その上で、「現時点で広く普及したCPUでほぼ必ず使えるSIMD命令」
としてのNEONはいろんなところで今すぐ使える技術
• これも1つの価値
• SVEのキャッチアップを今から始めるのもそれはそれで良いと思う
105
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. 今後PCにおいてARMがAMDに変わることは起こりうるか?
• AMDはx86(ないしx86_64、AMD64)のこととして回答します
• わかりません
• (特にWindowsで顕著ですが)ビルド済みバイナリの資産がx86最大の強み
• 一方でARMのx86エミュレーションも今後どんどん性能改善が進むはず
e.g.) AppleのRosetta
• これで特に問題ないならARMでも問題ない
• ただ少なくとも各社がARMに力を入れていることは確か
• MicrosoftでさえARM CPUを作っている
• モバイルの消費電力の観点や、自社でチップセット全体の設計に
手が出せるという辺りがx86と比べたときのARMの強みだと思う
• となるとまずはノートPCで広がりそう
106
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. 本日はARMアーキテクチャにおける手作業での高速化の話だったが
同様の高速化はx86_64アーキテクチャでも行われるか?
• もちろん行われる
• MMX, SSE各種, AVX, AVX2, AVX512 いずれもintrinsicがある
• https://software.intel.com/sites/landingpage/IntrinsicsGuide/
• ただし、ARMv8のSIMDと異なりこれらは拡張命令なので、intrinsicsを呼ぶ
前に「このCPUはそれらの命令に対応しているのか」を確認するコードが
必要(ARMv7のNEONも同様)
107
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. AVX512はやはり性能が良い?
• 実際にはAVX512を呼び出すとクロックが低下するといった問題もあり、
ちゃんと性能を出せるワークロードはあまり多くないという印象
• もちろんAVX2に比べて高い性能が出せるワークロードも存在するので、
うまく使ってやればちゃんと速くはなる
• また、単に「AVX512」といってもその名を冠した命令セットが
いろいろあり、自分の使っているCPUはどのAVX512が使えるのか…と
いった話もあるので使いやすさの面でもやや難しさがあると感じる
108
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. レジスタの使いすぎなどはどのように確認する?
• 確実な確認方法は、出力されたアセンブリを見てレジスタを退避する
コードが存在していれば使いすぎ、というもの
Q. アセンブリってよく見るもの?
• なんだかんだで最後に動くのはソースコードではなくコンパイル後の
実行バイナリなので、それと1対1で対応しているアセンブリが最終的
には大事
109
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. 計算時間の概算はできるものなのか?
• 各命令毎のサイクル数やメモリアクセスのレイテンシなどから
計算は可能
• 各命令毎のサイクル数などはマイクロベンチで取得
• ものすごくざっくりとした計算になると、「処理全体の𝑛割を占める
部分が𝑚スレッド並列できるので
10𝑚
𝑛+ 10−𝑛 𝑚
倍の高速化が見込める」
ぐらいで済ますこともある
• 業務の場合は顧客の要求次第でどの程度やるかは変わる
• 目標性能が提示されている場合、「このぐらいの目標ならこの方針で
達成できそうだな」となればざっくりとした計算で済ますこともある
110
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. SIMDは配列の処理が得意なイメージがあるが、リストのような
配列以外のデータ構造に対してSIMDはどの程度効果的?
• リンクリストとかはデータを直接拾ってこれないので、一度連続した
メモリ領域にデータをコピーし直す必要がある
• 関連して、デインタリーブ/インタリーブを行っていたように、
SIMDは同一種のデータをまとめて処理するのに向いているので、
AoS(Array of Structures)よりSoA(Structure of Arrays)の方が
適している
• AoSで持っているデータをSoAに変換するコストよりSIMD命令を使うこと
による高速化効果が大きい場合は有効、という話になる
111
Copyright © Fixstars Corporation. Some rights reserved.
質疑応答
Q. 割と実測値に基づいた話が多いが、時間計算量に基づく理論的な
話はするのか?
• 弊社の業務としてはあまりしないことも多い
1. 顧客側である程度アルゴリズムを詰めた後に依頼されることが多いため
• これは案件次第で、アルゴリズムの部分から一緒に考えていくような業務も
もちろんあります
2. 入力データ数が小さい場合定数項の影響が大きいため、定数項が大きく
並列化しにくい複雑なアルゴリズムよりも素朴で並列化しやすい
アルゴリズムに対して定数項を詰めていく方が有効なこともあるため
• このあたりは入力サイズ次第であり、やはり案件によります
112
Copyright © Fixstars Corporation. Some rights reserved.
おわり
113
0. はじめに
1. SIMDとはなにか
• フリンの分類
• SIMD演算器とSIMDレジスタ
• SIMD命令の使い方
• SIMDプログラミングの特徴
2. ARMのSIMD(NEON)について
• ARMとは
• ARM NEONとは
• NEONの特徴
• NEONのintrinsicの読み方
• NEONのintrinsicの探し方
3. ケーススタディ
• ループの入れ替え
• ループアンロールとpeel loop
• 計算のSIMD化
• ロード/ストアとインタリーブ
4. まとめ
5. 告知
Copyright © Fixstars Corporation. Some rights reserved.

Contenu connexe

Tendances

何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門masayoshi takahashi
 
分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17Takuya Akiba
 
CUDAプログラミング入門
CUDAプログラミング入門CUDAプログラミング入門
CUDAプログラミング入門NVIDIA Japan
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門大樹 小倉
 
畳み込みニューラルネットワークの高精度化と高速化
畳み込みニューラルネットワークの高精度化と高速化畳み込みニューラルネットワークの高精度化と高速化
畳み込みニューラルネットワークの高精度化と高速化Yusuke Uchida
 
高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装MITSUNARI Shigeo
 
近年のHierarchical Vision Transformer
近年のHierarchical Vision Transformer近年のHierarchical Vision Transformer
近年のHierarchical Vision TransformerYusuke Uchida
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexprGenya Murakami
 
モデルアーキテクチャ観点からのDeep Neural Network高速化
モデルアーキテクチャ観点からのDeep Neural Network高速化モデルアーキテクチャ観点からのDeep Neural Network高速化
モデルアーキテクチャ観点からのDeep Neural Network高速化Yusuke Uchida
 
すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!Genya Murakami
 
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)Kuniyasu Suzaki
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
最近のDeep Learning (NLP) 界隈におけるAttention事情
最近のDeep Learning (NLP) 界隈におけるAttention事情最近のDeep Learning (NLP) 界隈におけるAttention事情
最近のDeep Learning (NLP) 界隈におけるAttention事情Yuta Kikuchi
 
1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門NVIDIA Japan
 
モデル高速化百選
モデル高速化百選モデル高速化百選
モデル高速化百選Yusuke Uchida
 
Magnum IO GPUDirect Storage 最新情報
Magnum IO GPUDirect Storage 最新情報Magnum IO GPUDirect Storage 最新情報
Magnum IO GPUDirect Storage 最新情報NVIDIA Japan
 
Tensor コアを使った PyTorch の高速化
Tensor コアを使った PyTorch の高速化Tensor コアを使った PyTorch の高速化
Tensor コアを使った PyTorch の高速化Yusuke Fujimoto
 
【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Models【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Modelscvpaper. challenge
 

Tendances (20)

何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門何となく勉強した気分になれるパーサ入門
何となく勉強した気分になれるパーサ入門
 
分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17分散深層学習 @ NIPS'17
分散深層学習 @ NIPS'17
 
CUDAプログラミング入門
CUDAプログラミング入門CUDAプログラミング入門
CUDAプログラミング入門
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門
 
畳み込みニューラルネットワークの高精度化と高速化
畳み込みニューラルネットワークの高精度化と高速化畳み込みニューラルネットワークの高精度化と高速化
畳み込みニューラルネットワークの高精度化と高速化
 
高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装
 
近年のHierarchical Vision Transformer
近年のHierarchical Vision Transformer近年のHierarchical Vision Transformer
近年のHierarchical Vision Transformer
 
TVM の紹介
TVM の紹介TVM の紹介
TVM の紹介
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexpr
 
モデルアーキテクチャ観点からのDeep Neural Network高速化
モデルアーキテクチャ観点からのDeep Neural Network高速化モデルアーキテクチャ観点からのDeep Neural Network高速化
モデルアーキテクチャ観点からのDeep Neural Network高速化
 
すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!
 
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
最近のDeep Learning (NLP) 界隈におけるAttention事情
最近のDeep Learning (NLP) 界隈におけるAttention事情最近のDeep Learning (NLP) 界隈におけるAttention事情
最近のDeep Learning (NLP) 界隈におけるAttention事情
 
1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門
 
モデル高速化百選
モデル高速化百選モデル高速化百選
モデル高速化百選
 
Magnum IO GPUDirect Storage 最新情報
Magnum IO GPUDirect Storage 最新情報Magnum IO GPUDirect Storage 最新情報
Magnum IO GPUDirect Storage 最新情報
 
Tensor コアを使った PyTorch の高速化
Tensor コアを使った PyTorch の高速化Tensor コアを使った PyTorch の高速化
Tensor コアを使った PyTorch の高速化
 
【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Models【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Models
 

Similaire à ARM CPUにおけるSIMDを用いた高速計算入門

[Final]조진현 direct write
[Final]조진현 direct write[Final]조진현 direct write
[Final]조진현 direct write흥배 최
 
SORACOM UG 東京 #8 | eSIM オーバービュー
SORACOM UG 東京 #8 | eSIM オーバービューSORACOM UG 東京 #8 | eSIM オーバービュー
SORACOM UG 東京 #8 | eSIM オーバービューSORACOM,INC
 
4章 Linuxカーネル - 割り込み・例外 4
 4章 Linuxカーネル - 割り込み・例外 4 4章 Linuxカーネル - 割り込み・例外 4
4章 Linuxカーネル - 割り込み・例外 4mao999
 
仮想化技術によるマルウェア対策とその問題点
仮想化技術によるマルウェア対策とその問題点仮想化技術によるマルウェア対策とその問題点
仮想化技術によるマルウェア対策とその問題点Kuniyasu Suzaki
 
Dalvik仮想マシンのアーキテクチャ 改訂版
Dalvik仮想マシンのアーキテクチャ 改訂版Dalvik仮想マシンのアーキテクチャ 改訂版
Dalvik仮想マシンのアーキテクチャ 改訂版Takuya Matsunaga
 
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
PyCoRAMを用いたグラフ処理FPGAアクセラレータPyCoRAMを用いたグラフ処理FPGAアクセラレータ
PyCoRAMを用いたグラフ処理FPGAアクセラレータShinya Takamaeda-Y
 
13apr2013 kernelvm8-main
13apr2013 kernelvm8-main13apr2013 kernelvm8-main
13apr2013 kernelvm8-mainShotaro Uchida
 
SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力
SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力
SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力SORACOM,INC
 
プログラマ目線から見たRDMAのメリットと その応用例について
プログラマ目線から見たRDMAのメリットとその応用例についてプログラマ目線から見たRDMAのメリットとその応用例について
プログラマ目線から見たRDMAのメリットと その応用例についてMasanori Itoh
 
コンテナ型仮想化とはなんだったのか
コンテナ型仮想化とはなんだったのかコンテナ型仮想化とはなんだったのか
コンテナ型仮想化とはなんだったのかえむ ばーど
 
2014 dart flight school in Tokyo
2014 dart flight school in Tokyo2014 dart flight school in Tokyo
2014 dart flight school in Tokyonothingcosmos
 
エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~
エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~
エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~Takumi Kurosawa
 
LLVM overview 20110122
LLVM overview 20110122LLVM overview 20110122
LLVM overview 20110122nothingcosmos
 
HSM用ミドルウェア Conduit Toolkitの概要と使い方
HSM用ミドルウェア Conduit Toolkitの概要と使い方HSM用ミドルウェア Conduit Toolkitの概要と使い方
HSM用ミドルウェア Conduit Toolkitの概要と使い方Hiroshi Nakamura
 
130710 02
130710 02130710 02
130710 02openrtm
 
エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?
エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?
エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?Takumi Kurosawa
 

Similaire à ARM CPUにおけるSIMDを用いた高速計算入門 (20)

The simd
The simdThe simd
The simd
 
[Final]조진현 direct write
[Final]조진현 direct write[Final]조진현 direct write
[Final]조진현 direct write
 
SORACOM UG 東京 #8 | eSIM オーバービュー
SORACOM UG 東京 #8 | eSIM オーバービューSORACOM UG 東京 #8 | eSIM オーバービュー
SORACOM UG 東京 #8 | eSIM オーバービュー
 
4章 Linuxカーネル - 割り込み・例外 4
 4章 Linuxカーネル - 割り込み・例外 4 4章 Linuxカーネル - 割り込み・例外 4
4章 Linuxカーネル - 割り込み・例外 4
 
仮想化技術によるマルウェア対策とその問題点
仮想化技術によるマルウェア対策とその問題点仮想化技術によるマルウェア対策とその問題点
仮想化技術によるマルウェア対策とその問題点
 
Let's play with Goldfish
Let's play with GoldfishLet's play with Goldfish
Let's play with Goldfish
 
Dalvik仮想マシンのアーキテクチャ 改訂版
Dalvik仮想マシンのアーキテクチャ 改訂版Dalvik仮想マシンのアーキテクチャ 改訂版
Dalvik仮想マシンのアーキテクチャ 改訂版
 
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
PyCoRAMを用いたグラフ処理FPGAアクセラレータPyCoRAMを用いたグラフ処理FPGAアクセラレータ
PyCoRAMを用いたグラフ処理FPGAアクセラレータ
 
Boost.SIMD
Boost.SIMDBoost.SIMD
Boost.SIMD
 
13apr2013 kernelvm8-main
13apr2013 kernelvm8-main13apr2013 kernelvm8-main
13apr2013 kernelvm8-main
 
SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力
SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力
SORACOM Technology Camp 2018 | B5. SORACOM Air for セルラー グローバルカバレッジ の実力
 
プログラマ目線から見たRDMAのメリットと その応用例について
プログラマ目線から見たRDMAのメリットとその応用例についてプログラマ目線から見たRDMAのメリットとその応用例について
プログラマ目線から見たRDMAのメリットと その応用例について
 
コンテナ型仮想化とはなんだったのか
コンテナ型仮想化とはなんだったのかコンテナ型仮想化とはなんだったのか
コンテナ型仮想化とはなんだったのか
 
2014 dart flight school in Tokyo
2014 dart flight school in Tokyo2014 dart flight school in Tokyo
2014 dart flight school in Tokyo
 
エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~
エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~
エバンジェリストが語るパワーシステム特論 ~ 第3回:IBMオフコンはいかにして生き残れたのか?~第二章~
 
LLVM overview 20110122
LLVM overview 20110122LLVM overview 20110122
LLVM overview 20110122
 
HSM用ミドルウェア Conduit Toolkitの概要と使い方
HSM用ミドルウェア Conduit Toolkitの概要と使い方HSM用ミドルウェア Conduit Toolkitの概要と使い方
HSM用ミドルウェア Conduit Toolkitの概要と使い方
 
130710 02
130710 02130710 02
130710 02
 
SIMD in dlang
SIMD in dlangSIMD in dlang
SIMD in dlang
 
エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?
エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?
エバンジェリストが語るパワーシステム特論 ~ 第1回:IBMオフコンはいかにして生き残れたのか?
 

Plus de Fixstars Corporation

製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx
製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx
製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptxFixstars Corporation
 
製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~
製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~
製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~Fixstars Corporation
 
製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~
製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~
製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~Fixstars Corporation
 
株式会社フィックスターズの会社説明資料(抜粋)
株式会社フィックスターズの会社説明資料(抜粋)株式会社フィックスターズの会社説明資料(抜粋)
株式会社フィックスターズの会社説明資料(抜粋)Fixstars Corporation
 
Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)Fixstars Corporation
 
Jetson活用セミナー ROS2自律走行実現に向けて
Jetson活用セミナー ROS2自律走行実現に向けてJetson活用セミナー ROS2自律走行実現に向けて
Jetson活用セミナー ROS2自律走行実現に向けてFixstars Corporation
 
量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~
量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~
量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~Fixstars Corporation
 
金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化
金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化
金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化Fixstars Corporation
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)Fixstars Corporation
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)Fixstars Corporation
 
ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方
ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方
ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方Fixstars Corporation
 
AIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術について
AIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術についてAIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術について
AIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術についてFixstars Corporation
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)Fixstars Corporation
 
第8回 社内プログラミングコンテスト 結果発表会
第8回社内プログラミングコンテスト 結果発表会第8回社内プログラミングコンテスト 結果発表会
第8回 社内プログラミングコンテスト 結果発表会Fixstars Corporation
 
第8回 社内プログラミングコンテスト 第1位 taiyo
第8回社内プログラミングコンテスト 第1位 taiyo第8回社内プログラミングコンテスト 第1位 taiyo
第8回 社内プログラミングコンテスト 第1位 taiyoFixstars Corporation
 
第8回 社内プログラミングコンテスト 第2位 fy999
第8回社内プログラミングコンテスト 第2位 fy999第8回社内プログラミングコンテスト 第2位 fy999
第8回 社内プログラミングコンテスト 第2位 fy999Fixstars Corporation
 
第8回 社内プログラミングコンテスト 第3位 logicmachine
第8回社内プログラミングコンテスト 第3位 logicmachine第8回社内プログラミングコンテスト 第3位 logicmachine
第8回 社内プログラミングコンテスト 第3位 logicmachineFixstars Corporation
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)Fixstars Corporation
 
A challenge for thread parallelism on OpenFOAM
A challenge for thread parallelism on OpenFOAMA challenge for thread parallelism on OpenFOAM
A challenge for thread parallelism on OpenFOAMFixstars Corporation
 
マルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化について
マルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化についてマルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化について
マルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化についてFixstars Corporation
 

Plus de Fixstars Corporation (20)

製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx
製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx
製造業向け量子コンピュータ時代のDXセミナー_生産計画最適化_20220323.pptx
 
製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~
製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~
製造業向け量子コンピュータ時代のDXセミナー~ 最適化の中身を覗いてみよう~
 
製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~
製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~
製造業向け量子コンピュータ時代のDXセミナー ~見える化、分析、予測、その先の最適化へ~
 
株式会社フィックスターズの会社説明資料(抜粋)
株式会社フィックスターズの会社説明資料(抜粋)株式会社フィックスターズの会社説明資料(抜粋)
株式会社フィックスターズの会社説明資料(抜粋)
 
Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)
 
Jetson活用セミナー ROS2自律走行実現に向けて
Jetson活用セミナー ROS2自律走行実現に向けてJetson活用セミナー ROS2自律走行実現に向けて
Jetson活用セミナー ROS2自律走行実現に向けて
 
量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~
量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~
量子コンピュータ時代の製造業におけるDXセミナー~生産工程効率化に向けた新たなご提案~
 
金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化
金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化
金融業界向けセミナー 量子コンピュータ時代を見据えた組合せ最適化
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)
 
ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方
ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方
ソフト高速化の専門家が教える!AI・IoTエッジデバイスの選び方
 
AIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術について
AIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術についてAIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術について
AIチップ戦国時代における深層学習モデルの推論の最適化と実用的な運用を可能にするソフトウェア技術について
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)
 
第8回 社内プログラミングコンテスト 結果発表会
第8回社内プログラミングコンテスト 結果発表会第8回社内プログラミングコンテスト 結果発表会
第8回 社内プログラミングコンテスト 結果発表会
 
第8回 社内プログラミングコンテスト 第1位 taiyo
第8回社内プログラミングコンテスト 第1位 taiyo第8回社内プログラミングコンテスト 第1位 taiyo
第8回 社内プログラミングコンテスト 第1位 taiyo
 
第8回 社内プログラミングコンテスト 第2位 fy999
第8回社内プログラミングコンテスト 第2位 fy999第8回社内プログラミングコンテスト 第2位 fy999
第8回 社内プログラミングコンテスト 第2位 fy999
 
第8回 社内プログラミングコンテスト 第3位 logicmachine
第8回社内プログラミングコンテスト 第3位 logicmachine第8回社内プログラミングコンテスト 第3位 logicmachine
第8回 社内プログラミングコンテスト 第3位 logicmachine
 
株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)株式会社フィックスターズ 会社説明資料(抜粋)
株式会社フィックスターズ 会社説明資料(抜粋)
 
A challenge for thread parallelism on OpenFOAM
A challenge for thread parallelism on OpenFOAMA challenge for thread parallelism on OpenFOAM
A challenge for thread parallelism on OpenFOAM
 
マルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化について
マルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化についてマルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化について
マルチレイヤコンパイラ基盤による、エッジ向けディープラーニングの実装と最適化について
 

ARM CPUにおけるSIMDを用いた高速計算入門