00:23 tgt joined 00:41 cognominal joined 00:48 tgt joined 01:32 jnap joined 01:48 tgt joined 02:03 jnap joined 02:21 FROGGS_ joined 02:31 benabik joined 02:49 tgt joined 03:06 krunen joined 03:50 tgt joined 04:15 dagurval joined 04:51 tgt joined 05:51 tgt joined 07:22 camelia joined 07:33 camelia joined 07:37 FROGGS joined 08:02 tgt joined 08:26 odc joined
timotimo could it be that storing a flag where a pointer used to be is a Very Bad Idea if you don't tell the GC about that quirk? 09:23
i'm looking at a segfault i'm getting and my first suspicion is that there's a smallbigint inlined into a P6opaque and the gc is being told "follow this pointer and clean up stuff there!" 09:24
but i'm not sure
i'd like to wait for jnthn or diakopter to give an opinion on that 09:26
but maybe in the mean time i can just make P6bigint uninlinable
"This type cannot box a native integer" :\ 09:27
09:28 krunen joined
timotimo i think it *might* be a better idea to re-use the "sign" integer for a flag and alloc or used for the number data 09:33
and leave the pointer alone, or set it to 0?
er, wait 09:34
what am i saying.
the pointer only gets the flag, not the data
ah, "this type cannot box a native integer" was coming from not cleaning the serialized data 09:49
somehow 0x4000000009 lands in whatever "data" is 09:55
that is, a pointer to that address
the offset is "8", so the original data value has got to be 0x4000...0001 09:56
which could conceivably be a too-wide reading of data followed by the flag 09:57
i think get_boxed_ref returning (mp_int *)&(data->body.i) might be problematic for that reason? 09:58
hm. maybe the data is just being serialized wrong? that could be a possibility 10:03
hoelzro parallel conversations! 10:04
timotimo hm. the data remains at that curious value 10:05
i think i may very well be barking up the wrong tree.
dalek arVM/small_big_ints: dfd5e77 | (Timo Paulssen)++ | src/ (3 files):
trying more things to make this work. segfaults.
timotimo maybe someone else can step in?
10:23 ggoebel1113 joined
jnthn timotimo: What does the P6bigintBody structure look like now? 11:15
timotimo: uh...I didn't want you to inline mp_int... 11:29
That wsa meant to stay as mp_int *
So that Int doesn't get any bigger memory wise than it is today on 64-bit... 11:30
So the small bigint body is just a 32-bit flag set to 0xFFFFFFFF and then the 32-bit int storage.
teaching &
timotimo oh, but wasnt mp_int always inlined 12:55
timotimo reviews his patches 12:58
jnthn: github.com/MoarVM/MoarVM/compare/s...91aeebaR17 12:59
moritz patches his reviews 13:01
13:25 krunen joined
timotimo ah, i think one of the mistakes that lead to my stuff not working at all is that i don't create bigints to store the results of ops into at all. 13:44
so i'll have to rethink the way i allocate p6bigint or the way i handle the result objects 13:45
diakopter timotimo: but.. 14:02
timotimo: but the union doesn't save any storage 14:03
timotimo: what's the goal of the branch 14:12
timotimo it does not?
why not?
diakopter I retract that assertion until I understand what you aim to do 14:15
mp_int is 3 ints and a pointer
timotimo i am supposed to store the flag in the lowest bits of the pointer 14:16
and a 32 bit value somewhere in there 14:17
diakopter so just do that with the mp_int directly
timotimo it was very hard to write a literal 1 into the pointer. 14:19
diakopter why.. *(ptr) = 1 works 14:21
timotimo if you can tell me exactly how to treat mp_digit *dp as if it were a number i can change
i'm pretty sure that would write a 1 to where the pointer points
diakopter er, right.
timotimo you see my problem now? :))
diakopter *kickself*
tadzik what about ptr = 1? :P
diakopter yes, what I meant
timotimo tadzik: i have no idea.
tadzik that should work alright
diakopter yes, ptr = 1 is fine
timotimo if that's all i have to do, then i can make my code a bit simpler, aye. 14:22
tadzik I can't imagine a platform where 1 is outside the address space :P
diakopter but since jnthn thought that mp_int was a pointer and not already inlined
then your "I was supposed to" understand is probably invalid
*understanding 14:23
when did jnthn say that's what you were supposed to do 14:24
dalek arVM/little_big_int: ce20922 | (Timo Paulssen)++ | src/ (7 files):
get rid of unhelpful union, s/small/little/
14:26
timotimo uh
not sure?
tadzik haha, that's like a civil war outcome 14:27
get rid of unhelpful union
timotimo the way it is now will still save storage and cpu time, because we don't have the indirect pointer and malloc/free workload
at least if we are dealing with little big ints only
if we have mixtures, we won't get around that after all.
14:28 jnap joined
dalek arVM/little_big_int: 6d10d0a | (Timo Paulssen)++ | build/Makefile.in:
one more small->little rename i missed.
14:30
timotimo that branch will get a cleanup before it gets merged to get rid of the back&forth 14:31
hoelzro timotimo++ # rename to LBI
moritz if it's small enough, you can merge it with --squash to create a single commit 14:32
timotimo it'll be a bit bigger than that 14:34
i'll make sensible commits that individually not-break the build hopefully
diakopter if (0 && ( 14:38
timotimo yeah, that'd not break the build :) 14:39
diakopter ? 14:40
timotimo if it's turned off, it wouldn't break the build 14:41
diakopter I know what you meant, I was saying it's turned off 14:42
also the branch mentions MVMP6littlebigintBody
timotimo yes, i'm fixing as we speak
i'm not immediately pushing my stuff
i'm a bit distracted at the moment, so i'm not working at full speed
interestingly i'm getting a few outputs of "get_int on a SBI" where that really shouldn't happen since i don't build any SBIs at the moment 14:46
ah, of course 14:47
i'm thinking of starting from scratch now %) 14:51
i wonder how i got to "'0' is not a valid number" 14:53
14:55 benabik joined
diakopter if (!IS_SBI(src)) 14:56
why is that in copy_to
timotimo i just looked at that, too 14:57
it's not executed yet when i hit that point
it's because i thought it was only for copying the dp
when in fact it's supposed to copy all of the struct as well as the dp
i'm pretty sure i have a much improved clarity of the whole situation now 14:59
thank you, diakopter :)
diakopter what's the purpose of CLEANUP_*
jnthn timotimo: um, I didn't realize we didn't store mp_int*...
I guess we can leave it as mp_int 15:00
Though we may find that *most* programs are much more memory efficient with it being mp_int *, since most integers will be small.
Sorry for the confusion.
timotimo i can do that, too, if you'd like 15:01
diakopter that would also add an additional malloc per big bigint, of course
(which may be worth it, of course)
timotimo say, jnthn, how do you feel about this idea:
when we create a p6bigint, we wr^H^Hcreate the data for dp directly after the structure if we already know the size
that way it can be prefetched without indirection until it has to be realloc'd 15:02
(in which case it will explode in our faces, because that pointer doesn't have a malloc header in front of its data
but mayhaps we could handle that somehow if it's more efficient that way?)
jnthn diakopter: I think it would be worth it because people doing stuff with bigints will be the exception, not the norm...
timotimo i have to get off this train in a minute
jnthn timotimo: I don't think we easily can do that, REPRs are meant to have a fixed size for a given type. 15:03
And it'd make Int even bigger.
diakopter jnthn: I don't know. :) a *lot* of crypto code uses bigints
jnthn Well, actually not really...
diakopter and a lot of code is becoming crypto code
jnthn diakopter: Yes, that's (a) not typical, and (b) you shoudln't implement your own crypto in 99.99999% of cases :)
diakopter: I suspect a bunch of it will be delegated to well-trusted libraries. 15:04
diakopter (a) what's not typical
jnthn diakopter: Writing crypto code is not typical.
diakopter I had just said the contrary
jnthn I'm saying I don't agree wiht you. 15:05
benabik Rule #1 of writing crypto code: Don't. Rule #2: See rule #1
diakopter why would using p6bigint mean you are "implementing your own crypto" 15:06
why wouldn't the well-trusted libraries use it
benabik Generally they want speed. Variable sized and dynamically allocated integers is not really a good idea there.
jnthn Because the well-trusted libraries largely already exist and they're probably written in native code.
diakopter: Oh, did you mean code using bigints is becoming mroe common? 15:07
diakopter benabik: wut. how do you use multiple/arbitrary precision integers without dynamically allocated
and variable sized
jnthn should go to the airport... 15:08
bbl
benabik Cryptography is usually on fixed sized integers?
diakopter no
never
moritz depends, really 15:09
diakopter every crypto library I've seen uses very large integers
moritz lots of symmetric cryptography uses fixed-sized buffers or ints
asymmetric crypto usually uses variable-sized integers 15:10
diakopter ok
moritz in fact, libtommath is used as the base for libtomcrypt
diakopter .. which uses large integers 15:11
moritz aye
it does asymmetric crypto too :-)
diakopter it also provides its own pooling/custom allocator as an option (libtommath) 15:13
for its *dp allocation 15:14
but we're not using it
libgcrypt uses a fork of GNU MP Library, but the JS version from emscripten uses fast_mpi.js 15:19
openssl uses libcrypto's BIGNUM 15:26
timotimo: just make copy_to do a set_int(get_int( since those will already have the checks you need 15:28
benabik: ok, it was my understanding most cryptography was asymmetric; maybe that's wrong 15:31
benabik Asymmetric encryption tends to be just long enough to negotiate a shared key for symmetric encryption. :-D
Symmetric is MUCH faster.
diakopter oh 15:32
masak right. the reason to use asymmetric is to share the key safely for the symmetric, I guess.
a bit like a secure courier showing up with the encryption key in a sealed envelope, and then you downgrade to the fast mechanism. 15:33
benabik Right. SSL and SSH both work that way. I think PGP might be pure asymmetric by default, but I haven't actually looked.
timotimo in case the program is getting a big hit from little_big_int overhead, we could have a counter that short-circuits any LBI stuff 15:34
hm. 15:42
although it'd not work to replace mp_int *i with mp_int i at run time, except if we transform REPRs 15:43
jnthn: so ... should i try transforming P6bigintbody to have an mp_int* in it? 15:54
i think the amount of big integers you can even come up with at all will vastly be drowned out by all the Ints that rakudo constructs around you
except you can still use int if you know you only need 64 bits
16:32 FROGGS joined 16:35 jnap joined
diakopter timotimo: I think you should do what jnthn was suggesting (transforming P6bigintbody to have an mp_int* in it) 16:37
then on 32-bit platforms, make the body a union { struct { a 32-bit int (for the fast/small int value) and a 32-bit int to store the flag of whether to use the pointer }, and dp * 16:39
but then on 64-bit, simply use dp * only 16:40
er
actually duh. use the union I said on both...
(that's what I dreamt it up..) 16:41
er sorry, argh
I don't mean dp*, I meant mp_int*
timotimo :)
diakopter anyway yes, it should work out correctly on both 32 and 64
if you use that union
no matter how the compiler lays out the union or struct 16:42
timotimo thank you :)
diakopter do you agree?
timotimo i didn't think much about it, but it seemed right 16:43
diakopter [that that's a good application of what jnthn was suggesting?]
timotimo the thing i find annoying is i have to make a new struct for the two 32 bit integers to plug in there
otherwise the union won't fly
i could "just cast" it instead, but ... >_>
diakopter it's safer,ish, to use the union
ish
ish ish 16:44
timotimo but then i have to make like 10 structs and add lots of names everywhere 17:03
diakopter er wut
timotimo because one of the platforms we target doesn't support anonymous structs
diakopter it doesn't need to be anonymous
timotimo yeah, but then i have to put lots of stuff into the access code :) 17:04
diakopter why do you need 10 structs 17:25
what's wrong with the 1 I mentioned 17:26
tadzik which platform we target doesn't support anon structs? 17:27
I thought moarvm has those already
I seem to have seen them here and there
timotimo someone told me it wouldn't fly 17:29
diakopter I've used them in moar
timotimo 15:11 < JimmyZ> timotimo: union needs a name, solaris doesn't support anonymous union 17:30
diakopter yeah, sure enough, andy has been sending in patches to get rid of anon unions 17:32
but those are anonymous unions, not structs
besides, it's supported them for years, just gives warnings 17:33
sun studio that is 17:35
timotimo oh, huh 17:40
17:47 FROGGS joined
[Coke] moar is passing one more test today. 18:07
tadzik 1 moar test
[Coke] warnings free is nice.
18:19 tgt joined
benabik One of these days I'm going to remove the const/signed mismatch warnings I keep getting. 18:46
FROGGS jnthn: I've got a deal for you... I implement openpipe when you fix that STable bug :o) 18:59
benabik ... Stable bug? That sounds ominous. 19:00
FROGGS STable, that it not a typo :o) 19:01
benabik No less ominous, given how central STables are. 19:02
19:07 raiph joined
diakopter what was the stable bug 19:09
raiph I've built a rakudo/moarvm. what's the incantation to spectest nqp/moarvm? 19:11
timotimo TEST_JOBS=n make m-spectest 19:12
[Coke] you can't spectest nqp. 19:13
timotimo well, you spectest it indirectly :) 19:14
FROGGS diakopter: I can't even explain 19:20
diakopter: I just know it happens in v5, when you load a module like Config that depends on Perl5::Terms (which is like v5's setting) 19:21
diakopter: and this exception is thrown in that case: github.com/MoarVM/MoarVM/blob/mast...on.c#L2044 19:25
raiph [Coke], timotimo: s/spectest/run nqp's own tests against/ 19:26
diakopter apporpriate
reposession
FROGGS raiph: make m-test
[Coke] if moar is the only one you built, I'd expect make test to work, but I could be mistaken. 19:27
FROGGS it will work, yes
if you built the others too, it will run the test for every backend in order 19:28
exactly like there is a make install and a make m-install 19:29
diakopter: besides NYI'd openpipe() this bug hinders me running v5 on moar :o( 19:33
diakopter: would be nice to spectest in about 8 minutes instead of 35
hoelzro FROGGS: I'm working on openpipe() right now! 19:34
FROGGS uhh!!
hoelzro speaking of which...I'm guessing that #ifdef _WIN32 #error "openpipe is NYI on Windows" wouldn't be very nice, right?
what would be the preferred course of action?
FROGGS hoelzro: damn it, now how should I convince jnthn to hack for me? :o)
hoelzro (no one else is working on it, are they?) 19:35
FROGGS: get him to make sure it works on Windows? =P
FROGGS hoelzro: I can make it work on windows then...
hoelzro: if you can implement it on linux, that this is awesome on its own
hoelzro ok
benabik hoelzro: #error << throw_adhoc < working
hoelzro yeah, I came across it when vetting Perl 6 modules on Mokudo 19:36
I guess I can throw in a throw_adhoc
benabik Not allowing Moar to build on a platform just because one thing is NYI is LTA. 19:37
hoelzro that's why I ask =)
should I create a new .c file for pipe stuff? or shoehorn it into fileops? 19:41
FROGGS what about procops.? 19:42
procops.c* 19:43
PerlJam hoelzro: a new pipe.c file with references to magritte "Ceci n'est pas une pipe" :-)
FROGGS *g*
hoelzro heh 19:44
hmm...if I close() a pipe, should that just close my side of the pipe? 20:13
because the child will get a SIGPIPE, won't it?
20:15 tgt joined
hoelzro MoarVM has a uv loop per thread, right? 20:30
I take it to mean that if I create a handle on one thread, it can only ever be picked up by the corresponding thread? 20:31
diakopter eh 20:36
not yet
but actually I thought uv loops sat on top of another thread system in libuv, so you can have handle sharing across loops... or is that wrong?
hoelzro heh, I have no idea =/ 20:37
this is my first exposure to libuv
=)
MVM_repr_alloc_init actually allocates data, right?
diakopter i think jnthn said he wanted to do the io stuff 20:38
hoelzro and you have to free() it yourself?
diakopter: should I leave openpipe to him?
diakopter because he has some designs/plans in mind he hasn't communicated [because it would be faster to implement them than write them out, I'm sure]
hoelzro damn 20:39
diakopter well I just mean don't feel bad if he redoes all that :) but please explore and learn and try
it's pretty complex
hoelzro alright
good to know =)
diakopter I spent a lot of time thinking/talking with him about it last summer? spring? 20:40
jnthn hoelzro: I'm fine with you putting in openpipe 20:51
I *will* be shuffling IO stuff around a bunch, but I'll be doing it step by step rather than throwing everything out. 20:52
hoelzro ok 21:13
very good =)
is nqp::closefh($pipe) not supposed to throw an exception the second time? 21:41
hoelzro is looking at the JVM pipes test
nwc10 jnthn: is the MVMSTable written to disk as is? I assume not 21:44
basically, I have sizeof(MSMStable) down by 8
but it seems that something wrote out REPR_data etc at the old offsets
jnthn nwc10: No, it's not written out directly... 21:45
nwc10: I think the thing responsible is called serialize_stable
nwc10 OK, there has to be a structure initialiser somewhere... 21:48
jnthn They're typiccally allocated by the stable allocation function in srf/gc/allocate.t
uh
src/gc/allocate.c
nwc10 that's not what i'm trying to describe. 21:52
nwc10 flails harder
I see things like MVM_REPR_DEFAULT_ATTR_FUNCS in reprs.h
which looks like text that gets stiched together to be inside {...} to be used a struct initialiser
hoelzro alright, openpipe work is here: github.com/hoelzro/MoarVM 21:53
it's not done yet, but it's a good start =)
nwc10 so I think I'm looking for a struct initialiser for an stable 21:55
jnthn Ooh, Moar stage parse crept under 40s for the first time ever on my box 21:57
With the nwc10++ GC patch 21:58
nwc10 yay
jnthn nwc10: OK, I didn't think we had any STables statically initialized, if that's what you mean
nwc10: Can you describe the symptoms a little more?
nwc10 with a bit of pasting 21:59
correct working STable looks like this:
(gdb) p *((MVMObject *)c)->st$5 = {header = {owner = 4154457015, flags = 32767, size = 0, forwarder = 0x7ffff79ffba2 <allocate>, sc_forward_u = {sc = 0x0, st = 0x0}}, REPR = 0x7ffff79ffb90 <copy_to>, REPR_data = 0x7ffff79f4884 <MVM_REPR_DEFAULT_GET_ATTRIBUTE>,
gah.
tmux goes om nom nom :-(
jnthn That...REPR and REPR_data seem to be poitning to odd places 22:01
nwc10 hangon
this is working:
paste.scsys.co.uk/296447
however ugly and wrong that looks, it is from working MoarVM 22:02
better paste paste.scsys.co.uk/296448 22:03
jnthn And first sub-300s spectest (299s) for me too :)
dalek arVM: 4c8452a | nicholas++ | src/gc/collect.c:
process_worklist() only needs to enforce the GC invariant if the object moved.

The comment notes "In moving an object to generation 2"... but the code had been doing the work for *all* objects in generation 2, not just those moved there. We know whether we have just moved an object from the nursery to gen2, so track that, and only walk the recently marked items for a newly moved object.
22:04
jnthn $5 = {header = {owner = 4154457015, flags = 32767, size = 0, 22:05
All of those fields are bogus. The owner should be 0 or maybe 1, but certainly that size of a number. And size should not be 0
nwc10 and not working paste.scsys.co.uk/296450
OK
interesting
jnthn Oh, you know waht this looks like?
Like something has confused st and a REPR 22:06
nwc10 please tell :-)
OK, this is on a somewhat variant branch
jnthn st = 0x7ffff7dd4be0 <this_repr> 22:07
That st point is pointing to a REPR table
One of the static things in a src/6model/reprs/
*pointer
nwc10 ah OK
that would explain a bit. And we got away with it due to chance 22:08
(assuming it's not stupid I added)
jnthn p *(MVMStable *)c
timotimo hey jnthn :)
nwc10 c is 0
jnthn nwc10: Youv'e cast c to MVMObject * 22:09
timotimo jnthn: should i go ahead with the mp_int * change? that is, un-inlining mp_int from P6bigintbody
jnthn nwc10: But the flags indicate it's an MVMStable *
nwc10 oh, I must be somewhere else
jnthn flags = 10
I'm at the very top of what you pasted
10 = MVM_CF_STABLE = 2 + MVM_CF_SECOND_GEN = 8
timotimo: I'm fine with that. 22:10
timotimo sounds good
but not right now.
jnthn timotimo: It'll make the majority of programs memory-lighter.
timotimo do we have any way of using a different REPR if we detect a crapton of bigints being used?
nwc10 aha OK 22:11
jnthn timotimo: Not at present
nwc10 jnthn: this is me trying to work out why I later have garbage in the gen2 roots
so this particular one *is* me being stupid, I think 22:12
jnthn nwc10: Oh
nwc10 sorry to be a small waste of time
jnthn nwc10: Well, I know there's code that used to use the presence of a forwarder as a mark bit.
nwc10: In order to compact the gen2 roots
nwc10 this is sort of X Y
jnthn nwc10: After a full collect.
nwc10 found that, that's patched locally
jnthn nwc10: No, you're not being a waste of time at all. You're saving a lot of memory ;)
nwc10 I haven't *yet*
jnthn timotimo: I'm not saying we have to follow this approach for all time, simply that I think it's the right way for now. 22:13
timotimo right. will do. 22:14
is somebody already working on putting the forwarding pointer into the rest of the object?
jnthn timotimo: Additionally, once we get escape analysis in place, given we know about bigints and stuff at VM level, I'm hoping we can do stuff like, doing operations on mp_int without all the box/unbox
timotimo that would be lovely
jnthn timotimo: Yes, that's what nwc10++ is talking about/debugging righ tnow :)
timotimo ah, great!
saves one int for every single object, that's 8 bytes, yes? 22:15
do we have any heap/allocation/... statistics at the moment?
jnthn Correct.
timotimo i remember i wanted to build the gdb thing that can output statistics without having to put lots of conditional C in place
it may be easiest to have a "startup script" that gdb sources that says "put this breakpoint in this function, step until you reach this line, collect these locals and continue 22:16
that way, i wouldn't even have to walk worklists myself at all, i'd just observe what the GC does anyway
okay, nwc10++ will be removing 8 bytes from all objects, i'll hopefully remove 24 bytes from almost every Int 22:22
i'll be excited to see how big the "say 1" program running on rakudo-moar will be after we finish this particular piece of work 22:24
nwc10 jnthn: OK, the bug may be these 2 lines in MVM_gc_root_add_gen2s_to_worklist
if (!num_in_nursery && REPR(gen2roots[i])->refs_frames) 22:25
num_in_nursery = 1;
specifically that use of the REPR() macro
on a collectable, that turns out to be an STable
*not* verified
jnthn Oh, ouch
I can confirm with quite high confidence that that's wrong. 22:26
nwc10 shall I try locally fixing my REPR macro to assert that it's only used on Objects, not STables, and see what breaks? :-)
actully, scrub that
I am going to go to bed
but if nothing has changed, I will try that sort of thing tomorrow
jnthn Yes, try that. But above isn't right. In fact, it's potentially a performance bug today
nwc10 anyway, that's why I've been asking questions about why my assertions with REPR() fail, when I shouldn't even be using REPR() on them 22:27
because I was trying to work out why that REPR() there ended up as a NULL pointer de-ref
we have probably been getting away with it due to alignment chance between two different structures 22:28
timotimo a performance bug! \o/
jnthn nwc10: aye, seems likely 22:33
To clarify why it may be a performance bug: if we find anything but NULL at that address, we'll have been considering refs_frames to be true. 22:34
On STable
And so not letting them leave the gen2 roots list
dalek arVM: ffa4cb1 | nicholas++ | src/6model/reprs/P6opaque.c:
In P6opaque's compose(), zero the freshly allocated repr_data->unbox_slots.

Not all the slots are used, but serialize_repr_data() will serialise all of them, and hence write out garbage to disk. This doesn't cause crashes, because the unuused slots are never accessed, but it does conceal other problems. valgrind reports an awful lot of warnings, concealing real problems, and the generated bytecode files will not be byte-for-byte identical, preventing easy consistency checking.
22:39
timotimo nwc10++ # removing unnecessary garbage reads/writes 22:46
diakopter thinks that fix might not have detectable improvement 22:49
but I'm hoping to be surprised 22:50
jnthn diakopter: ffa4cb1 almost certainly ain't. 4c8452a was for me.
ffa4cb1 eliminates most of valgrind's sadness 22:51
diakopter I meant the repr frames one
jnthn diakopter: Oh
Yeah, I dunno about that. Marking STables isn't profile-expensive in any I've done. 22:52
But adding gen2 roots is.
Oh, and it wouldn't show up in there...
We'll see :)
diakopter leaves it to you
since I don't have my phone dev env set up yet
jnthn ETOOTINYTODEV 22:53
:P
timotimo cpython takes 4116 maxresidentk for print("hello world") 22:55
will we reach that? :3
if you think about it, using more than one floppie's worth of space in RAM just to print "hello world" is pretty crazy 22:56
jnthn So is thining "hello world" is a useful benchmark :P 22:59
*thinking