カテゴリー : ioLanguage
このカテゴリーの登録数:42件 表示 : 1 - 10 / 42
2009/11/15
scala の RemoteActor を IoLanguage でそれっぽく
2009/10/05
IoLanguage の Actor を Java でやってみる。その2
JavaはMethodそのものについて型のような概念がない。いや、一応java.lang.reflect.Methodには、Methodとして扱えるけど、Ioでいうところのmethodじゃない。
つまり、Ioだとmethodそのものがオブジェクトから取り出せる。
a := Object clone do ( hoge := method( "hello world" println ) ) hoge := a getSlot("hoge") hoge
これができるから、methodそのものを遅延させたりすることができるワケで。。。
a := Object clone do ( hoge := method( "hello world" println ) ) hoge := a getSlot("hoge") @hoge @hoge yield;yield
ということで、今回は、methodそのものを非同期/同期にできるようなことを少し考えてみた。
といっても、javaの静的型は強力なので、一部適当
とりあえず、いつもの実装
package actor; public interface Hoge { public String hello(int i); public Integer world(int i); }
package actor; public class HogeImpl implements Hoge { public String hello(int i){ try { Thread.sleep(1000); } catch(InterruptedException e){ // nop } return "hello" + i; } public Integer world(int i){ try { Thread.sleep(1000); } catch(InterruptedException e){ // nop } return new Integer(i); } }
以下、書きかけ
package actor; import java.lang.reflect.Method; public class Actor<E> { private Class<E> type; private ExecutorService executor = Executors.newFixedThreadPool(5); private Actor(Class<E> type){ this.type = type; } public static <E> Actor<E> register(Class<E> target){ return new Actor<E>(target); } public <T> SynchronizeMethod<T> sync(Object object, String methodName){ return null; } public <T> FutureMethod<T> async(Object object, String methodName) throws NoSuchMethodException { Method[] methods = type.getMethods(); for(Method method: methods){ if(method.getName().equals(methodName)){ return new FutureMethod<T>(executor, object, method); } } throw new NoSuchMethodException(methodName); } }
public abstract class ProxyMethod<T> { private ExecutorService executor; private Object target; private Method method; public ProxyMethod(ExecutorService executor, Object target, Method method){ this.executor = executor; this.target = target; this.method = method; } protected ExecutorService getExecutor(){ return executor; } protected Object getTarget(){ return target; } protected Method getMethod(){ return method; } public abstract Object call(Object...args); }
package actor; import java.lang.reflect.Method; public class FutureMethod<T> extends ProxyMethod<T> { public FutureMethod(ExecutorService executor, Object target, Method method) { super(executor, target, method); } @Override public Future<T> call(final Object...args){ return getExecutor().submit(new Callable<T>(){ @SuppressWarnings("unchecked") public T call() throws Exception { return (T) getMethod().invoke(getTarget(), args); } }); } }
package actor; import java.lang.reflect.Method; public class SynchronizeMethod<T> extends ProxyMethod<T> { public SynchronizeMethod(ExecutorService executor, Object target, Method method) { super(executor, target, method); } @Override public Object call(Object... args) { try { return getMethod().invoke(getTarget(), args); } catch (Exception e) { throw new RuntimeException(e); } } }
package actor; import java.util.ArrayList; public class ActorTest { @SuppressWarnings("boxing") public static void main(String...args){ HogeImpl impl = new HogeImpl(); Actor<Hoge> actor = Actor.register(Hoge.class); try { FutureMethod<String> method = actor.async(impl, "hello"); List<Future<String>> results = new ArrayList<Future<String>>(); for(int i = 0; i < 25; i++){ results.add(method.call(i)); } try { for(int i = 0; i < results.size(); ++i){ System.out.println("result=" + results.get(i).get(2, TimeUnit.SECONDS)); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } int i = 0; boolean done = false; while(true){ if(done && results.size() <= i){ break; } if(!results.get(i++).isDone()){ i = 0; continue; } done = true; } System.out.println("done"); System.exit(0); } catch(NoSuchMethodException e){ // nop } } }
まとめ
うーん、とりあえず、動くけど、こんなんどうでもいい。
これだけなら、こんな感じのと何が違うのかと。。。
package actor; import java.util.ArrayList; public class Test { public void main(String...args){ ExecutorService executor = Executors.newFixedThreadPool(5); List<Future<String>> results = new ArrayList<Future<String>>(); for(int i = 0; i < 25; ++i){ results.add(executor.submit(new Callable<String>(){ public String call() throws Exception { return "hello world" + System.currentTimeMillis(); } })); } for(int i = 0; i < results.size(); ++i){ try { System.out.println(results.get(i).get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }
うーん、型推論と静的型に勝てない。。。
2009/09/21
IoLanguage の Actor を Java でやってみる。その1
IoLanguageにおける、@と@@をjavaでやってみる。
ちなみに、io の @ は 同期メッセージ、 @@ は非同期メッセージとみなしてやってみる。
ioのActorについては↓
ref - http://iolanguage.com/scm/git/checkout/Io/docs/IoGuide.html#Concurrency-Actors
まずは、Ioのコード
こんな感じ
Hoge := Object clone do (
index ::= 0
hello := method(i,
msg := "hello: " .. i .. "(" .. index .. ")"
System sleep(1)
msg println
index = index + 1
msg
)
world := method(i,
msg := "world: " .. i .. "(" .. index .. ")"
System sleep(1)
msg println
index = index + 1
msg
)
)
hoge := Hoge clone
for(i, 0, 4,
writeln("result=", hoge @hello(i))
)
while(Scheduler yieldingCoros size > 0, yield)
for(i, 0, 4,
writeln("result=", hoge @@world(i))
)
while(Scheduler yieldingCoros size > 0, yield)
何をやってるかというと、Hoge インスタンスの hello メソッドに同期メッセージをバシバシ投げて、結果をそのまま受け取るようにしている。
同期メッセージなので、メッセージはmethodでブロックされる。
次に、同じ Hoge インスタンスの world メソッドに非同期メッセージをバシバシ投げて、結果を受け取っている。
こっちは、非同期メッセージなので、ブロックはされないし、戻り値もない
これの結果
こんな感じ
result=hello: 0(0) hello: 0(0) result=hello: 1(1) hello: 1(1) result=hello: 2(2) hello: 2(2) result=hello: 3(3) hello: 3(3) result=hello: 4(4) hello: 4(4) result=nil result=nil result=nil result=nil result=nil world: 0(5) world: 1(6) world: 2(7) world: 3(8) world: 4(9)
result=nilが連続している箇所はnon blocking
次に Java のコード
すごく長いけど
package actor; import actor.Act.Future; public class Main { public static interface Hoge { public String hello(int i); public String world(int i); } public static void main(String...args){ @Act({ @Future(method="hello", async=false), @Future(method="world") }) class HogeImpl implements Hoge { private int index = 0; public String hello(int i){ String msg = "hello:" + i + "(" + (index++) + ")"; try { Thread.sleep(1000); } catch(Exception e){} System.out.println(msg); return msg; } public String world(int i){ String msg = "world:" + i + "(" + (index++) + ")"; try { Thread.sleep(1000); } catch(Exception e){} System.out.println(msg); return msg; } } Hoge hoge = (Hoge) Actor.create(new Factory<Hoge>(Hoge.class), HogeImpl.class); for(int i = 0; i < 5; ++i){ System.out.println("result=" + hoge.hello(i)); } try { Thread.currentThread().join(3000); } catch(InterruptedException e){ e.printStackTrace(); } for(int i = 0; i < 5; ++i){ System.out.println("result=" + hoge.world(i)); } try { Thread.currentThread().join(3000); System.exit(0); } catch(Exception e){ e.printStackTrace(); } } }
肝心なのは、↓の部分
public class Main { public static interface Hoge { public String hello(int i); public String world(int i); } public static void main(String...args){ @Act({ @Future(method="hello", async=false), @Future(method="world") }) class HogeImpl implements Hoge { private int index = 0; public String hello(int i){ String msg = "hello:" + i + "(" + (index++) + ")"; try { Thread.sleep(1000); } catch(Exception e){} System.out.println(msg); return msg; } public String world(int i){ String msg = "world:" + i + "(" + (index++) + ")"; try { Thread.sleep(1000); } catch(Exception e){} System.out.println(msg); return msg; } } : : } }
この部分は、メソッド内にクラスを作っている。というのも、FutureProxyを作るために、java.lang.reflect.Proxyを使うんだけども、Interfaceが必要なので、実装クラスは何度でも破棄してもいいように、メソッド内のクラスを使う
また、この実装クラスに対して、同期メッセージか非同期メッセージか(Io の @や@@)はAnnotationで指定するようにしている。
本来なら、実行時に指定するんだけども、まぁ、JavaのAnnotationで出来る範囲でもないので、とりあえずこんなんで
その他クラス類
アノテーションは、メソッドが非同期かどうかさえ記述出来ればいいので、こんな感じ
package actor; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.TYPE, ElementType.CONSTRUCTOR}) public @interface Act { public @interface Future { String method(); boolean async() default true; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Async { boolean value() default true; } Future[] value(); }
あえて、Factoryは分離したので、こんな感じ。単純にClassからnewInstanceしているだけだけ。
package actor; import java.lang.reflect.Constructor; public class Factory<T> { private Class<T> interfaceClass; public Factory(Class<T> interfaceClass){ this.interfaceClass = interfaceClass; } public Class<T> getInterfaceClass(){ return interfaceClass; } public T createInstance(Class<? extends T> type, Object...args){ Class<?>[] clazz = new Class[args.length]; for(int i = 0; i < args.length; ++i){ clazz[i] = args.getClass(); } try { Constructor<? extends T> constr = type.getConstructor(clazz); return constr.newInstance(args); } catch(NoSuchMethodException e) { try { return type.newInstance(); } catch(Exception e1){ e1.printStackTrace(); } } catch(Exception e){ e.printStackTrace(); } return null; } }
Proxyというか、ここではActorの実装は、こんな感じ。
package actor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class Actor implements InvocationHandler { public static <T> Object create(Factory<T> factory, Class<? extends T> type){ return create(factory, type, new Object[]{}); } public static <T> Object create(Factory<T> factory, Class<? extends T> type, Object...args){ T target = factory.createInstance(type, args); return Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{factory.getInterfaceClass()}, new Actor(target) ); } private ExecutorService executor = Executors.newFixedThreadPool(5); private BlockingQueue<Entry> queue = new LinkedBlockingQueue<Entry>(); private Callable<Object> call = new ActorExecutor<Object>(queue); private Object target; private Actor (Object target){ this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { queue.put(new Entry(target, proxy, method, args)); Act act = target.getClass().getAnnotation(Act.class); for(Act.Future future: act.value()){ if(method.getName().equals(future.method())){ if(future.async()){ // java.lang.ClassCastException: java.util.concurrent.FutureTask // return executor.submit(call); try { return executor.submit(call).get(0, TimeUnit.NANOSECONDS); } catch(java.util.concurrent.TimeoutException e){ return null; } } return executor.submit(call).get(10, TimeUnit.SECONDS); } } return null; } }
大きくここでは、Proxyを使って、メッセージのやりとりを中継
:
:
:
public static <T> Object create(Factory<T> factory, Class<? extends T> type, Object...args){
T target = factory.createInstance(type, args);
return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{factory.getInterfaceClass()},
new Actor(target)
);
}
:
:
:
と、BlockingQueueを使ったメッセージとTaskの分離部分
:
:
:
private ExecutorService executor = Executors.newFixedThreadPool(5);
private BlockingQueue<Entry> queue = new LinkedBlockingQueue<Entry>();
private Callable<Object> call = new ActorExecutor<Object>(queue);
private Object target;
private Actor (Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
queue.put(new Entry(target, proxy, method, args));
Act act = target.getClass().getAnnotation(Act.class);
for(Act.Future future: act.value()){
if(method.getName().equals(future.method())){
if(future.async()){
// java.lang.ClassCastException: java.util.concurrent.FutureTask
// return executor.submit(call);
try {
return executor.submit(call).get(0, TimeUnit.NANOSECONDS);
} catch(java.util.concurrent.TimeoutException e){
return null;
}
}
return executor.submit(call).get(10, TimeUnit.SECONDS);
}
}
return null;
}
:
:
:
この2つ。
Ioだと、メソッドの戻りが動的であっても構わないので、Javaでいうところの、java.util.concurrent.Futureが返せるけど、javaはそんなことやると、java.lang.ClassCastException: java.util.concurrent.FutureTaskなわけで、とりあえず、そこは別の方法にしている。他の方法は後で考えてみる
他はもろもろ
package actor; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; public class ActorExecutor<T> implements Callable<T> { private BlockingQueue<Entry> queue; public ActorExecutor(BlockingQueue<Entry> queue){ this.queue = queue; } @SuppressWarnings("unchecked") public T call() throws Exception { Entry e = queue.take(); return (T) e.method.invoke(e.target, e.args); } }
package actor; import java.lang.reflect.Method; class Entry { Object target; Object proxy; Method method; Object[] args; Entry(Object target, Object proxy, Method method, Object[] args){ this.target = target; this.proxy = proxy; this.method = method; this.args = args; } }
BlockingQueueに実行時のパラメータを全部入れておいて、ExecutorService.submitで個別に処理を行う。というとこあたりは、通常どおり
javaの実行結果
Ioとほぼ同じですが...
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8 hello:0(0) result=hello:0(0) hello:1(1) result=hello:1(1) hello:2(2) result=hello:2(2) hello:3(3) result=hello:3(3) hello:4(4) result=hello:4(4) result=null result=null result=null result=null result=null world:0(5) world:1(6) world:2(7) world:3(8) world:4(9)
まとめ
JavaのAnnotationでLocalスコープが使えればもう少し別の書き方にできそう。あと、実行時に同期か非同期かを切り替えれるような透過的なインタフェースがあると便利なのに。
Ioのコードをあえて、Javaにすることで、どれだけIoが楽か、逆に、Javaでも同じような実装は可能な事、Javaはもう少し細かい実装が可能っぽいこと。などなど、色々わかった。
次回は、透過的なインタフェースを作るところを目指す。
2008/07/26
Log4Io を作ってみた。
Log4Io を作ってみた。
ref http://coderepos.org/share/browser/lang/io/Log4Io/io
使い方は、Log4** とほぼ同じで、こんな感じ
Log4Io logger := Log4Io getLogger("sample") logger addAppender(Log4Io ConsoleAppender with) logger setLevel(Log4Io Level INFO) if(logger isDebugEnabled) then ( logger debug("debug") ) if(logger isInfoEnabled) then ( logger info("info") ) if(logger isWarnEnabled) then ( logger warn("warn") )
これを実行するとこんな感じ。
==> 2008-07-26 20:48:50 JST INFO sample - info ==> 2008-07-26 20:48:50 JST WARN sample - warn
また、Lobby に log4ioLogger を設置しているので、こちらはそのまま使えます。
Log4Io // lobby log4ioLogger log4ioLogger error("hoge") log4ioLogger info("foo") log4ioLogger warn("bar")
こっちは、こんな感じに出力される
==> 2008-07-26 20:48:50 JST ERROR Log4Io - hoge ==> 2008-07-26 20:48:50 JST INFO Log4Io - foo ==> 2008-07-26 20:48:50 JST WARN Log4Io - bar
ほとんど log4js からパクったけど、Log4J 並みの細かいことも作れそう。
ただ、パッケージとかが無い分だけ色々と面倒なのだけど。。。
Io の clone でハマる
clone したときは、init メソッドが呼ばれて、そこで必ず初期化しないとダメだということ
以下のコードだと、オブジェクトの値が clone されるんだね
Hoge := Object clone do( values := List clone with := method(name, c := self clone c values append(name) c ) ) a := Hoge with("a") b := Hoge with("b") a values println ==> list(a, b)
これってつまり js だと、以下のコードと同じってことか。
var Hoge = function (){}; Hoge.prototype = { values: [], with: function(name){ var c = new Hoge c.values.push(name) return c } }; var a = Hoge.prototype.with("a") var b = Hoge.prototype.with("b") print(a.values) ==> (a, b)
ということで、Io のコード及び、js のコードを直すなら、こんな感じになる。
# って基礎中の基礎か
Io
Hoge := Object clone do( init := method( self values := List clone ) with := method(name, c := self clone c values append(name) c ) ) a := Hoge with("a") b := Hoge with("b") a values println ==> a b values println ==> b
js
var Hoge = function (){return this.init.apply(this)}; Hoge.prototype = { init: function (){ this.values = [] }, with: function(name){ var c = new Hoge c.values.push(name) return c } }; var a = Hoge.prototype.with("a") var b = Hoge.prototype.with("b") alert(a.values) ==> a alert(b.values) ==> b
慣れは怖い
2008/07/23
Io で curry, bind を実装する
prototype.js の Function.prototype.curry と Function.prototype.bind が Io にも欲しくなったので、簡単に実装してみた。
ちなみに、Function.prototype.curry は引数のカリー化を行ってくれる関数プロトタイプで、Function.prototype.bind は、スコープのカリー化もしてくれるヤツ(って説明でいいのかな)
Block apply := method(target, args,
args = if(args isNil, list(), args)
target = if(target isNil, scope, target)
self setScope(target)
self performWithArgList("call", args)
)
Block bind := method(
args := call message argsEvaluatedIn(call sender)
target := args at(0)
args = args slice(1)
b := self
return block(
b apply(target, args union(call message argsEvaluatedIn(call sender)))
)
)
Block curry := method(
args := call message argsEvaluatedIn(call sender)
if(args size < 1) then (
return self
)
/*
target := args at(0)
args = args slice(1)
*/
b := self
return block(
b apply(scope, args union(call message argsEvaluatedIn(call sender)))
)
)
まぁ、bindもcurryも同じようなものなので、こんな感じで動かす。
hoge := Object clone do( message := "this is hoge" ) foo := Object clone do( message := "this is foo" ) promp := block(a, b, "#{self message} at #{a}, #{b}" asMutable interpolate println ) promp setScope(hoge) a := promp curry("aaa") a call("bbb") ==> this is hoge at aaa, bbb b := promp bind(foo, "ccc") b call("ddd") ==> this is foo at ccc, ddd
使い道は、これから考える
2008/07/20
translate posts of io mailing list
io のメーリングリストを 超 オレオレ 訳にして、英語を読む。
via - Found something but I don't know what to make of it
[Io] Found something but I don't know what to make of it
何かみつけたんだけど、よくわからないんだ。So I was in the process of compiling all the libs for the addons and
poking around looking for some info when I ran into a site in a
language I can only guess is Chinese:アドオンのライブラリをコンパイルする方法とかの情報を探し回っていたら、
サイトに出くわしたんだけど、どうやら言語は中国語っぽいんだよね。http://blog.xole.net/category.php?k=ioLanguage
Seems to be lots of Io code but I can't decipher all of what it does.
Is this a translation of existing info? Anyone know?たくさんの io のコードがあるように思えるんだけど、それが何に関して書かれているか読めない(decipher: 解読?)んだよね。
ここに書かれているのは、何か既存の情報の翻訳なの?誰か知ってる?
投稿者C. Olson さんは、どうやら僕のところに漂着してしまったみたいだけど、中国語っぽくて読めなかったらしい。
んで、Brian さんが次の投稿で
Re: [Io] Found something but I don't know what to make of it
> So I was in the process of compiling all the libs for the addons and
> poking around looking for some info when I ran into a site in a
> language I can only guess is Chinese:
>Japanese actually.
日本語みたいだね。> http://blog.xole.net/category.php?k=ioLanguage
>
> Seems to be lots of Io code but I can't decipher all of what it does.
> Is this a translation of existing info? Anyone know?Doesn't look like stuff I recognize from other posts so I imagine this
user has been doing their own investigation. The GC write up is quite
interesting (relating to tweaking the incremental step used in
collection).うーん、どうやらそういった情報(ここでは「translation of existing info: 既存の情報の翻訳」かな)では無いみたいだよ。他の記事から私が想像するに、それらの記事は、それについて調査したことを書かれているんだと、私は思うな。
GC について書き上げているのはかなり面白いと思う(GC の収集には、増加ステップ(incremental step)の調整に関係しているってさ)
Brian さんは日本語が読めるらしい。しかも他の記事から読み解くとかスゲー人なんだな。
GC についても触れてくれてるのも嬉しい :)
そして、Steve Dekorte さんからの投稿
Re: [Io] Found something but I don't know what to make of it
> So I was in the process of compiling all the libs for the addons and
> poking around looking for some info when I ran into a site in a
> language I can only guess is Chinese:
>
> http://blog.xole.net/category.php?k=ioLanguage
>
> Seems to be lots of Io code but I can't decipher all of what it does.
> Is this a translation of existing info? Anyone know?Here's a translation, but I'm still not very clear:
http://209.85.171.104/translate_c?hl=en&sl=ja&tl=en&u=http://blog.xole.net/category.php%3Fk%3DioLanguage&usg=ALkJrhjVqXbSXGowxw8i2ZyTgQC-b85dUA
翻訳してみた。でもまだ明確にはならないなあ:
うーん、皆さんを困らしているみたいですが、私も困惑。
だって英語できないんだもん。オレオレ訳が正しいとも限らないしなぁ。
ちなみに、"translation of existing info" については、これらかな。
- http://xole.net/docs/IoGuide_ja.html
- http://xole.net/docs/IoTutorial_ja.html
- http://xole.net/docs/IoReference_ja.html
今回出てきた英語
話し言葉(?)みたいな英語が多くて、意訳しかできないけど、こんな感じかな
- Found something ...
- なんかみつけた。
チョーそのまま。Found は find の過去形だからこんな感じかなぁ - make of it
- 理解する(?)
make of ... っていろんな意味があってよく分からん。英英辞典曰く(to understand or meaning character of...)
ref - GetUpEnglish: WHAT DO YOU MAKE OF IT? - poke around...
- 探し回る(?)
- run into(ran into)...
- ...に偶然に会う
話し言葉っぽい感じ。英英辞典曰く(to meet by chance) - Seems to be...
- ...のようだ(appears to beににてる)
Seems to be... で開始されてもいいんだね。文法とかあんまり気にしなくてもいいんだ。 - look like stuff...
- ...のように見える(かな?)
seems とか looks like はなんだ。結構曖昧なの?
ref - seems like, look likeの使い方を詳しく説明して欲しいです。それと going to be... - Yahoo!知恵袋, seem to be と It seems that の違い - 教えて!goo
話し言葉っぽいのが多くて、their own の their はどれだよ。ってのが多いなあ。うーん、難しい。
2008/07/14
io で GC を見る
io の Collector を使って、GCの動きを少し見てみる
via - Io の make test(libs/iovm/tests/run.io)
コードとしては、こんな感じのを使うらしい。
writeln("mpa \t as \t mb \t time \t mbt \t gc") lastTimeUsed := 0.0 list(1.01, 1.05, 1.1, 1.2, 1.5, 1.7, 2, 4) foreach(as, list(0.01, 0.1, 1, 2, 4, 16) foreach(mpa, Collector setMarksPerAlloc(mpa) Collector setAllocatedStep(as) time := Date clone cpuSecondsToRun(test) mb := (Collector maxAllocatedBytes / 1000000) asString(0, 2) writeln( Collector marksPerAlloc asString(0, 2), "\t", Collector allocatedStep asString(0, 2), "\t", mb, "\t", time asString(0, 2) , "\t", (mb asNumber * time) asString(0, 2), "\t", (100 * (Collector timeUsed - lastTimeUsed) / time) asString(2, 1), "%" ) lastTimeUsed = Collector timeUsed // Collector showStats writeln("collected items: ", Collector collect) Collector resetMaxAllocatedBytes ) "" println )
今回は、循環参照するようにしてそれの GC を取ってみます。
aaa := method(
Hoge := Object clone do(
foo ::= nil
)
Foo := Object clone do(
hoge ::= nil
)
h := Hoge clone setFoo(Foo clone setHoge(Hoge clone))
f := Foo clone setHoge(Hoge clone setFoo(Foo clone))
h clone setFoo(f clone setHoge(h clone))
f clone setHoge(h clone setFoo(f clone))
l := List clone
for(i, 0, 100, l append(h, f, i * 0.987654321))
)
以下、全体
// Collector setDebug(true) aaa := method( Hoge := Object clone do( foo ::= nil ) Foo := Object clone do( hoge ::= nil ) h := Hoge clone setFoo(Foo clone setHoge(Hoge clone)) f := Foo clone setHoge(Hoge clone setFoo(Foo clone)) h clone setFoo(f clone setHoge(h clone)) f clone setHoge(h clone setFoo(f clone)) l := List clone for(i, 0, 100, l append(h, f, i * 0.987654321)) ) test := method(1000 repeat(aaa)) writeln("time: ", Date clone cpuSecondsToRun(test), " seconds") writeln("mpa \t as \t mb \t time \t mbt \t gc") lastTimeUsed := 0.0 list(1.01, 1.05, 1.1, 1.2, 1.5, 1.7, 2, 4) foreach(as, list(0.01, 0.1, 1, 2, 4, 16) foreach(mpa, Collector setMarksPerAlloc(mpa) Collector setAllocatedStep(as) time := Date clone cpuSecondsToRun(test) mb := (Collector maxAllocatedBytes / 1000000) asString(0, 2) writeln( Collector marksPerAlloc asString(0, 2), "\t", Collector allocatedStep asString(0, 2), "\t", mb, "\t", time asString(0, 2) , "\t", (mb asNumber * time) asString(0, 2), "\t", (100 * (Collector timeUsed - lastTimeUsed) / time) asString(2, 1), "%" ) lastTimeUsed = Collector timeUsed // Collector showStats writeln("collected items: ", Collector collect) Collector resetMaxAllocatedBytes ) "" println )
これを実行すると、以下な感じの出力になる。
time: 0.511943 seconds mpa as mb time mbt gc 0.01 1.01 0.00 1.62 0.00 0.0% collected items: 2 0.10 1.01 0.00 1.65 0.00 0.0% collected items: 183 1.00 1.01 0.00 1.68 0.00 0.0% collected items: 182 2.00 1.01 0.00 1.68 0.00 0.0% collected items: 182 4.00 1.01 0.00 1.69 0.00 0.0% collected items: 182 16.00 1.01 0.00 1.73 0.00 0.0% collected items: 182 : : : 0.01 2.00 0.00 0.40 0.00 0.0% collected items: 4879 0.10 2.00 0.00 0.40 0.00 0.0% collected items: 2155 1.00 2.00 0.00 0.35 0.00 0.0% collected items: 57131 2.00 2.00 0.00 0.35 0.00 0.0% collected items: 64649 4.00 2.00 0.00 0.34 0.00 0.0% collected items: 68050 16.00 2.00 0.00 0.35 0.00 0.0% collected items: 70914 0.01 4.00 0.00 0.38 0.00 0.0% collected items: 22421 0.10 4.00 0.00 0.40 0.00 0.0% collected items: 2373 1.00 4.00 0.00 0.32 0.00 0.0% collected items: 141619 2.00 4.00 0.00 0.31 0.00 0.0% collected items: 145915 4.00 4.00 0.00 0.38 0.00 0.0% collected items: 1119 16.00 4.00 0.00 0.38 0.00 0.0% collected items: 2878
なぜか、Collector timeUsed の時間が取得できない...
今度調べる。
ちなみに、Collector setDebug(true) で実行すると、GC が sweep していく様子が見れます。
Collector_sweepPhase() allocated 9951 allocatedSweepLevel 9938 Collector_sweepPhase() allocated 9892 allocatedSweepLevel 9891 Collector_sweepPhase() allocated 9965 allocatedSweepLevel 9964 Collector_sweepPhase() allocated 9892 allocatedSweepLevel 9891 Collector_sweepPhase() allocated 9979 allocatedSweepLevel 9978 Collector_sweepPhase() allocated 9899 allocatedSweepLevel 9898 Collector_sweepPhase() allocated 9994 allocatedSweepLevel 9993 Collector_sweepPhase() allocated 9910 allocatedSweepLevel 9909 Collector_sweepPhase() allocated 9871 allocatedSweepLevel 9870
データ推移
んで、これで終わりではなくて、すこしデータ推移も眺めてみた。
ちなみに、as は Collector allocatedStep で、mpa は Collector marksPerAllocとなっています。
各、as と mpa の意味として
the allocatedStep (can have a fractional component, but must be larger than 1). A collector sweep is forced when the number of allocated objects exceeds the allocatedSweepLevel. After a sweep, the allocatedSweepLevel is set to the allocated object count times the allocatedStep. Returns self
the number of incremental collector marks per object allocation (can be fractional). Returns self.
ということで、as: 1.05, 1.10, 1.20, 1.50, 1.70, 2.00, 4.00 のデータを抜粋
また、x 軸はmpa(marksPerAlloc)値、y 軸は回収されたオブジェクトの個数となっています。
ここから分かることは、as(allocatedStep) の値が小さい場合は、mpa(marksPerAlloc)の値が大きい方が 回収されるオブジェクトの個数が多いらしい。
しかし、as の値が大きくなるにつれて、mpa の値が小さい方が回収されやすくなっている。
これが、いわゆる GC 最適化って言われてるヤツかな。
他にも、as値が小さく、mpa値が大きいと GC にかかる時間が多いらしい(これはたまたまなのかもしれないけど)
また、回収された個数でみてみると、as値とmpa値が両方大きいからといって回収されるオブジェクトの個数が増えるというわけでは無いようだ。
as:1.10の時
as: 1.50の時
as: 2.00の時
as: 4.00の時
ってことで、簡単なまとめ
as(allocatedStep) 値は、回収されるオブジェクトの個数に比例する
mpa(marksPerAlloc) 値は、as 値が小さい時に、回収されるオブジェクトの個数を増やすことができるが、as 値が大きくなると、回収効率は見込めない。
as 値が小さい場合は、GC に時間がかかる場合が多いが、as 値はある一定以上になる場合は、GC の時間に比例しなくなる。
ということで、やっぱり↓の画像のとおりになりそう。
簡単なデータしか取ってないけど、Numbers から PDF に出力したモノを置いておきます。
http://blog.xole.net/resources/io_collector_low.pdf
2008/07/09
io で GoogleChart
io languageでGoogleChartのURLデータを出力するヤツを作ってみた。
ref - http://xole.net/googlechart/sample.io
上のサンプルにもあるように、こんな画像を出力できるようにしただけ。
# 画像処理とかやってないのが、ヘタれ
こんなコードを書きます。
#!/usr/local/bin/io doFile("GoogleChart.io") doFile("Types.io") doFile("Labels.io") doFile("Colors.io") doFile("Data.io") doFile("Styles.io") types := Types clone types PieChart p3 labels := Labels clone labels Title chtt( """ GoogleChart sample by Io WebAPI """) labels Label chl("hoge", "foo", "bar") data := Data clone data Text chd("60", "30", "10") styles := Styles clone styles size(320, 200) chart := GoogleChart URL withQuery(types, labels, data, styles) "Content-type: text/html" println """ <html> <head> <title>GoogleChart with Io</title> </head> <body> <p>#{chart asHTML}<br />generated on #{Date}</p> </body> </html> """ interpolate println exit
もう少しまともなヤツにしようと考えてる(もう少し表計算チックにできれば綺麗だよね)けど、とりあえずこれで。
コードはcodereposにあげました。
ref - http://coderepos.org/share/browser/lang/io/WebAPI/GoogleChart
2008/07/07
ioで、svn のようなコマンドインタフェースを簡単に作る
svn コマンドなんかの
svn help commit
って、これって io のメッセージングにとても似てる
例えば、今日の タイムスタンプを取得するときなんかは
Date today asNumber
これってつまり、レシーバ svnに対して、helpコマンドの実行、そしてその引数としてcommitとかを指定して実行するっていうもの。
上の、Dateは、io だと、レシーバに対して doString を使ってコマンドを指定できる。もちろん、Lobbyに全てのコマンドを渡せる
Date doString("today asAtomDate") ==> 2008-07-07T00:00:00+09:00 Lobby doString("Date today asAtomDate") ==> 2008-07-07T00:00:00+09:00
これの仕組みを使って、port っぽいのを io で
#!/usr/bin/env io // a.io Command := Object clone do( type := "port" install := method( writeln(self type, " ", call message name) ) uninstall := method( writeln(self type, " ", call message name) ) search := method( writeln(self type, " ", call message name) ) help := method( writeln(self type, " ", call message name) ) list := method( writeln(self type, " ", call message name) ) ) Command doString(System args slice(1) join(" "))
上のは、a.ioってファイルだったりすると、こんな感じで指定できる
nowel@MacBook: ~/workspace/test/io> ./a.io install port install
でも、これだと、各メソッドが何も返却しないので、次の動きができない
nowel@MacBook: ~/workspace/test/io> ./a.io install hoge
port install
Exception: nil does not respond to 'hoge'
---------
nil hoge doString 1
ということで、少し変更する
#!/usr/bin/env io // port.io Command := Object clone do( __dump__ := Object clone do( name ::= nil forward := method( writeln("port ", name, " ", call message name) ) ) install := method( __dump__ clone setName(call message name) ) uninstall := method( __dump__ clone setName(call message name) ) search := method( __dump__ clone setName(call message name) ) help := method( helpList := Object clone do( install := method( "Installs one or more addons" println ) uninstall := method( "Uninstalls one or more addons" println ) search := method( "Lists all packages which match the search parameters" println ) forward := method(writeln("no such help: ",call message name)) ) ) list := method( AddonLoader addons foreach(v, v name println ) ) ) Command doString(System args slice(1) join(" "))
これで、こんな感じに動く。
nowel@MacBook: ~/workspace/test/io> ./port.io install hoge port install hoge nowel@MacBook: ~/workspace/test/io> ./port.io uninstall foo port uninstall foo nowel@MacBook: ~/workspace/test/io> ./port.io help install Installs one or more addons nowel@MacBook: ~/workspace/test/io> ./port.io help search Lists all packages which match the search parameters
すごく簡単な仕組みだけど、できた。
ということで、io にパッケージマネージャとかが存在しないのが、面倒だなーと思った。
なので、coderepos で作ろうかと思う。
まだ、何も作れてないけど。思い立ったが吉日

今回はいつもと逆で Io でやってみる
ターゲットは scala の RemoteActor
Ioには透過的なactorがあるのに、remote化をするのが大変。例えば scala だとこんなに簡単
(server)
(client)
んで、これを Io で簡単な実装をやってみる。古き良きXML-RPC的な感じで
(server)
(client)
こんな感じで使う
(server)
(client)
Client側のCoroutineまわりはそのままActorと同じような実装、通常のactorと違って、@xxxの戻りは future proxyもどきだったりする
self coroutine := coroDo(requestCoroutine) Coroutine yieldingCoros atInsert(0, self coroutine)何より面倒なのは、justSerializeはちょっとあれな感じなので、codeを使ってserializeっぽくして値を送っている(受取側は receiver doString(request string)
ちょっと scala に近づけた気がする。
ってか、もう少し頑張れば RMI 的なこともできそう。後は、単純な並列化はActorが頑張ってくれるはずだから、スケールアウトとかもできるようになれば面白くなりそう。
ioだと逆に面倒っぽいけど...。