iOSで超解像 - その3：パフォーマンス改善

超解像とは、**画像の解像度を高める技術**のことで、100x100ピクセルの画像から200x200ピクセルの画像を生成する、というようなものです。

そしてwaifu2xは**深層畳み込みニューラルネットワークを用いた超解像システム**で、従来の補間処理によるリサイズとはまったく別次元の効果を発揮します。

（左）従来法による拡大、（右）waifu2xによる拡大

![comic_処理結果.png](https://qiita-image-store.s3.amazonaws.com/0/3180/a97d782c-c6be-7a26-7352-e4901916ef8b.png)

<img width="1081" alt="比較.png" src="https://qiita-image-store.s3.amazonaws.com/0/3180/59e0bdba-9ad9-e921-daf7-9fdc6841e3f9.png">

![](https://qiita-image-store.s3.amazonaws.com/0/3180/473d7dc7-8743-da98-3ac6-958acd00d14f.jpeg)

![](https://camo.qiitausercontent.com/d6c9d46d49b902ae6a4663015c61e40c94481423/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f333138302f34343636613337652d393730382d646366392d653436612d6465366537656535333638662e706e67)

（写真は[itSnap](https://itsnap.jp/theme_%E3%81%A1%E3%81%BE%E3%81%9F%E3%81%AE%E8%A6%96%E7%B7%9A%E3%82%92%E3%81%B2%E3%81%A8%E3%82%8A%E5%8D%A0%E3%82%81%E2%99%A1%E3%83%80%E3%82%B9%E3%83%86%E3%82%A3%E3%83%94%E3%83%B3%E3%82%AF%E3%81%AE%E3%83%AD%E3%83%B3%E3%82%B0%E3%82%B3%E3%83%BC%E3%83%88%E3%81%A7%E5%9C%A7%E5%80%92%E7%9A%84%E3%81%AA%E5%AD%98%E5%9C%A8%E6%84%9F%E3%82%92%E6%94%BE%E3%81%A4-20160328)様より特別に許可をいただき使用させていただきました[^dealforest]）

[^dealforest]: [dealforest](https://twitter.com/dealforest)さんありがとうございます！

「[その1](https://qiita.com/shu223/items/4b8a603215bb50424f85)」では、waifu2xのMetal実装である[waifu2x-metal](https://github.com/safx/waifu2x-metal)を手元の画像で試し、実際に効果があることを確認しました。（それが上の画像）

waifu2x-metalはmacOSで動くように実装されていますが、**MetalなのでiOSでも動作するはず**です。

これをiOSで**オンデバイスで**利用できれば、アプリのUX改善にとってその効果は絶大です。たとえば、サーバーからは非Retinaサイズで取得しておいて、デバイス側でRetinaサイズに拡大して表示、ということが考えられます。**画像のクオリティは維持しつつ通信量が4分の1**になれば、通信速度というUXにおける大きなボトルネックが改善し、ネットのインフラが十分でない環境でもサクサク画像が表示されるようになる、というような効果が期待されます。

そんなわけで前回の[その2](https://qiita.com/shu223/items/be520cfc920246d716ec)では、waifu2x-metalをiOSに移植してみました。

iOSへの移植は無事うまくいき、オンデバイスで動くには動いたのですが、前回ネタバレをした通り、現時点での実装では

> リアルタイム処理には程遠い

です。

この回では実際の処理時間の計測結果について書いたのちに、改善の展望を示したいと思います。

## 処理時間の計測

### 計測方法

計測は次のように`apply(for:)`メソッドの処理時間を計測しており、

```swift
let startTime = CACurrentMediaTime()
texture = Waifu2x().apply(for: "image")
let endTime = CACurrentMediaTime()
print("Time to apply: \(endTime - startTime)")
```


当該メソッドは、

1. jsonファイルからのニューラルネットワークのパラメータ読み込み
2. 入力画像のロード
3. waifu2x処理

を含みます[^1]。処理結果テクスチャの`MTKView`への描画は含んでいません。

[^1]: iOS移植版のソースコードはまだ公開していませんが、元となるmacOS版のアルゴリズム自体にはまだ手を入れていません。

### 計測結果

**iPhone X**/ iOS 11で220 x 220ピクセルの画像を超解像処理させたところ、計測結果は

> Time to apply: 11.0397451249883

となりました。**220x220ピクセルの画像を最新のiPhoneで超解像処理するのに11秒**です。

**100倍高速化できたとしてやっと10fps**です。コアなアルゴリズムと関係ないところだけ見ても、jsonデータの読み込み等の明らかな改善ポイントがありますが、これはそういうレベルでの最適化ではリアルタイムには辿り着けそうにありません。


ちなみに処理時間の内訳としては、上記3の**"waifu2x処理"が 10.57 sec**でした。残り 0.5 sec の処理もリアルタイム処理の観点からいうと最適化すべきですが、まずはこの「10秒」を抜本的に高速化できるような改善ポイントがないか、コードを見てみました。



## 問題点1: 処理を十分に並列化していない

コードを読んでいくうちに、この箇所に気付きました。

```swift
let outputs = zip(info.weight, info.bias).map { (a,b) -> MTLTexture in
    return waifu2x(inputs, weight: a, bias: b)
}
```

**CNNの各層において、1チャネルごとに別々に処理**しています。これではGPUの並列化の恩恵を十分に受けられていません。

このwaifu2xでは、各層のチャネル数は順番に、

32 → 32 → 64 → 64 → 128 → 128 → 3

となっています。ちゃんと**ベクタライズ**してこれらをすべて並列に処理できれば、パフォーマンスは圧倒的に改善される可能性があると思います。

どうベクタライズするかですが、

- Metal Performance Shaders (MPSCNN)を使って実装しなおす
  - `MPSImage`は4チャネル以上のテクスチャを並列処理するためのフォーマットなので、やはりこういうCNNの処理を実装するには一番向いてそう
- BNNSを使って実装しなおす
- Core MLを使って実装しなおす
  - 元のモデルデータをcoremltoolsで変換できるならこれが一番簡単かもしれない
- このMetal実装を修正する
  - 現時点ではwaifu2xのアルゴリズムを理解しないまま書いてるので、本当にMPSCNNやCore MLで置き換えられるのか確信がない。
  - ので、結局これが一番近道かもしれない

というあたりが考えられます。


## 問題点2: GPUをフルに働かせてない

現実装では、GPUにコマンドをコミットする度に、その完了を待ってから、次の処理を行っています。

```swift
commandBuf.commit()
commandBuf.waitUntilCompleted()
```

トリプルバッファリングでもっとGPUを働かせるようにすれば、処理時間はもっと短くなるはずです。

- [Metal Best Practices Guide: Triple Buffering](https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/TripleBuffering.html)

トリプルバッファリングの実装自体は簡単なのですが、問題点1の改善をしないままにこれをやっても、並列化されてないコマンドを次々GPUにプッシュすることになり**GPUにとっては徒労**なので、まずは問題点1の解決が必要です。

## 他の改善ポイント

waifu2x-metalの作者の方の記事に、いくつかの改善指針が示されています。

- [Safx: Metalでwaifu2xを実装してみた](http://safx-dev.blogspot.fi/2015/07/metalwaifu2x.html)

そのうち、

> オリジナルwaifu2xや ueshita/waifu2x-converter-glsl では入力画像を128x128ピクセルごとに分割して処理しているようですが、Metal版ではそれを行っていません。

は実用的にしていく段階で必要そうだなと思いました。

その他、Metal 2の新機能である、[Argument Buffers](https://qiita.com/shu223/items/4fe437698574434b722d)や、Non-uniform threadgroup sizeも、もしかしたら効くかもしれません。（前者はこのケースではあまり関係ないか）



## 次回

改善実装を行いたいと思います。リアルタイムとは行かないまでも、ある程度速くすることに成功したら何らかの形で公開したいと思っております。


## Metalの参考書籍

技術書「[iOS 11 Programming](http://peaks.cc/shu223/iOS11)」の、「第13章 Metal」を執筆しました。

書籍のタイトルにはiOS 11とありますが、Metalについては新機能の紹介だけではなくて、**基礎からじっくり解説**しています。Metalの章だけで**37ページ**もあり、日本語ではこれだけのまとまったMetalの解説はレアかと思います。

- 13.1	はじめに
- 13.2	Metalの概要
- 13.3	Metalの基礎
- 13.4	MetalKit
- 13.5	Metal入門その１ - 画像を描画する
- 13.6	Metal入門その２ - シェーダを利用する
- 13.7	Metal入門その３ - シェーダでテクスチャを描画する
- 13.8	ARKit+Metalその１ - マテリアルをMetalで描画する
- 13.9	ARKit+Metalその２ - MetalによるARKitのカスタムレンダリング
- 13.10	Metal 2
- 13.11	Metalを動作させるためのハードウェア要件

他の章も他著やネットではなかなか得られない濃い情報が詰まっているので、気になった方はぜひ[サンプルPDF](http://peaks.cc/shu223/iOS11)もあるので見てみてください。

- 第1章　iOS 11 概要
- 第2章　ARKit
- 第3章　Core ML
- 第4章　Swift 4の新機能とアップデート
- 第5章　Xcode 9 の新機能
- 第6章　Drag and Drop
- 第7章　FilesとDocument Based Application
- 第8章　レイアウト関連の新機能及び変更点
- 第9章　Core NFC
- 第10章　PDF Kit
- 第11章　SiriKit
- 第12章　HomeKit入門とiOS 11のアップデート
- 第13章　Metal
- 第14章　Audio関連アップデート

PEAKSのサイトにて電子書籍・紙の書籍ともに販売されています。

<div class="peaks_widget" style="overflow:hidden; padding:20px; border:1px solid #dedede;"><div class="peaks_widget__image" style="float:left; margin-right:15px; line-height:0;"><a target="_blank" id="purchase" href="https://peaks.cc/shu223/iOS11"><img alt="iOS 11 Programming" style="border:none; max-width:140px;" src="https://s3-ap-northeast-1.amazonaws.com/peaks-images/project002_cover.jpg" width="500"></a></div><div class="peaks_widget__info"><p style="margin:0 0 3px 0; font-size:110%; font-weight:bold;"><a target="_blank" id="purchase" href="http://peaks.cc/shu223/iOS11">iOS 11 Programming</a></p><ul style="margin:0; padding:0;"><li style="font-size:90%; list-style:none;"><span>著者：</span><span>堤 修一,</span><span>吉田 悠一,</span><span>池田 翔,</span><span>坂田 晃一,</span><span>加藤 尋樹,</span><span>川邉 雄介,</span><span>岸川克己,</span><span>所 友太,</span><span>永野 哲久,</span><span>加藤&#160;寛人,</span></li><li style="font-size:90%; list-style:none;">発行日：2017年11月16日</li><li style="font-size:90%; list-style:none;">対応フォーマット：製本版,PDF</li><li style="font-size:90%; list-style:none;"><a target="_blank" id="purchase" style="text-decoration:underline; color:#1DA1F2;" href="http://peaks.cc/shu223/iOS11">PEAKSで購入する</a></li></ul></div></div>

執筆を担当したARKit、Metalの章の詳細、あるいは全体的なおすすめポイントは以下の記事にも書きました。

- [「iOS 11 Programming」を共著で執筆しました／本書のおすすめポイント - Over&Out その後](http://d.hatena.ne.jp/shu223/20171128/1511827476)