2013年10月31日木曜日

Androidアプリ開発:OpenGL ESの実装について気になること

AndroidのOpenGLサンプルをちょこちょこ動かしてみてて、ちょっと気になったことがでてきました。Androidアプリの動作確認にはNexus7(初代)を使っています。動きが早くて、デバッグも楽なんですよね。
ところが先週、Nexus7においてOpenGLの動きに独特な箇所があることがわかりました。(GoogleによるAndroid自体の問題なのか、Nexus7に使われているnVidia Tegra3におけるビデオドライバの問題なのかははっきりしませんが)さらに別の、もっと重傷な問題が見つかりました。それは影の描画にNexus7とその他(Galaxy S2)で差がでる場合がでました。

同じアプリをまずNexus7で動かしたものです。

どうみても影の描画が変です。(影が横縞になって、透けてしまってます。)ちなみに同じアプリをGalaxy S2で動かすと以下の様になります。
普通に影が描写されます。

なぜNexus7でこうなるのかまだわかってません。Nexus7のGPUオプションがいくつかあるので色々条件を変えてみましたが、影の描画の異常は変化ありませんでした。当初はGPUアクセラレーション独特な問題に見えたんですが、そうじゃないようです。もう少しプリミティブなOpenGLのプログラム(関数)の動きから比較してみないといけないかもしれません。

2013年10月22日火曜日

Androidアプリ開発:テクスチャの勉強

AndroidではグラフィックスにOpenGL ESが実装されています。OpenGLは以前触っていたんで、個々の関数については「昔、聞いたことあるな~」と思いながら、サンプルを打ち込んでみて思い出しながら作業を進めます。
pngのbitmapをテクスチャとして貼るサンプルを作ってみましたが、これがどうしてもうまくいきません。テクスチャが本来表示されるはずのところが「真っ黒」になります。最初は光源の設定か、あるいはテクスチャを張るポリゴンの法線ベクトルの問題か?と思ったんですが、サンプルはそのあたりはデフォルトにまかせており、特におかしなことはしていません。
最初、bitmapを自分で用意したとき、適当なサイズで作ってしまいましたが、OpenGL ESでは2のべき乗でないといけない制限があるとのこと。そこで512x512で作り直しましたが、状況は変わらず。(大昔のOpenGLではそんな制限がありましたが、だいぶ前に自由になっていました。OpenGL ESは組込み用なので、そのあたりメモリの制限があるんでしょうね。)

さんざん悩んで、google先生にどう質問したらいいか何度か試しているうちに解決法がのっているサイトを見つけました。2012年7月くらいのAndroidの記事で、bitmapなどのresourceを保持するフォルダに、res/drawable-tvdpiというものができたとのこと。これまでアイコンのサイズを3通りくらい保持するためにldpi/mdpi/hdpiとかあったんですが、これら以外に「抽象ドット密度」というものができて、これを画像表示(テクスチャも)に使うとのこと。いやらしいことに、デフォルトでは作成されないフォルダ、res/drawable-tvdpiというフォルダに画像を保存しておくのが前提なんだそうです。そして別のサイトの情報で、さらにやっかいなことに今回最初にデバッグに使ったのがNexus7なのですが、この端末がtvdpiを実装されているtvdpi端末(表現が正しいかわかりませんが・・・)であり、ここに該当する画像ファイルがないと、hdpiにあるファイルを「自動でリサイズ」するとのこと。つまり、2のべき乗で作成しておいても勝手にリサイズされてしまい、それがくずれてしまって表示がされなくなってしまいます。(試しに別のAndroid端末をつないでみたら、表示されました)

解決法としては、res/drawable-tvdpiに2のべき乗の画像ファイルを保存しておくことになります。これでどの端末でも同じように表示されるようになります。
ただこの情報が書いてあったサイトでは、今度Android4.3ではOpenGL ES 3.0がサポートになり、テクスチャ画像の2のべき乗の制限がなくなるという情報がありました。自分のNexus7はAndroid4.3になっていますが、テクスチャ画像は2のべき乗でないとだめでした。まだダメなのかもしれません。

2013年10月18日金曜日

githubでEclipseプロジェクト(Android)の管理

gitの復習を兼ねてgithubの使い方もわかりました。さて次にコマンドラインではなくGUI、特にeclipseでgit(github)を操作する方法を調べてみます。どんなプロジェクトタイプでも同じでしょうが、特にAndroidプロジェクトのgitでのバージョン管理、複数PCでの開発環境構築について調べてみました。(eclipseにはEGitが入れてあります)

今回も結構手間取りました。ネットをググってみると似たような情報は結構あるんですが、いずれも複数のPCでの開発環境を構築するには足りないというか、部分的な解説しか見つからなかったんです。よくあるのが、
  • githubに既に存在しているリポジトリをeclipseで取得(しかもAndroidじゃない)
  • githubにeclipseからAndroidプロジェクトを登録する。但し他のPCでそれをどう使ったらいいかがない。
です。2つ組み合わせればよさそうな気がしますが、問題なのはeclipseのAndroidプロジェクトの場合、プロジェクト内のjava関係のライブラリ等をリポジトリに入れるわけにはいかないので、それを別のPCで開発環境の構築までどうやって持っていったらいいのかがわかりませんでした。
結局、かなり失敗してやり直しましたが(2,3回github上のリポジトリも壊してしまったw)なんとかできたようです。いかに手順を示します。

1.Mac上のeclipseでAndroidプロジェクトを作成し、githubに登録する
2.Windows上のeclipseにgithubからプロジェクトを取得、開発用のAndroidプロジェクトを構築する

大雑把にいって、この2つです。この後、実際に相互にファイルを編集してみて、相手に変更が反映されるのを確認しました。

1.Mac上のeclipseでAndroidプロジェクトを作成し、githubに登録する
まずMac上でAndroidプロジェクトを作成、一応動くことを確認しました。さてこれをgithubに登録するんですが、特にこれは問題なくできました。eclipseのExplorer上のプロジェクトを右クリック→Team→Share Project でダイアログが表示され、そこにリポジトリをどこに置くか聞いてきます。ここで、Mac(或いはwindows)のローカルにリポジトリが作られますが、デフォルトは$HOME/git/以下にどちらも作られました。(この時、プロジェクトは$HOME/workspaceから、$HOME/git/<リポジトリ名>に移動するのに注意。)次に、バージョン管理するファイルを選択、Addしてやります。(Androidのjarとかは当然入れません)そして「最初の」commitをしてやります。(Team→Shareでは、gitリポジトリの入れ物をローカルに作成しただけで、まだ何も入っていない状態です。)そしてPushを選択、githubのリポジトリ(事前にリポジトリの入れ物だけはgithubのHPで作っておきます)を入力して、github上にデータを送ります。ちょっと注意が必要なのが、最初のpushの時何をリポジトリに登録するかです。初めて作ったばかりならmasterのbranchしかないと思っていたら、HEADというのも表示されます。何でこれが表示されるのかわかりませんが、うっかり最初にHEADを選択してリポジトリに登録したら、プロジェクトの登録はできたのですがbranch名がHEADになってしまい、どうやってもmasterにできず結局github上でリポジトリを削除して作り直しました。
別のHPにも情報を後で見つけましたが、

  mastre[branch] (refs/heads/master) → refs/heads/master

が正解のようです。あと、議論が分かれるところですがeclipseのプロジェクトでは、".project", ".classpath"というような設定ファイルがあります。デフォルトではこれもリポジトリに登録する設定になっていますが、これはバージョン管理するな/しろ、の意見が半々でした。どちらがいいかはeclipseの環境にどんなplug-inを入れているかにもよるらしく、一律にコレというのはありませんでした。各自の開発環境で判断するしかありません。
(注:.project内にはプロジェクト名=フォルダ名が格納されています。そのため、gitに.projectがないと2項の方法でAndroidプロジェクトを作成するときプロジェクト名として起動時のクラス名が使われます。デフォルトでは新規プロジェクトを作成する時、プロジェクト名がそのまま起動時のクラス名になるので大抵の場合問題がでないんですが、わざとプロジェクト名と起動時のクラス名を変えている場合に問題がでます。)

追記:
gen/R.javaも管理を外しました。branchを変更したり、古いバージョンに戻す時にエラーがでます。プリコンパイル・ファイルなのでRun/Debug時に生成されますから、元のファイル(res/以下)さえ管理対象に入っていれば大丈夫でしう。

とりあえずこれで、このMacだけで開発を進めていく分にはバージョン管理ができるようになりました。

更に追記:
最初にリポジトリにpushしたPCでは、どうもうまくpullができません。色々やってみたのですが、eclipseのGUIでやるとconfigに以下のものが記録されないようです。(hogeは適当に変換してください)

branch.master.remote=origin
branch.master.merge=refs/heads/master
branch.master.rebase=true

remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
remote.origin.url=https://github.com/hogehoge/hogehoge.git

Repository Explorerでプロジェクトを右クリックして、Propertiesを選ぶとwindowがでてくるので、そこに追加で指定してやります。
どういうわけかeclipseから最初にリポジトリに登録してやるとこの5項目が登録されてなくてpullがうまくいきません。(つまりpushしかできない)
登録したら一度削除して、githubから取り出してやったほうが、このあたりの設定をしてくれて楽かもしれません。

2.Windows上のeclipseにgithubからプロジェクトを取得、開発用のAndroidプロジェクトを構築する
さて次が一番悩んだところです。リポジトリに登録時、Androidプロジェクトだという情報は入っていません。何も考えずにeclipseでgithubからImportしてしまうと、単なるファイルの塊がeclipseのExplorer上にできるだけです。もちろんその状態でファイルの編集/更新を相手に伝えることはできるんですが、Windows上でAndroid実機での動作確認ができない状態です。(Androidプロジェクトになってないから当たり前です)さんざん悩みましたが、以下の手順でいいようです。(一発でやる方法もありますが、あえて一つずつやっていきます。

(1)eclipseのGit Repositoryにgithubからcloneを作る
eclipseで、Window → Show View からGit Repositoryを表示する。そこで右クリック、あるいはボタンで目的のAndroidプロジェクトのcloneを作成する。

OKすると、master branchを取得したとダイアログがでてきます。(今回はmasterしかないし、たいていはmasterを取得するでしょうが、場合によっては別のbranchを取得する場合もあるでしょう)
これで$HOME/git/以下にローカルなリポジトリが作成されます。


次に、eclipseのExplorer上で右クリックでImport(或いは、FileメニューでImport)します。Sourceは、Git/Projects from Gitを選択します。EGitが入っていると、Local、Urlの2つが選べます。Urlは先ほどのGit Repositoryにcloneを作成する手順に入っていきます。Localはすでに存在するgitリポジトリを指定します。今回は先に作成したcloneとなります。そして以下の画面が表示されます。
ここで3つの選択がありますが、真ん中の"Use the New Project wizard"を選択します。次に、"Android Project from Existing Code"を選択します。

これで既存のコードでAndroidプロジェクトを作成することをeclipseに伝えることになります。次のダイアログで、どこにそのコードがあるのか指定する画面になりますが、$HOME/git/以下に1.で登録したプロジェクトのフォルダが存在しているはずなのでそれを選択してやります。これでOKです。

注:
先に、".project"ファイルをバージョン管理に入れるかという問題がありましたが、どうもこれが入っていないとこの時点で指定したフォルダがeclipseのプロジェクトと認識されないようです。実はこのあたりどう解決すべきか正解が自分にもわかりません。個々の開発環境に応じて考えてください。

また、この手順でWindows上にプロジェクトを作成すると、ちょっと通常のプロジェクトとはファイルの位置が異なります。

・通常のeclipseプロジェクトのファイルの位置
       $HOME/workspace/<プロジェクト名>

・今回のgit管理下のプロジェクトのファイルの位置
       $HOME/git/<リポジトリ名>/<プロジェクト名>

eclipseのExplorer画面上は同じように見えるので、ちょっと注意が必要です。(eclipseで操作する上では何も差がありません)

さて結果としてWindows上の画面は以下の様になります。
Package Explorer, Git Repositoriesの両方に同じ名前のフォルダがありますが、実体は同じものです。

2013年10月16日水曜日

githubを使ってみる(具体例)

以前、家庭内の複数のPCでgit repositoryを使う方法について書きました。しかし、いざやってみると、いちいちサーバーを起動しておかないといけないのが面倒くさい。(^^ゞ
色々なメモがき、下書きは既にgoogle drive(google doc)により複数のPCで作業してきましたが、(これもgoogle driveができるまでは家庭内NAS使ったり、USBで持ち歩いたりしてたんだよね〜、便利な世の中になったもんだ)プログラム自体も複数のPCでやり取りしたくなったのと、バージョン管理(特に過去の記録や、複数のブランチを持ちたい。一々プロジェクト名を作り直したくない。)をしたくなったので、githubを使ってみることにしました。(単に複数のPCでプログラムを持ち歩くだけならgoogle driveでもいいんですが、プログラムを作っていく途中で色々な機能を作りこんでいく途中課程を後で見たい場合、これまで一々プロジェクト名を少し変えて新規作成したりしてました。でも本来、バージョン管理でやった方がスマートですよね。)

さてググってみると、色々な使用例の説明がありましたが、どうも具体性に欠けているものが多く、調べ直しが多かったので個々のコマンドの説明を付加しながら、あらためてメモを残しておきます。(注:githubのサービス自体、まだ完成しているわけではなく、UIなんか変わっているせいもあるので、一応SSのっけときますが、何時変わるかわかりません。そのため、何の操作をしているかの説明を付加しておきます。)

1.githubにアカウントを作成する
まずgithubにアカウントを作成します。github.comのTopにいくと昔はアカウント作成のボタンがあったらしいんですが、2013/10/16時点では特にボタンはなく、画面にいきなり欲しいアカウント名、連絡用e-mailアドレス、パスワードを入力し、'sign up'すればアカウントを作成できて、e-mailがきます。
現状では、一般公開プロジェクトを作成するだけならフリーです。
自分のページは、https://github.com/(アカウント名)になります。ブラウザでいきなりURLを見ることができます。(但し、その時はread-onlyになります。データを取得するだけならこれでいいんですが、管理等したい場合はそこでログイン('sign in')してやる必要があります。)

2.リポジトリを作成する
上記、URLでリポジトリを作成します。(注:windows, Macには専用のツールがありますが、最新OSでないといけなかったり、更新が早そうだったのと、Linux用がない?という状態だったので、ブラウザだけでやりました。)
ブラウザのUIがググった先のページと違いましたが、repository操作のタブを選んで、Newすることになります。(ちょっと抽象的ですみませんが、下手にSS貼ってもすぐ変わりそうなので、言葉で説明します)説明用に、「git-hiro」としておきます。

ちなみに、ここで作成したリポジトリのgitコマンドでアクセスするときのURLは以下になります。
(httpsアクセスの場合)https://github.com/(アカウント名)/git-hiro.git
(gitプロトコルの場合)git@github.com:(アカウント名)/git-hiro.git
なお、この状態ではgithubにリポジトリが作成されただけで、当然中身は空っぽです。

3.PC上で最初のデータを作成してgithubに登録する
先ほど専用ツールは使わないと言いましたが、windowsの場合だけはgitコマンドを持っていないので、cygwinを入れるなりしない限り専用ツールが必要になります。(eclipseにplug-inがあるらしいんですが、それはまた別の機会に調べます)元々、gitってGUIがほとんどなかった気がします。なんか、最初に作ったLinusの方針(とにかく早く機能するものが必要だったので、UIなんかなくてコマンド入力でいいから作った?)からか、Linuxで動くGUIツールを見たことがありません。そこで、以下にMacで作業したときのコマンドを載せておきます。(Macでは端末からLinuxと同じようにコマンド入力でやりました。自分も結構、こちらの方が好きです。)

$  mkdir git-hiro  // 作業用のディレクトリを作成します
$  cd git-hiro/   // そこに移動します
$  git init     // gitの初期化をします(.gitディレクトリが作成されます)

最初のディレクトリは特にgithub上に作成したリポジトリ名と同じでなくてもいいと思うんですが、その方がわかりやすいので同じにしています。

$  git config --global user.name "hogehoge"
$  git config --global user.email "hogehoge@gmail.com"

一応、当該リポジトリを操作するユーザーの名前とe-mailアドレスを設定しておきます。(デフォルトだとマシン名が入ったりして格好悪いので)

$  emacs -nw README.txt  // 登録用にREADMEファイルを作成します
$  git add README.txt 
$  git commit -m 'first commit'

$  git remote add origin git@github.com:(アカウント名)/git-hiro.git
$  git push origin master

ところで上記の例で何かおかしいと感じた所はありませんか?自分は他の人のページを見てた時、「これでいいの?」と思ったんですが(githubアカウントの)パスワードを入力するところがありません。
このあたりはマシンの設定にもよるんでしょうが、Macの場合はここでPermissionがないと怒られました。gitプロトコルで行う際、sshの鍵の登録をしておく必要があります。
詳細は別のページにまかせますが、ssh-keygenで鍵を作成し、それをgithubの自分のアカウント設定のページでSSH Keyの登録を行ってやります。(後で、git push/pullをする度にアカウント名/パスワードを入力する例も示しておきます。複数のPCで作業する際、全部のPCに鍵登録するのも大変なので。)

4.githubのページでリポジトリの状態を確認する
2,3回README.txtを変更、登録を繰り返してその履歴などが記録されているのを確認できます。(手順5にSS例を表示します)
わざと2回目の履歴を表示しました。ブラウザ上で内容を確認できるし、前の回との差分も見られるようです。(さらにこのcommitに対するコメントが下段に入力できます)最上段がcommitコメントになっています。

5.別のマシンで既存のリポジトリを取得、変更を登録する
次にUbuntu(わざと別のOSで作業します)で、githubからリポジトリを取得し、変更して登録します。

$  mkdir git-hiro  // 一応、同じディレクトリ名でディレクトリを作成します

ここでgithubからデータを取得します。今度はわざとhttpsでやってみます。

$ git clone https://github.com/hiro1960/git-hiro.git git-hiro

initが必要ないのに注意してください。(やってもいいですが)また、わざとoriginへURLの登録もせず、直にコマンド入力してみました。データの取得なので特にアカウント名/パスワードは必要ありません。

$  emacs -nw README.txt   // 取得したREAMEの内容を変更します
$  git add README.txt
$  git commit -m 'add 3rd line from Ubuntu'  // 3回目のcommitのコメントです
$  git push https://github.com/hiro1960/git-hiro.git master

今度はgithub上のリポジトリの変更になりますので、ここでアカウント名/パスワードを聞かれます。(パスワードのssh設定をしていない場合ですが)

2回ほどMacでcommitし、3回目はUbuntuからcommitした状況です。それぞれの履歴の状態もブラウザ上でクリックして確認できます。

6.別のPCで変更分を取得する
さてUbuntu側でリポジトリを変更/更新したので、Mac側でそれを取得しないといけません。このあたりgitはsubversionと違い、ローカルでもリポジトリを持つのでちょっとコマンドが増えます。

$  cd git-hiro
$  git fetch           // githubからローカルのリポジトリに変更分をとってくる
(以下の様なメッセージがでる)
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1)
Unpacking objects: 100% (3/3), done.
From https://github.com/<プロジェクト名>/git-hiro
   4de59d5..72f762b  master     -> origin/master

$  git merge origin/mater  // 取得した差分をソースに反映(merge)する

fetchだけではローカルのリポジトリに更新データが反映されるだけで、ユーザーが直接さわるファイルには反映されません。わざわざ、origin/master(自分のファイルはmasterブランチだと仮定しています)にmergeすると明確に指示しないといけません。

実はこのあたり色々議論があります。1年前にeclipseでgitの操作するときは何気なしにメニューからpullを選んでいました。これはコマンドでも'git pull'でもいいんですが(実際、こっちではmergeの必要がありません)、ただ現在ではpullは使わない方がいいという人が多いようです。(但し、Toolを使わずに伝統的なコマンドラインでgitを操作する場合の話です。Toolで操作する場合はTool側で以下の問題は考慮してくれるでしょうから。)
最初にgithubからローカルにリポジトリを作成するさい(手順5)にgit cloneを使いましたが、ここはgit pullでも同じようなことはできます。でもcloneでやるとリポジトリのURLも覚えてくれるので、その後のpush/pull/fetch等でURLの指定が必要なくなり、間違いが減ります。そうなるとpullを使うと間違いの可能性が高まるので、いっそpullは使用するな、最初にcloneしてその後はfetch/mergeでやれ、という人が多いようです。
個人的には「慣れ」という気もしますが、仕事で複数の人間、複数のプロジェクト(当然、複数のリポジトリ)に関わる場合はちょっと制限が付きますが、この方が安全だと思います。

ただ一つ問題があるとすると、プロジェクトの作業中gitサーバが問題をおこし、臨時でbackupのサーバーをたてる場合です。当然、一時的にURLが変わるのでその時はpullで作業しないと対応できないでしょう。(当然、backup用のデータは保存してあるという条件です。仕事でやるなら作業の成果は逐次保存しておかないとまずいでしょうし。)でも本当はこんなことしたくないし、プロジェクトのメンバに注意しろと伝えるのも怖い。みんなの技量が同じ程度、あるいは十分なのか見極めれるのは問題が起こらなかった時だけですし。

7.github上のリポジトリの削除の方法
ところでこの使用例を書くためにgithub上に最初のリポジトリを作成するとき、うっかりタイプミスしてしまいました。丁度いいので、その削除方法を示します。(例によって、UIが他のページで調べたSSと若干違いました。そのあたりは何をしたいかを考えて応用してください。)
間違えて「git-hito」と作成してしまいました。これをクリックして、settingをクリックすると当該リポジトリの管理画面になり、削除のボタンがでてきます。(注:少し前までAdminというタブでこの管理画面に飛んだ様です。その辺は応用きかせて、何らかの管理を行う操作を探します。)
一番下に削除ボタンがあります。これを押すと確認用にリポジトリ名を入力するダイアログがでてきます。

面倒くさいですが、キーボードから削除したいリポジトリ名を入力し、"I understand ..."のボタンを押します。すると削除されます。


ちょっと注意が必要なのが、コメントやwiki(githubでは個々のプロジェクトにwikiを提供してくれます)への日本語(マルチバイト文字コード)の扱いに問題があるとの話があります。そのため今回はすべて英文字しか使っていません。

PS
コメントに日本語(UTF-8)と入力してみましたが、普通に使えました。すくなくともコメントは大丈夫のようです。
更に追記、ソースコードにSJISがあってもサイトでの表示、および使用に問題はでませんでした。日本語等のマルチバイト文字への対応ができているようです。


2013年10月14日月曜日

Android開発環境:ADT ver.22への更新

6月くらいにまたADTが、21→22に上がっていたようです。(しばらくさわってなかった(-_-;))
それでやっぱりまたうまくいきません。(いい加減にしてくれ!)

1.googleのURLをhttpsにしてみる。
Android Development ToolsのURLですが、http://dl-ssl.google.com/android/eclipseになっていますが、先頭をhttpsに変えてみる。

ただ、これいくつかのサイトを見てみるとどっちがいいのか時々によって変わるようです。どこかにアナウンスがあるのかもしれませんが、自分は見つけれませんでした。(今回は、結局httpの方で更新しました)

通常だと、SDK managerを単独で起動したりして先にSDKを更新しておき、後でeclipse側でcheck for updateしてやること、とどこでも書いてあるんですが、相変わらず「更新するものはない」といってきます。(でも、eclipse起動時にはADTが古いといって怒ってきます)

2.手動でinstall new software→available softwareにて更新してやる。
試しに手動でADTのURLに対して可能なソフトを調べてみると、ver.22があるといってきます。喜んでこれをインストールしようとすると、、、Macでは固まってしまいました。

しばらくぐぐってみたら、どうもver.22から新しい”Build-tool”というものがADT内に現れたらしく、これがないと色々悲惨なことになるようです。そこでSDK managerでとりあえずインストールしてみました。

(なんか3つくらいのバージョンが並行して表示されます。よくわからないので、とりあえず3つともインストールしてみました。→googleから9/17版の最新ADTをDLして動かしてみたら、最新のBuild-tool 18.1だけでした。試しに古い2つを削除してみましたが、問題なさそうです。ただ設定を変えることになるので、workspaceの再ビルドがかかります。しばらくeclipseをそっとしておく必要があります。

3.手動でavailable softwareでADTのver.22をインストール。
今度は先に進行しました。(固まりません) 最初はMacで試してみましたが、次にWindowsの方もやってみるとADTはver.22になったようです。


ただ、ちょっと気になる状態になってしまいました。

  1. eclipseが、Indigo(3.8)→Juno(4.2)になった。(まあ、大丈夫かな?)
  2. ADTのeclipse起動時のダイアログにADTのバージョンが表示されるが、相変わらずver.21のまま。(でも起動してから、古いとは文句をいってこなくなった。ちょっと気にはなります。)
  3. 「ADTについて」でADT関連のパッケージを調べてみると、なんかver.21が残っている?というかver.22になってから名称が微妙に変わった?(Android Developer Toolsから、Android Development Tools ?)そのせいか、2種類表示されてくる様子。→9/17版では、”Android Development Tools”というのはありませんでした。更にgoogleのAndroid Developerのサイトを見ると、ページにより"Developer Tools", "Development Tools"と両方ありました。google自体、混同してる?


また気になることに、10/15にOracleがJavaの脆弱性に対応するため大規模なJavaの更新をするとの予告がでています。これで問題ないか、簡単なアプリでも作ってみないとわかりません。→既存のHelloAndroidで、一部テキストだけ変更して再ビルド、実行してみましたが、問題なさそうです。

2013年10月7日月曜日

データ・ロギング・システムの検討(Etherによるデータ転送能力の調査)

さて、snappyの実力に関する実験データは取れたので、いよいよ検討してるデータ収集(ロギング)の検討に入ります。まずデータをEtherで転送するときの、実際の転送時間や、HDDへの記録にかかる時間、タイミングなんかを調べます。

まず、いま検討しているシステムは以下の様なものです。

PC1PCxはリアルタイムでデータを収集します。そのため自前でデータを記録していると、リアルタイムでのデータ収集のタイミングが乱される可能性があるので、収集したデータをEhterでどんどんPC0に送ります。(TCP/IPのSocket通信を想定しています)PC0では、送られたデータをどんどんHDDにログしていく。(ここは特にリアルタイムである必要はないが、PC1PCxより送られてくるデータを受信・ログが間に合う必要はある。)

本システムでタイミング的にボトルネックになりそうなのは以下の2点です。

PC1→PC0で収集データを送るときのEtherの通信時間。(データ長が長いと、それだけ通信時間が長くなる)
PC0によるHDDへの書き込み時間。(システムはLinuxで動かすのを想定しているので、PC0では強烈なファイルキャッシュが働くはずですが、それでも結局はHDDへの書き込み時間が問題になる)

結局、2か所でデータ長が問題になり、収集データを圧縮したいという要求がでてきます。それも高速に。(リアルタイム処理が乱されないように)
考えた、システムでの動作シーケンスの概要を以下に示します。
話を簡単にするために、データ収集としてPC1だけにします。

PC1PC0のSocketを接続
PC1で収集したデータをsnappy等で圧縮(今回の実験では、まだこの部分は未実装で計測)
PC1からPC0のEhterでデータを送信
PC0では受信したデータをHDDにログ
PC0からログ処理が終了し、次のデータを送ってもいいことを知らせるためにAckをPC1へ送る

まず基本的なデータを取るために、圧縮なしで100KBのデータをPC1は全速で送り、1回のloop処理時間を計測してみました。(10回loopを、3回実施)

1回目開始からの時間[sec]loopの時間[sec]2回目開始からの時間[sec]loopの時間[sec]3回目開始からの時間[sec]loopの時間[sec]
00.0094580700.0093951200.00931001
10.0188470.0093889310.0186870.0092918810.01864890.00933889
20.0281250.00927820.0280430.00935620.02789090.009242
30.0374820.00935730.03741910.009376130.0371830.0092921
40.0467150.00923340.04685210.00943340.04670190.0095189
50.05598310.009268150.05619910.00934750.05608110.0093792
60.06527520.009292160.0654850.009285960.06545310.009372
70.07459810.009322970.07483820.009353270.0748780.0094249
80.08389210.00929480.08408310.009244980.0841250.009247
90.0931940.009301990.09342410.00934190.09344390.0093189

約9msちょっとかかるようですね。なお、各loop処理時間の計測結果は毎回coutしてしまうと、コンソール出力処理の待ちにかなり時間を喰われてしまい、正確なloopの処理時間が計測できません。(このコンソール処理ってすごい時間がかかるんですよ。HDDへの出力はファイルキャッシュがあるので高速なんですが。)なので、プログラム中に計測した時間をメモリに記憶し、loop処理終了後にcoutするようにしています。

さて9ms/回はわかりましたが、ボトルネックとなる①、②はこのうちどれだけ時間がかかっているんでしょう?PC1にてWireSharkを起動し、パケットが何時送られたか時間を計測してみました。(PC0はHDDにログをしているのでファイルキャッシュがかなりきつい状態になっているはずです。そんな高負荷な状態でWireShark動かしてしまうと時間計測に影響を与えるといけないので、PC1で行います)
大体、以下の様な結果を得ました。(動作説明:PC1では「データ送信」の時に100KB分を1回だけsend( )関数をCallしています。それが35個のパケットに分解され、全部で2msかかったのがWireSharkのログで判明しました。また、PC0では1回のrecv( )関数では100KB分を受信できず、2,3回にわけて受信していました。つまりTCP/IPの実装部で35個のパケットを受信バッファにどんどん取り込んでいますが、アプリに制御が渡るのはまとめて2,3回になっていました。ただし、個々のパケット受信に対してはパケット受領のackがデータ送信の1ms後から始まり、8msの間続いたのがWireSharkのログで判明しました。PC0側で100KB分のデータをHDDにfwrite( )してプログラムに制御が返ってきたところで、PC1にAckの信号を送っています。それらの時間関係を示したのが、下の図です。)


PC0は受信において8msの間に35個のackパケットを250~300usにてほぼ等間隔にPC1に返していました。(例外もあります。時々50usのときもありましたし、1回300msもかかったことがありました。)

ところで上記の実験では10回loopでしたが、これだとログ・サイズは1MBにしかなりません。OSはLinuxでやっているので、余裕でファイルキャッシュに吸収されてしまい、実際のHDDへの書き込み時間(処理)の影響を測定できていません。試しにloopを30,000回にして2GB以上のログファイルを転送するときの時間を計測してみました。(PC0は32bit Linuxでメモリ2GB搭載です。これだけのログファイルをHDDへ書き込もうとすれば、オンメモリ上のファイルキャッシュは一杯になるでしょう。)
結果はやはり9ms/回でした。HDDの書き込み速度がそこそこ速く、処理が間に合ったのでしょう。(しかしファイルキャッシュの効果は偉大だ)

ただそれにしても送信処理より受信処理が4倍かかるのはちょっと予想外に時間がかかりすぎです。これだと高速な圧縮処理使うより、(snappy使うより)多少時間かかってもデータサイズを小さく圧縮(高圧縮率)した方がトータルで性能が出るんでない?
試しにHDDへの書き込みだけコメントアウトして、socketの受信処理だけに絞って計測してみました。

結果は、変わらないでした。ちょっとびっくりしましたが、受信時のackパケットを250us毎に返していたのはTC/IPの受信スタックの処理時間が優勢のようです。TCP/IPの処理は重いとは聞いていましたが、これほどとは・・・ データの圧縮どうこう言う前に、socket通信を少しでも早くするために、socketのパケットサイズを調整しないといけません。

今回実験してみて感じたことをまとめてみます。


  • Linuxのファイルキャッシュはかなり強力だった。(テストプログラムを終了したあとも、しばらくHDDが動きっぱなしでした。実際に2GB強のデータをHDDに書き込まれたのはテストプログラム終了後しばらくしてからでしょう。)
  • TCP/IPの処理はかなり時間がかかる。今回、1回のloopで100KBをまとめてsend( )しましたが、パケットとしては35個に分割されていました。他に何台のPCがPC0に接続されるかによりますが、システムの利用状況に応じてパケットサイズを調整しないといけない。(あるいはこんなくらいの時間がかかると思ってシステムの設計を行うかのどちらか)
  • 結局、snappyの有効性確認ができる状況が作れませんでしたが、ある程度の圧縮率が見込める運用状況ならやった方がいいでしょう。(圧縮率200%が見込めるなら、単純にパケット長が半分になるので、上図の送信・受信処理時間も半分になってくれるはず。)
  • 収集データをHDDにロギングまでする必要があるというのはかなり特殊な用途ですが、1回のシステムの動作で10GBくらいのデータを収集するなら64bit OSマシンでオンメモリに保持した方がいい。今回2GB強のログサイズだったので、ファイルキャッシュがなんとかもったが、10GBくらいだとたぶんHDDの状態にも影響されるでしょう。(テストプログラム終了後のdisk I/Oがすごかったので、ちょっと怖くなった)


まあ、snappy使って送信データを圧縮・伸長しようなんてのは、たいていはクラウド系のシステムでの利用がほとんどでしょうから、今回の検討が特殊な状況だとは思います。

PS:
結論として高速にEtherで通信したいと思ったら、snappy等でデータを圧縮し、パケットのサイズを減らし(なんとか1パケットに抑えれるのがベスト!)、受信側の負荷を下げてやるのが一番いいということになると思います。



2013年10月5日土曜日

emacs内のshellで、変な文字コードが表示される時

Linuxでのプログラム開発のとき、自分はほとんどemacsでプログラミング/ビルド/デバッグおよび実行をします。(マウスを使ってwindowを切り替えるのが面倒)かなり昔からこのスタイルで、はやりの統合開発環境はあまり好きではありません。(さすがにJavaプログラムをするときはeclipse使いますが)
何年か前からですが、emacs内でshellを起動してファイル操作、実行等しているときに"ls"コマンドで変な文字コードが出るようになりました。(行末、行頭に何かの制御コード?)


自分はtcshが好みなんですが、やむをえずbashを起動すると正常になっていました。最近はUbuntu使うことが増えたので、デフォルトのbashを使うことも多くなったのですが、ところが最近のLinuxだとそれでもダメなことが増えてきて、本格的に調べてみました。
原因は"ls"がshell起動時にaliasされて、"ls --color=auto"になっていたことでした。ここ数年前から"ls"コマンドすると、ファイルの種類やディレクトリの表示を種類別にカラー表示してくれるようになるのがデフォルトになっていましたが、emacs内のshellにはカラー表示の機能がないため、その制御コードが表示されていました。
aliasを削除してもいいんですが、普段のコンソールでは便利なのでemacs等のshellだけ無効にするため、.bashrcの最後に以下の様な$TERM環境変数をチェックするif文を追加しました。(TERMにはGUI環境では通常"xterm"等が入っています)


ファイルの種類によるカラー表示はだめになりましたが、昔ながらの"-F"オプションで種類は識別できます。


2013年10月2日水曜日

Google snappyの試行:時間計測

Mac OSXの処理時間と乱数データではそもそも圧縮がかえってサイズが増えてしまう結果がでたため、Linux PCを使って詳細なデータを取ってみました。(snappyのパッケージ内の試験用のデータ、21個を使いました)

結果を以下の表にまとました。

1.Linuxでの結果
ファイル名ファイルサイズ[B]圧縮サイズ[B]圧縮率[%]圧縮時間[msec]圧縮レート[MB/s]伸長時間[msec]伸長レート[MB/s]圧縮時間[sec]伸長時間[sec]
alice29.txt15208988017172.8%1.446100.3060.368394.0130.0014460.000368
asyoulik.txt12517977525161.47%1.24695.8120.285419.010.0012460.000285
baddata1.snappy2751226675103.14%0.157167.2470.0161618.3490.0001570.000016
baddata2.snappy2748326724102.84%0.143183.220.0151744.9490.0001430.000015
baddata3.snappy2838427476103.3%0.169160.1350.0191419.1990.0001690.000019
cp.html2460311838207.83%0.213110.2040.039603.7550.0002130.000039
fields.c111504728235.83%0.11394.0930.02537.3510.0001130.00002
geo.protodata11858823295509.07%0.537210.6360.1051078.0750.0005370.000105
grammar.lsp37211800206.72%0.04972.6050.008437.7650.0000490.000008
house.jpg126958126797100.13%0.133910.090.0139404.2990.0001330.000013
html10240022842448.3%0.529184.5880.103948.1470.0005290.000103
html_x_440960092221444.15%2.021193.2760.56697.4890.0020210.00056
kennedy.xls1029744424519242.57%6.284156.2762.373413.8430.0062840.002373
kppkn.gtb18432069526265.11%1.364128.8730.403436.2610.0013640.000403
lcet10.txt426754234392182.07%3.854105.5991.001406.5290.0038540.001001
mapreduce-osdi-1.pdf9433077421121.84%0.259347.440.0422143.8620.0002590.000042
plrabn12.txt481861319097151.01%4.91493.5151.321347.8510.0049140.001321
ptt551321692963552.06%2.244218.1110.714685.430.0022440.000714
sum3824018722204.25%0.313116.4960.065562.3530.0003130.000065
urls.10K702087335387209.34%4.933135.7281.221548.3990.0049330.001221
xargs.142272509168.47%0.05770.7450.009444.9470.0000570.000009

参考までにMac OSXの結果ものせときます。

2.Macでの結果
ファイル名ファイルサイズ[B]圧縮サイズ[B]圧縮率[%]圧縮時間[msec]圧縮レート[MB/s]伸長時間[msec]伸長レート[MB/s]圧縮時間[sec]伸長時間[sec]
alice29.txt15208988017172.8%2.10768.8420.542267.6450.0021070.000542
asyoulik.txt12517977525161.47%1.84864.60.477250.3580.0018480.000477
baddata1.snappy2751226675103.14%0.256102.4650.029909.4890.0002560.000029
baddata2.snappy2748326724102.84%0.241108.7360.027972.850.0002410.000027
baddata3.snappy2838427476103.3%0.28794.2990.033822.7240.0002870.000033
cp.html2460311838207.83%0.33370.4960.07335.8770.0003330.00007
fields.c111504728235.83%0.1859.0730.032332.8360.000180.000032
geo.protodata11858823295509.07%0.642176.1430.176642.7530.0006420.000176
grammar.lsp37211800206.72%0.0939.3760.012297.6810.000090.000012
house.jpg126958126797100.13%0.262462.0860.0264659.0140.0002620.000026
html10240022842448.3%0.658148.4060.175558.0390.0006580.000175
html_x_440960092221444.15%2.465158.4680.695562.0580.0024650.000695
kennedy.xls1029744424519242.57%7.409132.5452.087470.5240.0074090.002087
kppkn.gtb18432069526265.11%1.734101.3720.556316.1580.0017340.000556
lcet10.txt426754234392182.07%5.33476.3011.234329.7960.0053340.001234
mapreduce-osdi-1.pdf9433077421121.84%0.393228.9560.0711266.1750.0003930.000071
plrabn12.txt481861319097151.01%6.62169.4071.693271.4330.0066210.001693
ptt551321692963552.06%2.312211.7010.855572.3070.0023120.000855
sum3824018722204.25%0.44182.6810.106343.7310.0004410.000106
urls.10K702087335387209.34%6.454103.7441.417472.4680.0064540.001417
xargs.142272509168.47%0.10538.4270.014286.5760.0001050.000014

結果を見て感じたことを箇条書きにしてみます。

  1. 圧縮率は確かにあまりよくない。特にbaddata*というファイルは、ほとんど圧縮できていませんが、圧縮レート/伸長レート自体は悪くない。
  2. 圧縮レートより伸長レートの方が圧倒的に速い!
  3. 圧縮レートも結構速いが、圧縮率が大きいほど圧縮レートが遅いというわけでもなく、関係がよくわからない。(圧縮率が大きいほど圧縮レートが遅くなると予想したが、これははずれた)
  4. Macの方はCPUのクロックが遅いのでやはり全体的に処理速度が遅い。全般的な傾向はLinuxと同じだが、圧縮レートを比較すると個々の結果で差があるが、理由がわからない。(Macの方は時間計測において結構バラツキがありました。tickの時間がLinuxと違うのか、単にタイマーの精度が悪いのか・・・
圧縮/伸長時間やレートはCPUの性能が大きく関係するので、判断を一概にできませんが今回使ったCPU(Core2Duo E6850)だとGbit Etherくらいまでは圧縮した方が今回の目的(データを収集して、別のPCにEhterで送りそこでHDDにログする)には使えそうな気がします。(もちろん100Mの昔のEtherなら間違いなくsnappy使った方がいいでしょう)また、複数のPCでデータを収集して、1つのPCで集中してログする場合も有効でしょう。(転送データのサイズが減れば、それだけEtherのパケットがライン上で衝突する確率が減ります)

ただ今回のログを一つのPCで集中的に行いたいという目的だと、伸長レートは別に遅くてもいいんで、本当は圧縮レートの方が早いのがうれしいんだけど。(ログは圧縮したまま行った方がHDDに書き込む量/時間を減らせるし、ログ解析時は多少伸長に時間がかかっても問題ない場合が多いです。)