enebularをSORACOM SIMをさしたゲートウェイで動かそうとしたけどうまくいかなかったのでSSH Port forwardingの小ネタ

サブタイトル:フルスケールエンジニアへの道は険しい

これはソラコム(https://soracom.jp/)のSoftware Design 誌 巻頭特集記念リレーブログ(https://blog.soracom.jp/relay-blog/2016-04/) 5/2分です*1

SORACOM SIMを使っていて困ることあるあるランキング第一位*2は、なんといってもPrivate IPアドレスしか割り振られないことでしょう。Global IPアドレスが割り振られるSIMであれば、SIMをさしたデバイス自身がサーバー(WebサーバーやSSHサーバー)になれるので、リモートから簡単にアクセスすることができます*3。しかし、Private IPアドレスしか割り振られないSIMを使用している場合は、一工夫が必要となります。

例えば、SSHによりリモートログインをしたいという場合、SSHの"踏み台サーバー"を立てて、それを経由してログインする方法が公開されています。

SORACOM AirとBeamと時々NAT越え - SORACOM Blog

Soracom Airで繋がったデバイスにリモートからSSHする | Developers.IO

これらのブログに書いてある通りにやってみたところ、微妙に上手くいかなかったので、それを回避するTIPSを紹介します。

使用したデバイス

今回は、SORACOM SIMがさせるデバイス(ゲートウェイ)として、アットマークテクノ社のArmadillo-IoT G1を使用しました。Armadillo-IoT G1で使用されている3GモジュールはSierraWireless社のMC8090というものです。

LAN経由は問題ないけど、3G経由だと上手くいかない

前述のブログに記載してある手順通りに、ゲートウェイ側では下記コマンドを実行しました。Armadillo-IoT G1は、標準ではSSHサーバーが起動していないので、事前にSSHサーバーを起動させておいてください。

# ssh -N -f -R <remote port>:localhost:22 <remote server>

そして、サーバーから下記コマンドで接続を試みてみましたが、上手くいきません。

$ ssh <user name>@localhost -p <remote port>


正確には、ゲートウェイを有線LANでネットワークに接続しているときにはうまくいきますが、SORACOM SIMを使った3G回線の時はうまくいかないようでした。サーバーから接続するときに、ssh -vvv としてデバッグメッセージを表示しながらやってみると、下記の部分で止まっているようです。

...
debug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY
Connection closed by <IP address>

ワークアラウンド

今回は、ゲートウェイのネットワークインターフェースのMTUを小さくすることで回避しました。

$ ifconfig wwan0 mtu 576

パケットフラグメンテーションを回避することで、症状が緩和されるようです。
SSH Frequently Asked Questions

問題は回避できましたが、これで正しい対処なのか、イマイチ自身がありません。実はネットワークの深い仕組みをよく知らないという、自分自身の課題が浮き彫りになった形です。

懺悔

今回のリレーブログでは、本当はenebularのagentをSORACOM SIMが刺さったデバイスに送り込むというネタをやるつもりでした、そこでもネットワークの課題が解決できずに没になったのでした。。。フルスケールエンジニアへの道は険しいですね。enebular自身も日々進化しているので、次回は是非リベンジしたいと思います。

*1:このブログを公開したのは何日だったっけ?

*2:私調べ

*3:一方で下手なセキュリティ設定で運用していると、悪い人たちに悪用されまくりという困った側面もあるので、一概にPrivate IPアドレスが割り振られることがデメリットとも言い難いのですが。

SORACOM Beamを使って安全にデバイスの位置情報を調べる

会社の方のブログに、Armadillo-IoT: GPSを使わずに位置情報を取得する | アットマークテクノ ユーザーズサイト というのを書きました。3G接続できるデバイスのおおよその現在地を調べる方法です。精度は全くありませんが、GPS不要で3Gの接続さえ生きていれば使える方法なので、デバイスの盗難対策などには重宝するのではないかと思います。

ただ、つらつらと書いていて思ったのが、「セキュリティの観点からするとイケてない」ということです。

  1. APIへのアクセスキーがデバイス側のスクリプトに埋め込まれてしまっている
  2. curlコマンドの -k オプション(--insecure, 証明書を検証しない)を使っている

1 は、たくさんあるデバイスで共通のAPIキーを使っていた場合、一つのデバイスがハックされてキーが流出したら、キーを更新するために全てのデバイスのファームを書き換えなくてはならなくなるという問題に繋がります。かといって、デバイスごとに個別のAPIキーを割り振ろうと思うと、キーの管理やデバイスの出荷作業などの手間が掛かって、コストに跳ね返ってきます。

2 は、単純に証明書をインストールすれば、回避できます。(会社ブログでは説明する分量を省くために、あえてスルーしました。)ただ、こちらも実運用を考えると証明書の更新という問題が出てきます。

この二つの問題、SORACOM Beamを使うとキレイに解決できるということに、改めて気づきました。

仕掛け

会社の方のブログでは、Armadillo(デバイス)から直接Google Maps APIを叩いて、結果を取得していました。これを、SORACOM Beam経由で叩くようにします。

Google Maps APIHTTPSのPOSTメソッドを使い、パラメータはbodyにJSON形式で渡すようになっています。また、APIキーはクエリストリングとして渡します。developers.google.com

SORACOM Beamの設定は、下記のようにしました。

  • エントリポイント
    • プロトコル: HTTP
    • ホスト名: beam.soracom.io
    • ポート番号: 8888
    • パス: /geo
  • 転送先
    • プロトコル: HTTPS
    • ホスト名: www.googleapis.com
    • ポート番号: (指定なし)
    • パス: /geolocation/v1/geolocate?key=APIキー
  • ヘッダ操作
    • 指定なし

現在位置を調べるスクリプトは下記のようになります*1。URLがbeam.soracom.ioになり、API_KEYがスクリプトから消えています。

#!/bin/sh

URL="http://beam.soracom.io:8888/geo"

MCC="440"
MNC="10"

cell=$(/etc/config/3g-celllocation)
set -- $cell

LAC=$1
CID=$2

BODY=$(cat << EOF
{
  "cellTowers": [
    {
      "cellId": ${CID},
      "locationAreaCode": ${LAC},
      "mobileCountryCode": ${MCC},
      "mobileNetworkCode": ${MNC}
    }
  ]
}
EOF
)

curl -H "Content-type: application/json" -d "$BODY" "$URL"

効果

APIキーをデバイス側で持っておく必要が無くなる

これにより、全デバイスが共通のスクリプトで動作できる=運用が楽になるということと、APIキーの流出リスクを減らすことができるという二つのメリットを、同時に享受できます。

元々、APIキーというものは、APIへのアクセスコントロールのために存在するのだと思います。それが、デバイスにAPIキーを持たせた場合には、デバイス認証という意味合いを持ってしまうため、「デバイスごとに個別のAPIキーを発行」などという設計になりがちなのかもしれません。

SORACOM Beamを使うと、指定したグループのSIMからのリクエストしか転送しませんので、それがデバイス認証としての役割を果たします。「デバイスの認証はSIMで、APIのアクセスコントロールはAPIキーで」というように役割をきっちりと分けて考えることができるので、システム全体としての設計もシンプルになります。

証明書の更新が不要になる

デバイスからBeamへのリクエストはHTTPで行いますので、CAの証明書をデバイス側は持つ必要が無くなります。当然ながら、更新も不要ですのでデバイスの管理が楽になります。

それでいながら、デバイスからBeamのエントリポイントまでは、なりすまし不可かつ盗聴不可な経路を通りますので、安全性が損なわれるわけではありません。

ということで

改めて、実運用を考えたときに、SORACOM Beamというのは便利だと思いました*2。IoTの世界はコンセプトフェーズから実践フェーズに入ってきた感があるので、これからはセキュリティや運用といった面がクローズアップされてくるでしょう。その観点からも、SORACOM(+ AWS)というのは、良い解なのではないかと思います。

*1:3g-celllocationの中身などは、会社ブログの方を参照してください。

*2:書いている内容は二番煎じじゃないかというツッコミはご容赦ください。

SORACOMの利用料を実際のユースケースで検証してみる

※ 本記事は ソラコム SORACOM リリース記念リレーブログ への寄稿記事です。

さて、既に各所で話題騒然となっていますが、2015年9月30日に株式会社ソラコムの"IoTプラットフォーム"が公開され、一般への提供が開始されています。

ソラコムのサービスは下記の2つから構成されます。

  • SORACOM Air: IoT向けデバイス通信サービス
  • SORACOM Beam: データ転送支援サービス

10月16日に行われた SORACOM Developper Confference #0 では、AirにカスタムDNS機能、BeamにAWS IoT連携機能の追加を発表するなど、すさまじいスピードでどんどんと成長していくようで、今後が楽しみです。

そんなソラコム社のサービスですが、既報のニュースなどを見ていると、少し誤解されているところがあるように思えます。例えば、下記の日経新聞の記事。

www.nikkei.com

"格安スマホと比べても8割以上安い通信料で提供する"とされていますが、比較対象が違いますよね。比較するなら、これまでのIoT/M2M向けのデータ通信プランとすべきでしょう。

IoT/M2M向けの通信プランは、現状では、各社から500円~1,000円を切るぐらいの価格が定価として出てくるのが相場観のようです(あくまで、個人の主観では)。安いところでは、月額400円を切るプランを提示するところ*1や、月額290円というプラン*2もあります。各社と個別に交渉すれば、条件次第ではより安い価格を引き出すこともできるでしょう。

SORACOM Airは"月額300円 + 通信量に応じた従量課金"ですので、確かに安くはあるのですが、最安というわけではありません。それでも、実運用を考えると、非常に大きなメリットがあると感じています。以下、その理由について書いてみます。

SORACOM Air/Beamの費用構造

SORACOM Airの費用はオープンになっており、Webページから参照できるようになっています。初期費用を除いて考えると、"基本料金 + 従量課金" という構造です。

SORACOM Air のご利用料金 - 株式会社ソラコム

基本料金はデータ通信のみのSIMの場合 10円/日 、SMSも利用可能なSIMでは 15円/日 となっています。

従量課金部分は、1. 料金クラス、2. 上り/下り、3. 利用時間帯によって変わります。料金クラスは通信速度と対応しており、s1.minimumの32kbpsからs1.fastの2Mbpsまで、ユーザー自身が随時変更することができます。通常時間帯(日本時間 午前6:00-翌 午前2:00)では、s1.minimumで上り 0.2円/MB 、下り 0.6円/MB となっており、s1.fastでは上り 0.3 円/MB 、下り 1円/MB となっています。深夜時間帯(日本時間 午前2:00-午前6:00)では、全ての料金クラス、利用時間帯において、上り下りともに 0.2 円/MB に設定されています。

ちなみに、これまでのIoT/M2M向けのデータ通信プランで費用を抑えようとした場合、通信速度、最大データ容量を抑える*3か、利用時間を制限する*4などの制約をかける必要がありました。ソラコム社のデータ通信サービスが実運用において大きなメリットがあるのは、このような制約をユーザー自身がコントロールできることが理由となります。

Airの他に、Beamを使うと1リクエストあたり0.001円かかります。

SORACOM Beam のご利用料金 - 株式会社ソラコム

Airの費用 + Beamの費用がSORACOMを利用する際に必要な費用となります*5

費用シミュレーション

いくつか、実際の利用シーンを念頭に置いて費用をシミュレーションしてみましょう。

小さな測定データを頻繁にアップするケース

温度センサと湿度センサから得たデータを、なるべく高頻度でサーバーにアップしたいというケースを考えてみます。

センサの分解能が10bitだとすると、二つのセンサーデータは高々2Byteです。しかし、それにタイムスタンプやデバイスIDなどを付け、ペイロードJSON形式にして、サーバー側を単純にREST APIで作り、セキュリティのためにHTTPSで送ろう、、、などとすると、1回の送信あたり平気でKByte単位になってきます。仮に1KByteとして、それを毎秒送ろうとすると下記の計算になります。

1KByte × 60sec/min × 60min/hour × 24hour/day × 30day/month = 約2.5GB/month

IoT/M2M向けの"安い"通信プランは月間データ通信容量は32MBや128MBに制限されているのが一般的ですので、月間2.5GBという通信容量はあまりにも多すぎます。ということで、HTTPSを止めてHTTPにしようか、HTTPよりもMQTTの方がヘッダ少なくて済むよね、などなど工夫して、通信量を抑えるというのがこれまでの常でした。128MBに収めるためには、1回のデータ送信量を50Byte程度にする必要があります。こうなると、TSLを被せるわけにもいかず、セキュリティを諦めるか、データ取得頻度を少なくするか、妥協を求められます。

SORACOMを利用した場合はどうでしょうか。Airのみでやろうとすると、速度クラスとしてs1.minimumとした場合、約800円ほどで実現できます。2.5GBつかって800円であれば、十分安いと言えるでしょう。

(Air基本料金) + (Air従量課金分) = (10円/day * 30day/month) + (0.2円/MB * 2500MB/month) = (300) + (500) = 約800円/month

SORACOM Beam を併用した場合はどうなるでしょうか?TCPHTTPS変換機能やMQTT→HTTPS変換機能を使う前提で、1回あたりの送信データ量を100Byteに抑えられたとして、月の通信量は下記のようになります。

100 Byte × 60sec/min × 60min/hour × 24hour/day × 30day/month = 約250MByte/month

このときのAirとBeamの費用は下記となります。(速度クラスとしてs1.minimumとした場合)

(Air基本料金) + (Air従量課金分) + (Beamの利用料) = (10円/day * 30day/month) + (0.2円/MB * 250MB/month) + (0.001円/resuest * 60request/min * 60min/hour * 24hour/day * 30day/month) = (300) + (50) + (2592) = 約3000円/月

1requestあたり0.001円とはいえ、チリも積もればですね*6。今の所毎秒データを送るようなユースケースではBeamでオフロードするよりも、デバイス側で頑張ってHTTPSで送る方が安くつくようです。

まれに大量のデータ通信をするケース

IoT/M2M向けのデータ通信プランで、費用を安く抑えようと月の通信量が32MBのプランを選択したとします。その場合困るのが、「たまに大量のデータ通信をしたい」というケースに対応できないということです。セキュリティホールが見つかったので臨時でファームウェアアップデートをしたいという場合、普段は1分に1回のデータアップロードで構わないが何か問題が発生した場合に問題のある個体だけ毎秒アップロードに切り替えたい場合、などが想定できます。

年に一月だけ、あるいは、問題が発生している個体だけ、プランを変更できればいいのですが、これまでのIoT/M2M向けデータ通信プランでは2年一括など包括契約をしているケースもあり、柔軟な対応は困難でした。なので、回線を使う側は普段は32MBプランでいいのに、何かあった時のためにあえて128MBプランで契約しておいたり、あるいは、回線提供側が「年に一回なら。。」といって多少は契約通信量を超えても見逃すといった対応をしていました。これは、使う側にとってはムダが発生していることになり、回線提供側からはリスクとなります。

SORACOM Airの場合、「使った分だけ」支払う従量課金制ですので、ある月だけたくさん使っても、他の月の費用が上がることはありません。また、課金はSIMごとに行われるので、ある個体がたくさん通信したからといって、他のSIMの費用が上がることもありません。ノーマルケースだけでなく、イレギュラーケースを考えた場合の運用コストの最適化が図れている形になります。

普段は通信しないケース

IoT/M2Mのユースケースで見逃せないのが、普段はデータ通信をする必要は無いが、「火山が爆発した」、「河川が氾濫した」、「家に不審者が侵入した」など"何か"がが起こった時だけ通信をしたいというユースケースです。

普段は通信量0で良いところがポイントとなります。このようなユースケースでは、通信しないときは基本料金を抑えられると良いのですが、現状そのようなプランを提供しているところは寡聞にして知りません。

例えば、「データ通信不可/SMSのみ可能」というようなプランがあり、月額基本料が低く設定されているといいな、と思います。"何か"が起こった時にデバイスからSMSを一発飛ばして、それを受けたサーバー側がAPIを叩き通信プランを「データ通信あり」にを変更、デバイスとのデータ通信を開始し、受信し終わったら再びAPIを叩いて「データ通信不可/SMSのみ可能」にしてSIMを"寝かせる"という使い方が考えられます。通信事業者にとっては儲からない話なので、どの事業者も積極的に取り組まなかった課題ですが、今後の展開に期待したいと思います。

まとめ

SORACOM Air/Beamの費用は基本料金が月300円~と安いので多くのユースケースでは十分に低く抑えることができるでしょう。しかし、従量課金部分があるため安く抑えられるケース/高くつくケース両方ありえます。ユーザー側でプランをコントロールすることができるので、安く使えるか、高くつくかはユーザー次第な部分もあります。単なる「格安SIM」として捉えないで、システム全体、運用サイクル全体での最適化を考える必要があるでしょう。

*1:月額400円以下でIoT用のモバイル通信を実現するArduino互換モジュール「Spark Electron」 - GIGAZINE

*2:小容量データ通信向けM2M専用モバイル通信サービスを提供開始|KCCS

*3:128kbps固定/月間128MBまでとか、32kbps固定/月間32MBまでなど

*4:上記のKCCS社の月額290円プランでは3時~5時までの利用に制限されています

*5:カスタムDNSを使う場合は、別途その費用も必要となります。

*6:料金表では"エントリーポイント (Beam) へのリクエスト、Beamから転送先へのリクエスト、それぞれを個別に 1 リクエストとカウントします。"とあるので、実はこれの倍かかるのかもしれません。

これからの仕事のことを考えてみよう

もしもインターネットがなかったら・・・
昭和のビジネスを再現してみた。

という記事がネットの片隅で話題になっています。

パソコンやインターネットが無い時代、Word/Excelもメールも無い時代の仕事の仕方というのは、それが当たり前になった現在の我々にとっては想像がつきません。しかし、その時代から現代までの「仕事」の変遷を考えてみると、今後の「仕事」というものが見えてきます。

私がパソコンを初めて購入したのは中学生の時(1995年)ですが、「インターネットを使いたい」というのが主な動機でした。「年賀状を作れれば十分だからワープロでいい」と主張する父を、「パソコンだったらプリンタをつなげれば年賀状"も"作れる」と、なんとか説得して買ってもらったのです。

その時に購入したパソコンはWindows95搭載のPC-9821 Cx3(CanBe)でした。プリンタも合わせて、40万円くらいしたでしょうか。私が購入したモデルAはワープロソフトとしてJustsystem一太郎を搭載していました。(モデルBがMicrosoft Word搭載でした。当時は国内では一太郎の方が普及していたのです。)

当然ながら、就職した時点でインターネットやメールを使えるのが当たり前の時代でした。その時代と、記事中にある「パソコンやインターネットが無かった時代」とを比べると、仕事の仕方として大きく違うのは、当時は"完全分業制"だったということでしょう。

何しろ、社内の情報伝達用のエアシューターで送られてくるメッセージを拾うことが専門のメールボーイなる人が居たという話です。現在、電話番やメール番だけをしている人がいたら、真っ先にリストラの対象になるでしょう。

反対に言うと、現在は資料の作成からプレゼン、情報伝達(メール)までなんでもかんでも一人でやるようになった、すなわち自己完結的な仕事の仕方になったとも言えます。現在でも、ある程度忙しくなってくるとアシスタントや秘書を付けたりして、一部分業したりしますので、"やるようになった"というよりは、"できるようになった"というべきでしょうか。

分業から自己完結へという流れに沿って、未来に向けて考えてみると、今後、我々一般のオフィスワーカーは、何を自分でやるように(あるいは、できるように)なるのでしょうか?

その一つとして、私は"データ分析"を個々のオフィスワーカーが行うようになるのではないかと考えています。なぜならば、今後、通り一辺倒の定型間接業務を行う人員というのは急速に不要になり、考える業務しか残されないからです。

情報を右から左に流すだけの業務は、ほとんどが自動化され、コンピュータに取って代わられます。CRMやBIツールの発展により、単なる計数をまとめただけの報告資料は自動で生成されるようになります。freeeなどの会計ソフトの発達により、経理処理は自動化されていきます。IBM WatsonなどのAIの発達により、単なる電話受付や問い合わせ処理もコンピュータが自動で対応してくれるようになります。また、IoT(Internet of Things)あるいはIndustorie 4.0という流れにより、生産管理の中の調整業務も自動化されていくことでしょう。生産管理部門と営業部門の調整の場である、製販会議というものも不要になるかもしれません。残るのは、なにがしかの判断を伴う業務だけです。

判断の根拠となるのは、データです。そのため、データを分析することが、今後のオフィスワーカーの主要な業務の一つとなるでしょう。折しも、MicrosoftがPowerBIというBIツール無償化*1しました。Word/Excelがパソコンに標準インストールされることで、資料作成を自分でできるようになったように、BIツールも無料で手に入るようになったので、データ分析を自分でできる時代になってきました。

今の時点で、データ分析が主要な業務の一つとなる、と言っても、ピンと来ないかもしれません。しかし、パソコンもインターネットも無かった時代、オフィスにいる時間のほとんどをWord/Excelやメールと睨めっこしているようになると誰が想像したでしょうか?今後は、Word/Excelで作成するような文書は自動で生成されるようになってきますので、その時間は不要になり、別の業務に
割り当てられます。その一つがデータ分析となるでしょう。

コンピュータやインターネットの発展は、今後も留まることはありません。
その流れの中で、仕事の自己完結化は、ますます進んでいきます。
そんな中、「これからの仕事」はどのように変わっていくのでしょうか。
あなたは、どのように考えますか?

ARM NEON命令を使って画像の回転

What is the fastest way to copy memory on a Cortex-A8?
という記事によると、単純にワードごとにメモリをコピーした場合に比べ、ARM NEON命令を使うと約50%ほどコピーが早くなるそうです。

画像の回転も、基本的には順番を入れ替えながらのメモリコピーですので、NEON命令を使うとどのくらい早くなるのか、調べてみました。

単純にC言語で実装した場合

まずは、こんなコードでFull HD(1920x1080サイズ)画像を180度回転させて、どのくらいかかるか測定します。bits/pixelはRGBxを想定して32bit決め打ちです。画像を180度回転させる処理は、コピー元の開始アドレスから1ピクセル(32bit)ずつ読み出して、コピー先の最終アドレスから逆順にコピーしていくだけの処理となります。

/**
 * @param src 入力画像アドレス
 * @param dst 出力画像アドレス
 * @param nr_pix 画像のピクセル数
 */
void rotate180(void *src, void *dst, size_t nr_pix)
{
        unsigned int i;
        uint32_t *s = (uint32_t *)src;
        uint32_t *d = (uint32_t *)dst + nr_pix - 1;

        for (i = nr_pix; i ; i--) {
                *d = *s;
                s++;
                d--;
        }
}

Cortex-A9 シングルコア、DDR3 SDRAM 512MB搭載のCPUボードで動作させてみたところ、約110msecかかりました。

gcc -Sで出力したアセンブリコードは下記のようになります。

        .align  2
        .global rotate180
        .thumb
        .thumb_func
        .type   rotate180, %function
rotate180:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
        cbz     r2, .L1
        subs    r3, r2, #1
        add     r1, r1, r3, lsl #2
.L3:
        ldr     r3, [r0], #4
        subs    r2, r2, #1
        str     r3, [r1], #-4
        bne     .L3
.L1:
        bx      lr

コピーを半分にする

入力と出力で2面も画像用のメモリを持つのは勿体ないので、元の画像用メモリを上書きするようにしてみました。

/**
 * @param p 画像アドレス
 * @param nr_pix 画像のピクセル数
 */
void rotate180_2(void *p, size_t nr_pix)
{
        unsigned int i;
        uint32_t *s = (uint32_t *)p;
        uint32_t *d = (uint32_t *)p + nr_pix - 1;
        uint32_t tmp;

        for (i = nr_pix/2; i ; i--) {
                tmp = *d;
                *d = *s;
                *s = tmp;
                s++;
                d--;
        }
}

ループが半分になったので、処理時間は約70msecとなりました。

アセンブリコードは下記のようになります。

        .align  2
        .global rotate180_2
        .thumb
        .thumb_func
        .type   rotate180_2, %function
rotate180_2:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
        lsrs    r3, r1, #1
        push    {r4}
        beq     .L6
        subs    r1, r1, #1
        mov     r2, r0
        add     r1, r0, r1, lsl #2
.L8:
        ldr     r0, [r1, #0]
        subs    r3, r3, #1
        ldr     r4, [r2, #0]
        str     r4, [r1], #-4
        str     r0, [r2], #4
        bne     .L8
.L6:
        pop     {r4}
        bx      lr

NEON命令を使う

インラインアセンブラNEON命令を使って書いてみます。vrev64.32 は、64bit長のレジスタ(d0やd1)を32bitずつ入れ替える命令です。

/**
 * @param p 画像アドレス
 * @param nr_pix 画像のピクセル数(8の倍数)
 */
void rotate180_simd(void *p, size_t nr_pix)
{
        unsigned int i=nr_pix/8/2;
        uint32_t *s = (uint32_t *)p;
        uint32_t *d = (uint32_t *)p + nr_pix - 1 - 8;

        asm volatile("\n"
                     ".p2align  2\n"
                     ".loop:\n"
                     "pld [%0, #64]\n"
                     "pld [%1, #64]\n"

                     "vld1.64 {d0-d3}, [%0]\n"
                     "vld1.64 {d4-d7}, [%1]\n"

                     "vrev64.32 d11, d0\n"
                     "vrev64.32 d10, d1\n"
                     "vrev64.32 d9, d2\n"
                     "vrev64.32 d8, d3\n"

                     "vrev64.32 d15,  d4\n"
                     "vrev64.32 d14,  d5\n"
                     "vrev64.32 d13,  d6\n"
                     "vrev64.32 d12,  d7\n"

                     "vst1.64 {d8-d11}, [%1]!\n"
                     "vst1.64 {d12-d15}, [%0]!\n"
                     "subs %1, %1, #64\n"

                     "subs %2, %2, #1\n"
                     "bne .loop\n"
                     :
                     : "r"(s), "r"(d), "r"(i)
                     : "q0","q1","q2","q3","q4","q5","q6","q7");
}

このコードで、約40msまで短縮することができました。

アセンブリコードは下記のようになりました。

        .align  2
        .global rotate180_simd
        .thumb
        .thumb_func
        .type   rotate180_simd, %function
rotate180_simd:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
        sub     r3, r1, #9
        fstmfdd sp!, {d8, d9, d10, d11, d12, d13, d14, d15}
        lsrs    r1, r1, #4
        add     r3, r0, r3, lsl #2
#APP
@ 139 "rotate.c" 1

.p2align  2
.loop:
pld [r0, #16]
pld [r3, #16]
vld1.64 {d0-d3}, [r0]
vld1.64 {d4-d7}, [r3]
vrev64.32 d11, d0
vrev64.32 d10, d1
vrev64.32 d9, d2
vrev64.32 d8, d3
vrev64.32 d15,  d4
vrev64.32 d14,  d5
vrev64.32 d13,  d6
vrev64.32 d12,  d7
vst1.64 {d8-d11}, [r3]!
vst1.64 {d12-d15}, [r0]!
subs r3, r3, #64
subs r1, r1, #1
bne .loop

@ 0 "" 2
        .thumb
        fldmfdd sp!, {d8, d9, d10, d11, d12, d13, d14, d15}
        bx      lr

まとめ

画像を180度回転させるコードを、NEONを使って書いてみました。倍速とまではいきませんでしたが、それに近い高速化(70msから40ms)ができました。

今回は単純な180度回転のみを扱いましたが、90度回転をしたい場合は、 Rotating a color image 90°using ARM "NEON" Advanced SIMD assembly code が参考になると思います。