2014年10月18日土曜日

今更Dockerを使い始めて初日に感じたことメモ

この1年ぐらいTLの一部(というか割と多く)でDockerの話題が出て、Docker 1.0が出たとかMicrosoftともアライアンス組んだとかそういうニューストピックという認識はあったんだけど、取り立てて自分の手元での用途が無かったのでこれまで全く触ってなかった。

先週あたりからちまちまと東京メトロの オープンデータ活用コンテスト に取り組み始めて、軽い分析系のものをいくつか試作したい欲が湧いたところでDockerの機運が高まったので使い始めた。

まずはざっくりと勝手がわからないとつらい。
さらっと使ってみるには いまさら聞けないDocker入門 が良いと感じた。
ちょうど1.0がリリースされる前後に開始した連載で、1.0ベースの話が展開されている。
インストールから基本操作(pull, run, build, rm, rmi, exportあたり)は把握できた。

Dockerを触る前から不思議に感じていて、基本操作を試しているうちにより不思議に感じてきたのが「皆、コンテナ内で生成された重要データはどうやって永続化してるの」というあたりだった。
DockerといえばImmutable Infrastructureだ! 環境はバンバンぶっ壊すものだ! という先鋭的な向きもあるとは思うけど、そうは言っても全部ストレージはS3パイセンへ、というのはやっぱり辛い。
なんたって手元はさくらのクラウドにあるメモリ2GBのインスタンスで全部完結させたい。RDSもねぇ、ElastiCacheもねぇ、EC2を使う気これっぽっちもねぇ、という環境なのでこのへんは切実でもある。

まあきっと「処理主体としてのコンテナ」と「コンテナによって読み書きされるデータ」の標準的なレイアウトなんてのはなくて勝手ルールがいっぱいあるんだろうなーと、あまり期待しないところスタートであった。

多分世にあるDockerfileを読んでみるのが手っ取り早いんだろうと思って真っ先に読んだのは@futoaseさんの futoase/GrowthForecast (@futoaseさんには、某OSSへのパッチなど様々な場面でお世話になっている)。-v(--volume)でのボリューム割り当てによるコンテナ内部のデータの外部での永続化、-e(--env)での環境変数設定など、「あー、こういう流儀なのか」という納得が結構あった。

他にもいくつか読んでいくなかで、割と早い段階で持った疑問が
「自分は今、あんまりメモリが豊富でない環境でいくつかのサービスをホストしようとしてるんだけど、各コンテナでMySQL持つの?辛くね?」
ということだった。
まあ、Rubyのバージョン地獄をrvmで回避したり、「まだrvm使ってんの?普通rbenvだろ」って煽られたり、Node.js各バージョンをnodebrewで使い分けたりnpmで-gは滅多に使わなかったり、果ては「perlbrew使ってないの?マジで?」というご時世なイメージはあって。なんでもかんでも自家醸造の時代だよ、麺だって自作だよヌードルメーカーだよ!というのは分からなくもないんですけれど。

つまりライブラリ群の依存関係で深く傷つくぐらいなら色んなものをproject-wideに制限していこうという話で、そのイメージでいくと別段コンテナごとにMySQLサーバのプロセスを内部に抱えるのも違和感は少ないのかもしれない。

けれどまあまともに動かすならMySQLに最低100MBぐらいは持っていかれるでしょうと。ほほう、PHPですか、そのApacheはpreforkしてくださるんですか35MBメモリお召し上げですかと。

考えていくと、ここしばらくRaspberryPi(初期モデル、メインメモリはVRAM共有で256MB)環境でサーバをやり繰りしていた際についた貧乏症が出てきて「なるたけ共有で良い(こだわり設定でない)MySQLはひとつのコンテナに集約したいでしょう」 となる。

Dockerを使う上で、各アプリにぴったり合うミドルウェアをバージョンごと指定して確実に動かして快適性を得る、というのがモチベーションのそれなりに大きなエリアを占めてるはずなので、「じゃあこれPHPやめて書き直そう」とか「MySQLより小規模利用時にはメモリ消費の少ないデータストアに置き換えよう」とかは不毛。

かといってDockerのホスト側でMySQLを走らせるかというとそれは違うよなーと思ってた。

そんな時に眺めたのが sameersbn/redmine だった。Dockerfileを軽く眺めると、OSバージョンを固定(加えて、この場合は自分が使うのに最低限便利な構成にしたOSイメージを派生させてる)してあとは必要パッケージをいくつか追加してさっくりと完了しているように見える。
それでいてデータストアにはコンテナ内部のMySQLを使う方法(実際にはこれは古い利用方法で、後述のコンテナリンクが推奨される)、外部MySQLのアドレス/ポート/ID/PWをコンテナ起動時の環境変数で指定する方法、そしてコンテナリンク(--link)なる方法で別のコンテナとつないで使う方法が用意されてるらしかった。
まあ、もう少しちゃんと読むと 、この 初期化スクリプト がなかなかの規模でヒィとなったんだけど。

コンテナのリンクというキーワードまで到達したらもう少しで全体像が見えてくる感じがした。
公式ドキュメントの Linking Containers Together (ドキュメント自体は1.3のもののほうがかなり分かりやすいので概念理解向きだけど)を眺めて、これはリンクで参照される側とリンクする側の間でループバックインタフェースによる接続を張れるようにして、リンクで参照する側からは所定の環境変数を見ることで接続情報を把握して接続するらしいことがわかった。
もう10年以上、同サーバでPHP類とMySQLを同居させる時にはUnixDomainSocket経由でしか使ったことがなかったので、Dockerを最初にざっと眺めた時には「うーん全部TCP/IP経由かー」と思ったのだけど、この構造なら構成面の利便性がパフォーマンス差を上回りそうなので十分に納得いった。
3306あたりはiptablesで外向きを閉じておくかなーと思っていたのだけど、そもそもDockerホストの外部に対してポートを露出せずとも内部では通信できるという形なので、なかなか面白い。


ログは本番環境でも皆捨ててるのか(前述のRedmineでは、Redmine部分のログはVOLUMEで外部へ出せるようにしてた)、そもそもDockerベースのままで本番環境まで持っていかないのか、Docker Hubのどっかにはひたすらいろんなコンテナのログ出力部分をfluentdやらsyslog-ngやらへ流すようなDockerfile編集を行なってる職人がいるのか、全然分からなかったけれどこのへんは検討課題ということで。

現状のDockerfile+プリビルドされたコンテナをホストするDocker Hubによってユーザの手元に超手軽なパックとして実現(提供)されているものが
  • OSのバージョン固定
  • ミドルウェアのインストール
  • ミドルウェアの設定
  • ソフトウェアのインストール
  • ソフトウェアの設定
という「ready-to-start」な構成だとすると、
  • アクセス/オペレーションログ
  • ソフトウェア上で生成されたデータ(例えばWebコンソールから設定されてファイルに書きだされた設定情報、ユーザからアップロードされたデータやその他の経時生成されたデータ)
あたりを、場合によってはスナップショット込みでいい感じに制御して、あるホストが役目を終えようという時に他所のホストへと持って行きやすいようにする何か。
「大体の場合はこれに従っていればok」という感じのユニバーサルなんたらレイアウト的なサムシングがあるといいんだろうなーという小学生並みの感想を持った。
どういうものだろ。Dockerfile内でのVOLUME指定にカテゴリ(ログ、データ、設定など)を切っておいたうえで、ホスト上に展開する場合はホスト側からすぐに見える特定ディレクトリ以下にディレクトリを切って処理するとかかな。
こういう時にdocker exportしたら負けだと思ってる。

まあ初日だしね。最近の1.3とかのバージョンではいい感じにセマンティックになってるのかもしれない。
あと、コンテナリンクを含めてそれなりに複雑な構成を取ろうとすると、構成管理的な意味でChefとの関連というか連携があると概念的にはしっくり来るなーというのも感じた。こういう動きがChef Containerなんだろうか。出始めの時の記事を斜め読みした感じでは、Chef系で誰かがCook-bookをメンテする苦しみを味わってくれれば世の他の人は利益を享受できる格好だと認識してるのだけど、これをDockerでフリーダムなコンテナを作る系へと強引に接続してもあんまりうまく行くイメージがないので、きっと何か工夫があるんだろう。


さ、下回りの構成をいいかんじに現代化して試行錯誤しやすくなったところでアプリ作ろう。俺達のオープンデータ活用コンテストは、まだ始まったb(ry

2014年10月14日火曜日

VMware FusionでWindows8.1(Hyper-V対応)を使っていて時折IRQL_GT_AT_SYSTEM_SERVICEのループに陥る問題

手元、OS X 10.9.5上のVMware Fusion 6.0.4 + Windows 8.1 Proで時折発生します。

主に、http://msdn.microsoft.com/ja-jp/library/windows/apps/jj945426.aspx の手順でMac上のVMware Fusion上にてWindows Phone 8のエミュレータ環境を作っている人向けです。

なお、上記のような特殊な用途以外で、Hyper-Vゲストを有効にするメリットはほぼありません。動作速度的なメリットはないどころか、手元の調査ではVT-x/EPT有効&Hyper-V対応ゲスト化によって、大規模なC++コードのVSでのビルド速度が10%ほど低下しました(MSDNにもパフォーマンス落ちるよ!と書いてある通り)。



こういう画面(問題が発生したため、PCを再起動する必要があります。エラー情報を収集しています。自動的に再起動します。...詳細については、次のエラーをオンラインで検索してください: IRQL_GT_ZERO_AT_SYSTEM_SERVICE)が出てリブートの無限ループに陥ることがあります。

エラー情報収集→リブート→エラー情報収集→リブート→...を数回繰り返したあたりで心が折れそうになります。

 unsupportedなのだからしょうがない気もしますが、諦められないのでなんとかします。
手順はとても簡単です。

Settings→GeneralにてOSをHyper-V(unsupported)→Windows 8 x64へ変更して保存しましょう。

そしてもう一回起動すると普通に起動完了します。

その後はもう一度シャットダウンしてOSをHyper-V(unsupported)に戻せばokです。

なお、私はこれに気付かず一度VM環境を作り直しました。

Windows Phone 8端末の技適が通れば、Hyper-Vをわざわざ有効にする必要も減るのですけれどねっ。

こちらからは以上です。

2014年6月16日月曜日

「Windows Phone Storeの使用言語比率9位は日本語」ではありません

Windows Phone国内展開のカギは「Cortana」と「SIMロックフリー」 - 阿久津良和のWindows Weekly Report
という記事の間違いに気付いたのですが、twitterなどで指摘して訂正依頼するには話が少々ややこしかったのでblogにまとめました。

元記事はWindows StoreとWindows Phone Storeでのダウンロード動向をもとにした分析記事です。
このなかで
気になるのは9位にランクインしている「Japanese」。全体数から見ればわずかだが、国内向けのWindows Phone 7デバイスか、並行輸入などでWindows Phone 8デバイスを購入し、使用しているのかもしれない。
というくだりがあるのですが、これは誤りです。


2014年3月15日土曜日

PNaClとasm.jsの最近について喋ってきました(Code Lunch)

ある休日の昼下がり、@h13i32maru @iizukak 両氏のやっているCode Lunchというpodcastにお邪魔してきました。


収録機材はガチなマイク(すごい迫力)とMacのGarageBandでした

Code Lunch自体を簡単に紹介すると、Web周辺の技術について毎回トピックを決めて30-40分ほど喋るというもので、http://rebuild.fm/ にインスパイアされて始めたそうです。今回で4回目。

今回の内容は、次世代Web技術のひとつとして注目を集めつつあるPNaClとasm.jsについて、以前SlideShareで公開した社内ミーティング用資料


と、@mhidakaasm.jsとかPNaClとかLLVMに興味あったので調べて回ったら少しだけ理解できた話 を下敷きにしつつ、その後に調べを進めた結果見えてきたものなどを盛り込んでいます。

Vol.4 2014/03/15 PNaClとasm.js からお聞き頂けますので、通勤のお供などにいかがでしょうか。

2013年12月22日日曜日

OmniFocusとPivotalTrackerの連携でタスク管理が17倍効率化すると話題に

こんばんは。この記事はGTDアドベントカレンダー2013*0の12/22分の記事で、自分の中で話題沸騰中の案件です。設定方法直行はこちら

Pivotal Trackerは、「やることや、その優先度をどんどん組み換えつつ進行してプロジェクトの目的を達成するための管理ツール」です*1

ソフトウェア開発でありがちな
  • 開発期間が短い
  • 仕様が変わるのに開発期間は延びない
  • やることの順位付けが正しくできない
  • 誰に確認してもらえばいいのか明確でない
  • やってるうちにテンションが下がっていく
といった問題に対して「やりたいことを突き詰めていったら、そりゃ当初見えてなかった仕様が必要になるかもしれないし、優先度が変わってくるかもしれない。市場へ出してみてフィードバックが得られたら尚更。だけど時間は有限なのでそんなに無茶は効かないし、やむなく無茶するならするで最低限無茶をやってることだけは認識を持ちつつ納得して進めよう。いのちだいじに。」という進行をしたいプロジェクトに打ってつけのものです。
逆に、「新しい技術要素は無いし、仕様を変更することもまずないので、粛々とやろう。」という進行のプロジェクトにはあまり向きません。
興味を持ちましたか? 多分持ちましたよね。今なら60日間無料でフル機能を使えるトライアルがあるので、使ってみてください。Googleアカウントでログインしてすぐに使えます。OSSなど、タスクを全世界へ公開しても問題ないプロジェクトであれば全機能を無期限無料で利用できます。

Pivotal Trackerの初歩については技術評論社さんの「Smartphone Design」(2012年9月28日発売)に記事を書いたので、興味のあるかたはぜひどうぞ。思い返してみるとあの本(というかムックか)はVisual Studio方面の高橋忍さん、かれこれ10年ぐらい前に私がWin32 APIとC#を学ぶ際に一方的にお世話になった赤坂 玲音さん、コロプラの馬場さん、Simejiの矢野りんさんやadamrockerさん、先日テックヒルズで話す機会を頂いたAimingの牧野さんら、相当控えめに言っても「錚々たるメンバー」による作で、いろんなトピックをぱらぱらと読んで楽しめて役に立って1,580円(税込1,659円)は大変お得だと思います #sウテマ
ちなみにPivotal Trackerについては、1年経ってサービスのロゴと一部ログインUIこそ変わり検索など機能強化されました(そして執筆当時にはあった日本語処理上の不具合が解消されました)が、基本的な考え方や扱い方は執筆当時から全く変わっていません。

さて、本題です。

Pivotal Trackerは、各ストーリー(タスク)に細かな期日を割り当てないという特徴を持っています。あくまでも特定のスプリント内での処理をターゲットとし、また、柔軟にその処理順を変更することで細かなピボットを繰り返していくというコンセプトからすると、マイクロマネジメント的な過度の細分化と管理は不要ということでしょう。
いえ、正確には不要ではないのです。「自分のやることの期日は自分で管理できる人または集団のためのもの」ということです。純粋にプロジェクトをスクラムで進行する場合であれば、スクラム会議(朝や夕など定めたタイミングでの15分以内の日次ミーティング)を軸として解消するものといえます。
では、オープンソースのプロダクトや日次ミーティングを開催するのが難しい環境ではどうすればいいのか。
やはりプロジェクトメンバー各々が管理するしかありません

ここで大きな問題が生じます。タスクの多重管理です。
GTD運用に重要なポイントはいくつかありますが、それは機会を改めて書くことにして、今回着目したいのは
「要対応のタスクをGTDのツール(手帳でも、ソフトでも)上へ漏れ無く書き出す。そして優先度をつけ、実行フェーズでは脇目もふらずに処理する」
という性質です。このためには
  • Pivotal TrackerとGTDツールとの間でタスク情報を同期する(二重管理を極力避ける)
  • タスク管理に必要な操作量と複雑さを極力低減する(操作は少ないほうが良い)
ことが重要です(※人力で複雑なワークフローを回すと数十〜数百のタスクを処理しているうちにきっと漏れが出ます。漏れを出さないようにチェック回数を増やす策も一定有効ですが、明らかにストレス源となります)。

Pivotal TrackerはAPIが充実しており、外部ツールへと情報を取り出しやすいです。それを使った連携方法としてomnifocus.rb + omnifocus-pivotaltracker.rbが大変便利なので紹介します。OmniFocusでなくても勿論良いのですが、私がOmniFocusにべったり依存した生活を送っているので、OmniFocusベースの紹介です。


設定方法:
先に、Pivotal TrakerのAPIキーを発行します。
My Profileページへ行き、画面下部のAPI TOKEN部分で"CREATE NEW TOKEN"をクリックします。

図のようにトークンが発行されればokです。
続いて、Rubyで書かれたOmniFocusバインディングとPivotal Trackerプラグインをgemsからインストールします。
$ sudo gem install omnifocus omnifocus-pivotaltracker
$ of sync
ここまで実行すると、~/.omnifocus-pivotaltracker.ymlに設定ファイルのテンプレートが作成されます。このファイルに、Pivotal Trackerから発行したAPIキーを記入します。:user_name:には、プロジェクト内で利用している略名を指定しましょう。なお、omnifocus-pivotaltracker 1.1.0以上では、マルチアカウントをサポートしています。公式READMEにあるように
---
-
  :token: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
  :user_name: muo
-
  :token: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
  :user_name: KN
と複数のトークン設定を続けて書くことで、複数のアカウントの情報と同期できます。様々なプロジェクトで別々のアカウントを持っている場合などに大変便利です*2
トークンを設定したら、もう一度
$ of sync
を実行しましょう。OmniFocusのnerdというプロジェクトディレクトリ中にPivotal Trackerのプロジェクト群がインポートされたはずです*3

このような状態のプロジェクトは、OmniFocus上で

このように表示されます。運用ルールとして「期日入力はOmniFocus上でおこなう。ほかの一切のワークフロー処理はPivotal Tracker上で通常通りにおこなう」と定めるのが重要です。あとはcrontabで
*/10 * * * * of sync 1>/dev/null 2>&1
のように10分毎の実行を仕込んでおきましょう。Pivotal Tracker上で完了状態(Deliveredなど)へと変更したタスクは、10分以内に反映されることになります。あとは「完了タスク、ちゃんと完了チェック付けたっけ?」などは忘れてひたすらと実行フェーズを頑張ってください。
ちなみにOmniFocus上でタスクを完了状態にしても、Pivotal Tracker上で何らかの完了状態になるまでは次回同期時に状態が巻き戻されます。期日入力以外をOmniFocusでおこなう=ルールに違反したミスなので、早期に検出できて良いですね*4

Q. ところで17倍ってどういう意味?

A. 素数!*5

*0: こちら
*1: Pivotal...といえばリーン・スタートアップ(懐かしい)書籍中で技術協力としてPivotal Labsがクレジットされていたのを覚えている方もいらっしゃるでしょう。えっ、いない?そんなぁ…ともかく、その会社の製品(サービス)です。
*2: 別のアカウントで持っているプロジェクト名が同じだったらタスクが混ざってカオスなことになるので注意してください
*3: omnifocus.rbの現行版にはnon-ASCII文字コードの扱いに問題があり、日本語プロジェクト名を扱うと「同期するたびに空の同名プロジェクトが増える」という不具合があります。パッチは投げてあるので、取り込まれるのを待つか、日本語プロジェクト名を避けるか、手パッチで対応してください。
*4: OmniFocus側を状態の正とするのはイマイチ良い策といえません。Pivotal Tracker内には「ストーリーの結果をオーナーに確認してもらい、問題があれば差し戻してもらう」というワークフロー概念および「FeatureとChoreというストーリー種別」が存在しており、OmniFocusでシンプルにこれらをカバーするのが割と無理筋なためです。
*5: Effective Android 達人出版会版の36章参照

2013年11月14日木曜日

Android 4.4で加わったメディアAPIの進捗を確認する

本記事はTechBoosterさんによる Android 4.4 KitKat 冬コミ原稿リレーを開催 という企画の11/12の寄稿記事です。11/12の寄稿記事です。

日本の皆さん、こんばんは。頭のなかだけアメリカ西海岸に居ます。11/13の18時を回ったところです。夏時間終わったので日本からは-17時間です。日本の皆さんは既に13日のとみーさんの記事をご覧頂いているかと思います。素晴らしいデザイン系まとめですね。恐縮してしまいます。それでは12日の記事です。

Android 4.4では、マルチメディア機能が大きく強化されました。動画の再生中に解像度変更を伴う動的なストリーム変更ができるようになったり、HTTP越しのライブストリーミング機能が強化されていたりします。



そんな中で今回主に扱うのは、AV同期のためのオーディオタイムスタンプ取得機能です。
http://developer.android.com/about/versions/kitkat.html では
Audio timestamps for improved AV sync
The audio framework can now report presentation timestamps from the audio output HAL to applications, for better audio-video synchronization. Audio timestamps let your app determine when a specific audio frame will be (or was) presented off-device to the user; you can use the timestamp information to more accurately synchronize audio with video frames.
と紹介されています。そのまま訳すと以下の通りです。

AV同期のためのオーディオタイムスタンプ取得機能
オーディオフレームワークにおいて、より良い映像/音声同期のために現在の再生タイムスタンプをオーディオ出力HAL(ハードウェア抽象化レイヤ) からアプリケーションに対してレポートできるようになりました。オーディオタイムスタンプを利用することで、特定のオーディオフレームがどのタイミングで端末からユーザに対して発せられたか(または、られるか)を知ることができます。この情報を利用して、映像と音声をより良く同期させることが可能です。
= 機能の扱い方 =
扱い方の概要はhttp://developer.android.com/about/versions/android-4.4.html#AudioTimestampに記載されています。
割と簡単なAPIで、予めタイムスタンプ取得用に作成したandroid.media.AudioTimestampのインスタンスをAudioTrack#getTimestamp(android.media.AudioTimestamp)として渡すことで、ナノ秒単位のモノトニックなタイムスタンプを取得できます。
モノトニックなタイムスタンプは、それ自体では少々扱いづらいため、オーディオの再生を開始するタイミングなどで記録したSystem#nanoTime()との比較にて処理するのが良いでしょう。

実際に特性を調査していく前に、android.media.AudioTimestampの定義をもう少々読んでみましょう。nanoTimeがナノ秒単位のタイムスタンプを表すのは明らかですが、framePositionは何でしょう。
これは、指定AudioTrackインスタンスの持つバッファ中での現在再生位置を示します。結果、バッファを使い切った際の値更新挙動に差が生じます。例えば

  • AudioTrackのバッファサイズを小さめに確保する
  • AudioTrack#setNotificationMarkerPosition(int)でAudioTrackのバッファサイズを超える位置を指定する
  • onMarkerReached()でのコールバックにてAudioTrack#stop()を呼び出し、再生を停止する
とすると、発音がAudioTrackのバッファサイズ相当の箇所で停止すると共にframePositionの値も更新が止まるのに対し、nanoTimeの値は増加し続けます。このため、AudioTimestampを利用したアプリケーションの開発時には「nanoTimeの値が更新されている=発音が続いている」わけではないことに注意が必要です。


さて、この機能があると何が嬉しいのか、といった点について考えてみます。


= ほいで、ゲーム開発などに使えそう?→調べてみる =

タイムスタンプが取得できたところで、有効に利用できなければ意味が薄いです。今回は、ゲーム開発などのシーンにおいて活躍してくれそうな機能か否かを検討するべく、いくつかの負荷条件下での比較をおこなってみました。

使うサウンドは、先日のPlaygroundハッカソンで@mhidakaさんが作ってくれた「進捗だめです.mp3」(2216ミリ秒程度)です。進捗だめでした。
オーディオ再生、タイムスタンプ取得周辺のスニペットは以下のとおりです。


今回は単純化のために、AudioTrackをSTREAMモードで利用し、事前にbyte列へ読み込んだwavデータ(lame --decodeにてMP3からデコードしたリニアPCM; 16bit-mono)を投入しています。最後のサンプルまで再生されたらリスナでイベントを受け取り、AudioTrackの停止をおこないます。これに割と雑な定期タイマーを組み合わせて(なるべく)定期的にログを取得します。

これをNexus 5で実行すると、以下のようにタイムスタンプが更新されていきます。




なお、AOSPベースの4.4を焼いたGalaxy Nexusではタイムスタンプ取得機能がサポートされておらず、AudioTrack#getTimestamp(AudioTimestamp)がfalseを返します。これがハードウェア上の制約によるものか、ドライバの作り込みによって回避できるものかについては調べられていません。


これを単純に4回計測してデータ処理した結果は以下のとおりです(Nexus 5, 4.4/KRT16M, 単位はミリ秒, 以下同)。

平均サンプリング成功回数
85
最大値(最も性能が良い)
-1
最小値(最も性能が悪い)
-23
最大値-最小値の平均
19.75
中央値の平均
-12
すごい! 優秀ですね。特に最大値と最小値の差が22ms程度に収まっているのはなかなか良いです。そして、今回の計測系では理想的に実行した場合85回程度の処理がおこなわれることも分かります。

この結果が理想的なパターンでのものですが、実際のAndroid端末では

  • 同プロセス内で複数のスレッドが実行されている
  • 他のプロセスにて処理が実行されている

のが普通です。これらがどのような特性変化をもたらすのか、もう少し調べてみましょう。

まずはプロセス内に重い処理(ゲーム実行本体などを想定)を抱える場合を想定し、以下のコードを追加してみます。

プロセス内(Dalvik)に重い処理がある場合
平均サンプリング成功回数
60
最大値(最も性能が良い)
0
最小値(最も性能が悪い)
-21
最大値-最小値の平均
19
中央値の平均
-10


スレッド間のスイッチが発生するためかサンプリング回数は低下していますが、基本的には最初の結果と同様の傾向を示しています。

それでは、外部に重い処理を追加してみます。

= 外部の負荷源を導入する =

今回は手軽に負荷を発生させるために、どこのご家庭にもあるLuaのJITコンパイル対応版実装のLuajitを利用しました。


1|shell@hammerhead:/data/local/tmp $ ./luajit -e "i = 1 while true do i = i + 1 end" &
このようにして無限ループを作り、バックグラウンドで6プロセス走らせます。
CPUをぶん回してますね。この状態でホーム画面などを操作するとかなり引っかかりがあり、使い勝手がかなり落ちています。
「あー端末重いわー、つらいわー、再起動したいわー」という状態よりも更に行き過ぎている感がしますが、古めの端末ではよく見かける状況なので気にせず進みます。

プロセス外に重い処理がある場合
平均サンプリング成功回数
56.75
最大値(最も性能が良い)
0
最小値(最も性能が悪い)
-50
最大値-最小値の平均
32
中央値の平均
-11.75


明らかにオーディオここで"最大値-最小値の平均"に注目してください。さきのふたつの結果よりも明らかに増えています。一方で"中央値の平均"はあまり変わっていません。これは「概ね良い結果を出しているが、時折大きく外れた結果につながる」ことを意味します。
ただし、これはAudioTrack#getTimestamp(AudioTimestamp)の性能が悪いことを意味するものではありません。「Androidでは、アクティブなアプリケーション以外がCPUを一定以上消費している際に、アクティブなアプリケーションの反応も一定以上悪化するケースが多い」ことを示すのみです。とはいえ、これ自体がゲームなどの実行環境として不向きには違いありませんが…。
ちなみに定性的なものですが、計測中に可聴ノイズは特にありませんでした。オーディオを出力するという基本機能はしっかりと維持しています。

さて、最後にプロセス内が重くプロセス外も重いという二重苦状態も試してみます。

プロセス外に重い処理があり、プロセス内(Dalvik)にも重いスレッドがある場合
平均サンプリング成功回数
4.25
最大値(最も性能が良い)
-1
最小値(最も性能が悪い)
-100
最大値-最小値の平均
33
中央値の平均
-12.25


サンプリング成功回数の激減からお察し下さいという感じがしていますが、かなり重い状態です。ここで初めて計測中に音が飛ぶ形の可聴ノイズが数回確認できました。明らかにCPU時間不足です。今回の実装においてユーザ側からは全てのオーディオサンプルを事前にプラットフォームへ渡してあり、バッファキュー追加処理類はおこなっていないため、この挙動は少々不可解です。Androidプラットフォームの実装に優先度制御の甘い箇所があるのかもしれません。
ここで特筆すべきは"最大値-最小値の平均"と"中央値の平均"が共にひとつ前のものとほとんど変わらないことです。これは「CPU性能が足りず音飛びが発生するような環境においても、一定以上の精度でタイムスタンプ取得が可能」という特性を意味します。表中で最も性能の悪いケースで100ミリ秒程度の差が生じているのは、おそらくタイムスタンプ取得後に比較用のSystem#nanoTime()実行までの間にDalvik内のスレッド切り替えがおこなわれたなどの事情によるものでしょう。

= まとめ =
以上の4つの計測結果をまとめると以下のようになります。
状況
理想的
内部高負荷
外部高負荷
内外高負荷
平均サンプリング成功回数
85
60
56.75
4.25
最大値(最も性能が良い)
-1
0
0
-1
最小値(最も性能が悪い)
-23
-21
-50
-100
最大値-最小値の平均
19.75
19
32
33
中央値の平均
-12
-10
-11.75
-12.25


  • 基本的に、取得できるタイムスタンプは信用していい
  • アプリ内・アプリ外が共に高負荷である場合、Androidのサウンド出力自体はまだ甘いらしく、アプリ側での補正が必要
  • 新しいアーキテクチャの低スペック端末などで音飛び/フレーム飛びを補正する上での手軽な策として割と使えそう

= NDKに恩恵は…? =
NDKでバリバリ書かれたものについてはどうなるんだろう、何か効果あるのかな?ということも気になりますね。
ありません。

そもそもJava側のAudioTrack専用の機能なので、Low Latency Audio出力などを視野に入れたOpenSL|ESでの実装系では提供されていません(Android NDK r9b時点)。
そもそも、NDKベースで開発する場合には、ある程度自由なスレッド優先度制御が元々可能であること、バッファキューを利用する場合にはそれなりの精度(概ね10ms未満程度)で現在処理中のバッファ位置を推測できることから、あまり使い道が大きいとも言えないのが実際のところです。
とはいえ「バッファキューへ複数のバッファを積んでいる状態でどこまでが実際にスピーカなどから出力されたか」という点は従来提供されてきた機能では推測することしかできなかったため、将来的にNDKに類似機能が提供されると一定のユーザ体感改善へ寄与するものと考えられます。

以上、「AV同期のためのオーディオタイムスタンプ取得機能」について述べました。

以下は蛇足です。

まず、本機能が実装された背景を少々推測して書いてみます。
まず、端末性能の全体的な向上が挙げられます。これによりDSPにデータを流すだけでなく、一定はOSでの処理を介在させてもユーザ体感を損なわない下地ができてきたのでしょう。また、OS自体の最適化により余裕が生まれたということも考えられます。特にサウンドまわりは、4.1からだいぶ良くなってきたので、サウンドチップ周辺からユーザランドへ情報を戻すような機能を実装しやすくなったのではないかと考えられます。

「動画の再生中に解像度変更を伴う動的なストリーム変更ができるようになった」件
そういえば、前半で書いていた本件について少々触れておきます。
本機能を調べ始めた@muo_jp氏(28歳)によると
「この機能を使うにあたってはプラットフォームにサポート有無を問い合わせる必要があります。当初エミュレータで調べていたところ、以下のような調査コードが
String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
  MediaCodecInfo.CodecCapabilities ccp = codecInfo.
    getCapabilitiesForType(types[j]);
  boolean isAdaptivePlaybackSupported = ccp.isFeatureSupported(
    MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
  String supportStatus = "adaptive playback is " +
    (!isAdaptivePlaybackSupported ? "not " : "") +
    "supported for " + types[j];
  Log.d(TAG, supportStatus);
}
平然とfalseを返してきました。おっふ…。さすがにエミュレータだとメディア系が諸々弱そうだから使えないか。

それではAOSPベースのGalaxyNexusではどうかなーと考えてhttp://blog.sola-dolphin-1.net/archives/4566598.html で配布されていたものを利用してみたのですが同じくfalse。
そうだよねー、メディア系は割とAOSP実装と商用実装で各種コーデックが異なるとかあるよねー。
こういうのはちゃんと商用実装で確認しよう的なやつだ。

ということでNexus 5(4.4, KRT16M)で試した結果…
adaptive playback is not supported for video/mp4v-es
adaptive playback is not supported for video/mp4v-es
adaptive playback is not supported for video/3gpp
adaptive playback is not supported for video/avc
adaptive playback is not supported for video/x-vnd.on2.vp8
adaptive playback is not supported for audio/3gpp
adaptive playback is not supported for audio/mp4a-latm
adaptive playback is not supported for audio/amr-wb
adaptive playback is not supported for audio/flac
adaptive playback is not supported for video/x-vnd.on2.vp8
adaptive playback is not supported for audio/mp4a-latm
最新開発者向けフラッグシップ端末も平然とfalseを返してきました。oh... 詰んだオワタ、まだ人類には早すぎた。

Xperia系とか、メディア周りの実装がしっかりした端末が4.4にちゃんと対応しないとこれ無理なんでないかという感がしている」とのことです。

2013年7月25日木曜日

Android 4.3来たにゃー 気になる点メモと他雑感

Android 4.3がついに。 http://developer.android.com/about/versions/android-4.3.html

事前情報からあまり外れないけど、やっぱり気になるところはある。


  • restricted profile environmentというのが増えてて、例えば標準ブラウザにすらアクセス出来ないモードとかも出来るようになった(多分iOS 6でのguided accessと同じで、店頭端末用とか企業利用向け及びペアレンタルコントロール用だよね)
  • HTTP経由のメディアストリーミングで、データの暗号化を行いやすいようになった(4.3主流になる頃≒2年後?には使えるかな)
  • DRMのための仕組みが、10バージョンぶりぐらいに強化された。 http://developer.android.com/reference/android/media/MediaDrm.html (同上)
  • OpenGL ESのサーフェスを動画へエンコード出来る機能をサポート(検証系に嬉しい感じする)
  • OpenGL ESで、テクスチャ圧縮を結構まともにサポート。これはGoogle Playでの配布時にMultiple-APKsの条件へ指定出来るので「端末ごとに割と最適なフォーマットで、配布時に重複の無駄なく」ということを実現しやすい。関連はここ http://developer.android.com/guide/topics/graphics/opengl.html#textures
  • 普通のBitmapに、mipmappingが適用出来るようになった。CPU遅めな端末で嬉しいケースあるかもだけど、動的なスケーリングを行うケースってそんなにあるのかな? res/以下のプロファイル切り分けを直接代替するものではないので利用には注意が必要そう
  • 画面回転時のエフェクトが指定出来るようになった(iOSへ少々追いついた感じある)
  • UI操作の自動テストを支援するAPIが追加された。backとかhomeキーのマッピングもあったりと、これは大量の画面がある場合の遷移チェックなど、開発に役立つ感じする http://developer.android.com/reference/android/app/UiAutomation.html
  • アプリ側からsystraceの有効/無効を指定出来るようになった。printfデバッグ仕込むノリでsystrace切り替えられるのは、パフォーマンスしっかり見つつアプリ改善する上で嬉しい
  • "Android Key Store"という、アプリごとに個別の秘密鍵ストアを持てるようになった。これ応用してiOS的なキーチェーン実現出来るのかな?アプリのアンインストール&再インストール時のデータ揮発性など要調査
  • ハードウェアでの秘密鍵生成器/保持器サポート。これは未来が来てる感じ。OS自体の対応はスタート地点でデバイス側対応が重要になるけど、諸々のセキュアエレメントを秘密鍵ストアとして駆動出来るようになるとDRM捗る


----

変更を分類して所感とともにもうちょいメモ。

  • 今日から開発に嬉しい系
    • UI操作の自動テスト(エミュレータ組み合わせてCIが非常に捗る)
    • systraceのアプリ側からのon/off
    • OpenGL ESのサーフェスをがっつり動画に記録して端末動作検証など(これ自体のパフォーマンスインパクトがそれなりにありそうなので、開発用のハイエンド機以外では逆に厳しい感じもする)
  • 新しい仕組みでデモ作ったりすると未来見えて嬉しい系
    • ハイエンド端末でのOpenGL ES 3.0デモ。Galaxy S4とかで扱うと「えっ、モバイルでここまで出来んのSUGEEE!」な感じ出せてよさげ。S4のbig.LITTLE対応Google版はよ(※big.LITTLEは浪漫)
    • Bitmapをうまく使ったデモとか、案外映えるのかもしれない。mipmappingが有効な 奥行きのあるものを、ということになるけど結構鬼門な気がするなー
    • セキュリティ系の技術デモとしては、USBでつないだRSAドングルあたりをLinuxのドライバきちんとつなぎこんでAndroid(AOSP)に手を加えて認識させ、かなり安全な鍵管理を実現(ただし端末盗難に弱いというオチ付きで)という感じのネタやりやすいかも
  • すぐに反映出来て、対応端末ではちょっと未来嬉しい系
    • 画面回転時のエフェクトとか、ゲームではちゃんとしていたほうが嬉しい場面あるので、少数ユーザでも4.3な人々がヾ(*´∀`*)ノキャッキャ出来るものを早めに出せると良いのかも
    • 圧縮テクスチャの強化もこのへん。x86なAndroidが多少なりとも伸びてくるであろうことを考えると、結局Multiple-APKsを使いこなしていかないと配布サイズ的に厳しい局面増えてくる。早めに実戦投入しておくと、シェア伸びてきた際に慌てなくて済むという意味でも早期に開発成果物としてのapk群をほどよく生成出来るパイプラインを作り込むのは大事な感じ
  • 2年後ぐらい、バージョンシェアが十分に伸びてくると新サービスへ積極的に使えて嬉しい系
    • メディアストリーミングにおける暗号化サポート
    • DRMのフレームワーク強化(DRM対象にしたいメディアって、やはり対象のユーザベースが広くないとどうにも話にならないケースが多い。面展開のメリットが出てくるまでは準備期間と位置づける)


一番お金になりそうなエリアは、一番バージョンシェアが必要というね。そらそうですね。「過去18ヶ月以内に発売された端末については、端末メーカーをGoogleが資金面でも技術面でも徹底サポートして、むこう6ヶ月以内に4.3対応アップデートを配信する」ぐらいの男気対応があれば、きっと移行は否応なしに進むんですけどね。あっ、アップデート保証プログラム…(察し)。厳しいですね。こういうエリアはGoogle Play Servicesでカバーする流れになっているので、コンテンツ保護系はPlay側の強化と共に歩んでいくことになるんでしょう多分。

蛇足ですが全体的にメディアフォーマットとしてMP4を標準と扱ってる節があって、WebMどこ行ったの感がしてますが まあChromeからもサポート外すと言いつつずっと乗ってますし今のところこんなもんでしょう。デファクト兼デジュールスタンダード強し。

超主観まとめですが、間違いなどあれば指摘頂けると幸いです。