Here is just another update on Erjang progress. Two weeks ago I started looking seriously at running some of the OTP tests. There's a lot of them part of the standard otp distro, and so it's a great source of bug hunting context for Erjang.
A simpler test_server
But, but, but, ... the test_server is a big chunk of code and it was a bit too much to make it all run in one take; so I've written a small stand-alone version of test server that satisfies the same API as the real one to be able to run tests.
To run a test with Erjang, cd into the test_server subdirectoy, and copy a test into there (it's a file ending with _SUITE.erl), so e.g. the ets tests are in ets_SUITE.erl found in the lib/stdlib/tests directory. Then, compile the test and launch Erjang...
prompt$ cd $ERJANG/test_server prompt$ cp $ERL_TOP/lib/stdlib/tests/ets_SUITE.erl . prompt$ erlc ets_SUITE.erl prompt$ java -Xss100m -jar ../erjang-0.1.jar -home $HOME Eshell V5.7.3 (abort with ^G) 1> ts:run(ets).
And off you go! I spent a few nights looking into the ets tests, and at present it passes close to half of the tests in the ets suite. Some of the ets tests are pretty scary exposing lots of detail about the implementation of ets.
The estone test suite
I've only been playing around with a few of the tests, but one of the more interesting ones is the estone suite, which gives some sort of performance measurement. Comparing BEAM to Erjang on my machine gives BEAM a score of ~140.000 so-called "estones", and Erjang a score of ~120.000 estones. Bigger is better, so BEAM is still a little ahead. The following is a graphical break-down of the components of the "estones" suite; and so here you can see where Erjang is slower or faster. The estones is the sum of these values; you can find the complete numbers in this Gist.
The areas where Erjang is particularly fast are "BIF dispatch", "Float arithmetics" and sending "Huge messages". "BIF dispatch" is fast (I guess) because BIF calls are generated as direct calls, and so the JVM can inline BIFs. "Floats" are fast because they can often be encoded directly as Java doubles so arithmetics is close to Java. For integer arithmetics Erjang looses because small integers have to overflow into big ints, and that calls for boxing all ints. But Erjang also shines in the categories "Alloc and dealloc" and "Links".
Not much has been done in terms of optimizing these, so there is plenty room for improvement. I still notice some congestion in the scheduler; which I have not tried to optimize. So there is lots of low hanging fruit.
In particular it is noticeable that Erjang falls behind in the "Function calls" category. I suspect this is because erlang:apply(M,F,A) is fairly slow as it is now; an inline cache per call-site could do wonders there.
Well, well...
Mnesia
Another thing I've been playing around with is to get mnesia to run. And it actually gets humping, ... look at this trace.
krab$ ./erl.sh -name erjang@`hostname`
Eshell V5.7.3 (abort with ^G)
(erjang@renaissance.local)1> mnesia:create_schema([node()]).
ok
(erjang@renaissance.local)2> application:start(mnesia).
ok
(erjang@renaissance.local)3> mnesia:create_table(users,
[{disc_copies, [node()]},{type, bag}]).
{atomic,ok}
(erjang@renaissance.local)4> mnesia:transaction(
fun()-> mnesia:write({users,"krab",{"Kresten", "Thorup"}}) end).
{atomic,ok}
(erjang@renaissance.local)5> mnesia:transaction(
fun()-> mnesia:read({users, "krab"}) end).
{atomic,[{users,"krab",{"Kresten","Thorup"}}]}
(erjang@renaissance.local)2> application:stop(mnesia).
ok
=INFO REPORT==== 18-May-2010::14:25:36 ===
application: mnesia
exited: stopped
type: temporary
Given that distribution also "works" it should be possible to mix erjang and beam nodes in a mnesia cluster. Kind of cool, eh?
More on how to run tests...
Here are some more notes on how to use the test suite. You can use ts:list(ets) to get a list of the tests in the ets module as follows...
2> ts:list(ets).
[{new,[default,setbag,badnew,verybadnew,named,keypos2,
privacy]},
{insert,[empty,badinsert]},
{lookup,[time_lookup,badlookup,lookup_order]},
{delete,[delete_elem,delete_tab,delete_large_tab,
delete_large_named_table,evil_delete,table_leak,baddelete,
match_delete,match_delete3]},
firstnext,firstnext_concurrent,slot,
{match,[match1,match2,match_object,match_object2]},
t_match_spec_run,
{lookup_element,[lookup_element_mult]},
{misc,[misc1,safe_fixtable,info,dups,tab2list]},
{files,[tab2file,tab2file2,tab2file3,tabfile_ext1,
tabfile_ext2,tabfile_ext3,tabfile_ext4]},
{heavy,[heavy_lookup,heavy_lookup_element]},
ordered,ordered_match,interface_equality,fixtable_next,
fixtable_insert,rename,rename_unnamed,evil_rename,
update_element,update_counter,evil_update_counter,
partly_bound,match_heavy,
{fold,[...]},
member,t_delete_object|...]
And you can run a specific test (or a test set) using ts:run/2 as follows
3> ts:run(ets,match).
Running test category [match]
*** Running [ets_SUITE,match1]
*** SEED: {3172,9814,20125} ***
Calling with options [{write_concurrency,false}]
1 [{write_concurrency,false}]
2 [{write_concurrency,false}]
345Ets mem info: {notsup,undefined}Calling with options [{write_concurrency,true}]
1 [{write_concurrency,true}]
2 [{write_concurrency,true}]
345Ets mem info: {notsup,undefined}[{comment,"Incomplete or no mem leak testing"},{comment,"Incomplete or no mem leak testing"}]
*** [ets_SUITE,match1]: Succeeded
...
*** [ets_SUITE,match_object2]: Succeeded
[{success,{ets_SUITE,match_object2}},
{success,{ets_SUITE,match_object}},
{success,{ets_SUITE,match2}},
{success,{ets_SUITE,match1}}]
For some tests you'll run into a stack overflow. That's often because the Erlang compiler generates code for list comprehensions which is not tail recursive; and so the stack need grows proportional to the size of the list being generated. To get around this you can run Erjang with -Xss100m to run with 100m stacks; one of these days somebody should write a transformation that rewrites the beam generated for list comprehensions into a tail recursive version (with a reverse in the end)... Some tests also need more async threads, whych you give it by passing -Derj.threads=X for some X. Ports that spawn external processes needs two async threads each, because Java has no non-blocking IO for that particular case.
It would be a great way to help the Erjang project to take one of the other Erlang/OTP tests and try to run it ... and let us know what you see. Perhaps you'll feel intrigued to go fix a bug; be my guest!
Recent Comments