あけましておめでとう。
去年は、結構ドタバタとした一年を過ごしてしまいました。
昨年の大きなトピックは以下の3つですかね。
- リフォーム
- 結婚
- 転職
それぞれ大きな出来事ですが、どれも今後10、20年と楽しい人生を過ごしていく為の準備が着実にできたのはよかったです。
プログラミングとしては、今年は小粒なゲームしか作れなかったのが残念でした。
AppEngineやAndroidを使ったサービスを今年こそはいくつかリリースしたいものです。
イラストロジックで遊べるサイト「twicross」を作りました
Twitter上のアイコンから、イラストロジック(ピクロス)を自動生成して遊べるゲームサイトtwicrossを作りました。Twicross と言う名前はTwitterとピクロスのもじりです。
http://twicross.appspot.com/:twicross
Twitterのアイコンから問題を生成して…。
Wiiを買ってからここ一年ほど、マリオのスーパーピクロスやイラストロジックのゲームをひたすら妻と解きつづけていたのですが。市販のゲームは問題数に限りがありますし、ピクロスの為にコンシューマゲームをいくつも買うのも何となくもったいないしからWeb上でイラストロジックのゲームが延々とできないかと思って作ってみました。これだと、Twitterのアカウントの数だけ問題が作れるので一生楽しめることでしょう。
現在の課題は「解答可能とは限らない問題を検出できない」「何となく簡単な問題ができやすい」「解けた絵とtwitterのアイコンが似てない」「そもそもまったく人気(需要)がない」などなど。
最後の「人気がない」は自分と妻が楽しめればそれで充分なんですが、妻があんまりこのゲームで遊んでくれません。('A`)
基盤はGoogleAppEngine+さくらVPS(画像処理がGoogleAppEngineでは制限されるため)。GUIのFWはGoogleWebToolkitとGoogle三昧のサービスになってます。
Twicrossはこれから自分専用のゲームサイトとして色々な自作ゲームを置く場所にしていこうかな。
eclipse + maven で gwt 開発
eclipse + maven(m2eclipse) + Google Plugin for Eclipse を使って GWT 開発をしようと思ったら、意外と面倒くさかったので設定のメモ。
まずは、普通に maven の war プロジェクトを作ります。
だいたいこんな感じになるはず。
SampleProject
-- pom.xml |
んで、 pom.xml を以下のように書き足します。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>SampleProject</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>com.google.gwt</groupId> <artifactId>gwt-user</artifactId> <version>2.0.3</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.gwt</groupId> <artifactId>gwt-servlet</artifactId> <version>2.0.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>gwt-maven-plugin</artifactId> <version>1.2</version> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> </configuration> </plugin> </plugins> </build> </project>
これで gwt.xml を追加すれば maven で gwt のコンパイルが行えます。mvn jetty:run-war で起動も出来ます。(jetty:run はsrc/main/webappを見にいくので不可)
次に eclipse の設定ですが、こちらはまず上記のプロジェクトを m2eclipse で Enable Dependency Management を ON にします。
それから、次に実行メニューから一度 mvn package を実行します。(重要)
そのあと Project の Properties から Google-> Web Application と移動して This Project has a WAR directory を選択し、 War directory に target/SampleProject-0.0.1-SNAPSHOT を指定します。
Google-> Web Toolkit の Use Google Web Toolkit のチェックももちろん ON に。
これで、 eclipse でも maven による推移的な jar の解決を行いつつ、 Google Plugin for eclipse の恩恵も受けることが出来ます。
ただし、依存 jar を追加した場合には必ず mvn package を実行して target/SampleProject-0.0.1-SNAPSHOT/WEB-INF/lib 以下に依存 jar を配備する必要があります。でないと、Google Plugin for eclipse で Web Application を起動した時に jar をロード出来ずにエラーになります。
あと gwt-servlet への依存は、なぜか Windows では必要なのデスが linux の maven で実行した時は不要でした。
(なぜか target 以下の WEB-INF/lib に gwt-servlet-2.0.3.jar が配備される)
アノテーションと継承のコスト差
ふと、特定の処理の前後に差し込む処理を継承などを用いて実現する場合と@Beforeなどのアノテーションやリフレクションを用いて実現する際のパフォーマンス差を測定したのでメモ。たぶん、こんなベンチマークは何年も前にみんな見たことあるだろうし今さら書いても特に意味は無いのだけれど、せっかくなので。
Core2Duo メモリ3Gのマシンでそれぞれ100万回実行を10回繰り返した結果です。
No | アノテーション(ms) | アノテーション(キャッシュあり)(ms) | 継承(ms) |
---|---|---|---|
1 | 17605 | 294 | 10 |
2 | 17312 | 257 | 9 |
3 | 17496 | 256 | 9 |
4 | 17270 | 255 | 9 |
5 | 17332 | 255 | 10 |
6 | 17372 | 255 | 8 |
7 | 17362 | 256 | 9 |
8 | 17385 | 257 | 9 |
9 | 17265 | 257 | 9 |
10 | 17215 | 256 | 10 |
平均 | 17361.4 | 259.8 | 9.2 |
とまぁ、継承で実装する場合を1とするとアノテーションを使って実現すると1890倍、キャッシュを用いても70倍と、かなりの差がある。
ただし、一回当たりの実行時間はアノテーションを用いても 0.01736 ms。キャッシュを用いると 0.00025 ms。
使う場所を十分検討すれば、パフォーマンスに大きな影響を与えないレベルである事も分かる。
早すぎる最適化を避け、可読性を重視するのであればアノテーションを使うほうが理にかなっているような気もします。
以下、ソースコード。
何のベンチーマークかよく分からないなどのつっこみどころがあると思いますが、ひとまずこれで。
アノテーションクラス(RetentionPolicy.RUNTIMEを忘れないように)
package test; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Before { }
ベンチマークのベースとなるクラス(コメントアウトを外すとキャッシュを使った実装になります)
package test; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public abstract class BaseClass { // private Map<String, List<Method>> cache = new HashMap<String, List<Method>>(); protected void setUp() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { // if (cache.containsKey(this.getClass().getName())) { // List<Method> list = cache.get(this.getClass().getName()); // for (Method method : list) { // method.invoke(this); // } // return; // } Method[] methods = getClass().getMethods(); List<Method> beforeMethods = new ArrayList<Method>(); for (Method method : methods) { Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { if (annotation.annotationType().equals(Before.class)) { beforeMethods.add(method); method.invoke(this); } } } // cache.put(this.getClass().getName(), beforeMethods); } public void execute() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { for (int x = 0; x < 10; x++) { long start = System.currentTimeMillis(); int loopCount = 1000000; for (int i = 0; i < loopCount; i++) { setUp(); } System.out.println(System.currentTimeMillis() - start); exec(); start = System.currentTimeMillis(); for (int i = 0; i < loopCount; i++) { tearDown(); } System.out.println(System.currentTimeMillis() - start); } } public abstract void exec(); public abstract void tearDown(); }
ベンチマークするクラス
package test; import java.lang.reflect.InvocationTargetException; public class TestClass extends BaseClass { public int i = 0; @Override public void exec() { } @Before public void beforeMethod() { i++; } public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { new TestClass().execute(); } @Override public void tearDown() { i++; } }
あけましておめでとう
今年は何をしようかなぁ。作りたいものはいろいろあるけれど、時間は少ない。
宝くじも一等が当たらなかったので、モラトリアム時間を作るのはちょっと難しい。
いろいろと取捨選択して、その時々でベストだと思うことをしていくしかないのだろうなぁと思う。
がんばるぞー。おー!
よい職業プログラマ
「よいプログラマってなんだろう? http://blog.clouder.jp/mt/mt-tb.cgi/1074 」というブログ記事を見て、僕も考えてみました。
ただし、よいプログラマではなく、よい職業プログラマです。
なんでかって言うと僕自身はナージャ・ジベリみたいなプログラマもよいプログラマだと思ってるので、これから書く条件ではそういうプログラマを評価できなくなってしまうからです。
と、いう訳でよい職業プログラマの条件。
- 汚いコードを整理・改善できる。
- 抽象化のセンスがよい。
- データ設計が誰でも理解しやすい構造になってる。
- メタな設計をやたらと濫用しない。
- 変数名・クラス名のセンスが良い。
- 何を意味しているのかがよくわかる。
- クラス名から想像できる機能を持ったメソッドが定義されている。
- 改善の手段・足がかりをコードやDBに残しておける。
- 例えば、データの増分の推移や、スナップショットを要件外だったとしてもDBに残すなどの備えを作っておく。
- コード中に拡張ポイントをいくつか用意しておく。
- ドキュメントを書ける。
うん、基本的には誰でも理解できるプログラム(を書ける|に書き直せる)のがよいプログラマだと僕は思ってます。職業プログラマって言うのは、パズルを作る人じゃないですから。自分がプログラムを書かなくなった時に、いつでもそのプログラムを他の人に引き渡せるような状態にしておく事を心がけるのがよい職業プログラマですよね。
この手の記事って、そこらじゅうに散らばってるのでかぶっている事や矛盾してる事も沢山あるでしょうし、あくまで僕の見解という事で。