Ruby: politics & performance [long]

The problem: Read a structured file (the details are irrelevant)
and generate a program which, when run, would produce the original
file. Modify the program, re-run it, and you have a relatively
easy way to change the original structured file.

For various reasons, C++ seemed like the way to go when I first did
this. It worked, until I was handed structured files that were 4 MB
long and the generated C++ program went on for some 600,000 lines
and g++ crashed trying to compile it.

I needed a scripting language. I should have realized that a long
time ago.

I tried Ruby. Ruby had a political advantage: It wasn’t Perl.
The problem with Perl was that it’s been around long enough to have
a reputation, deserved or not, for being hard to read. I don’t
mind Perl, but my boss was visibly relieved to hear I was using
something else.

I could have tried Python, but the Ruby book (Programming Ruby)
was shorter. Ruby had some other, less tangible, advantages
which I won’t go into.

If what follows has already been discussed, let me know. I looked,
but I couldn’t find anything.

I wrote a Ruby script generator that worked for small input files.
It worked for large input files, too, but it was slow, very slow.
It took 18 minutes to reproduce a 4 MB structured file. My
generated C++ program couldn’t do it at all, but 18 minutes was
still too long.

When I took a closer look, I realized that most of those 18 minutes
were spent parsing the script, and most of that time was spent in
list_append.

The problem, I finally figured out, was the arrays. The generated
script had lots of them, and many of them were big, some with more
than 10,000 elements.

At this point, I realized that mine wasn’t your typical Ruby
application. Normal, handwritten programs don’t have arrays that
huge.

What to do… I could give up and try, say, Python, or I could see
if there was a way to tweak Ruby.

I didn’t have time to learn Python.

Back to Ruby…

list_append (from 1.6.8) looks like this:

···

static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

 if (head == 0) return NEW_LIST(tail);

 last = head;
 while (last->nd_next) {
last = last->nd_next;
 }

 last->nd_next = NEW_LIST(tail);
 head->nd_alen += 1;
 return head;

}

Following nd_next links for each of n array elements takes
O(n**2) time. A tail pointer would have been useful, but with
the node structure being as compact as it is, there was no room
for one.

Desperate times called for desperate measures. I don’t expect
anyone to like what I did. I’m not crazy about it myself.

I added another word to the node:


typedef struct RNode {

union {
struct RNode *node;
} u4;
} NODE;

#define nd_tail u4.node

static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

 if (head == 0) return NEW_LIST(tail);

 if (head->nd_tail) {
last = head->nd_tail;
 } else {
last = head;
 }

 head->nd_tail = last->nd_next = NEW_LIST(tail);
 head->nd_alen += 1;
 return head;

}

It worked. The monster script that generated 4 MB of structured
stuff ran in under six minutes instead of 18. This is almost
acceptable, and it’s a lot better than a g++ compilation that
takes 15 minutes to not work at all.

My immediate problem seems to be solved, but I don’t want to make
my company dependant on a patched version of Ruby forever, so I’d
like some feedback.

I can see this going one of several ways:

  1. Array parsing should be improved. Adding a word to the node
    structure is crude and unimaginitive, but it may be necessary.

  2. Array parsing should be improved, and there’s a better way
    to fix it than by bloating the node size by 33%.

  3. Array parsing could be improved, but don’t expect it to
    happen any time soon. If you insist on having 10,000-element
    arrays, you might consider finding another language.

If the answer is #3, I don’t mind. I can deliver what I’ve got
and take the time to try something else. On the other hand,
I’d be a fool to assume anything.

Thanks for taking the time to read this.

Opinions…?

Louis Krupp

In article 3E1FF274.2090106@pssw.NOSPAMPLEASE.com.invalid,

The problem: Read a structured file (the details are irrelevant)
and generate a program which, when run, would produce the original
file. Modify the program, re-run it, and you have a relatively
easy way to change the original structured file.

Interesting… Just curious, what kind of files are these?

For various reasons, C++ seemed like the way to go when I first did
this. It worked, until I was handed structured files that were 4 MB
long and the generated C++ program went on for some 600,000 lines
and g++ crashed trying to compile it.

I needed a scripting language. I should have realized that a long
time ago.

I tried Ruby. Ruby had a political advantage: It wasn’t Perl.
The problem with Perl was that it’s been around long enough to have
a reputation, deserved or not, for being hard to read. I don’t
mind Perl, but my boss was visibly relieved to hear I was using
something else.

Again, interesting… It has so often been the opposite case (that the
boss was upset at someone using Ruby instead of Perl because the boss
never heard of Ruby) - this is a refreshing change.

If what follows has already been discussed, let me know. I looked,
but I couldn’t find anything.

I wrote a Ruby script generator that worked for small input files.
It worked for large input files, too, but it was slow, very slow.
It took 18 minutes to reproduce a 4 MB structured file. My
generated C++ program couldn’t do it at all, but 18 minutes was
still too long.

When I took a closer look, I realized that most of those 18 minutes
were spent parsing the script, and most of that time was spent in
list_append.

The problem, I finally figured out, was the arrays. The generated
script had lots of them, and many of them were big, some with more
than 10,000 elements.

Maybe not so unusual…

At this point, I realized that mine wasn’t your typical Ruby
application. Normal, handwritten programs don’t have arrays that
huge.

What to do… I could give up and try, say, Python, or I could see
if there was a way to tweak Ruby.

I didn’t have time to learn Python.

Back to Ruby…

list_append (from 1.6.8) looks like this:


static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

if (head == 0) return NEW_LIST(tail);

last = head;
while (last->nd_next) {

last = last->nd_next;
}

last->nd_next = NEW_LIST(tail);
head->nd_alen += 1;
return head;

}

Following nd_next links for each of n array elements takes
O(n**2) time.

Yes, that does seem excessive…

A tail pointer would have been useful, but with
the node structure being as compact as it is, there was no room
for one.

Desperate times called for desperate measures. I don’t expect
anyone to like what I did. I’m not crazy about it myself.

I added another word to the node:


typedef struct RNode {

union {
struct RNode *node;
} u4;
} NODE;

#define nd_tail u4.node

static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

if (head == 0) return NEW_LIST(tail);

if (head->nd_tail) {

last = head->nd_tail;
} else {
last = head;
}

head->nd_tail = last->nd_next = NEW_LIST(tail);
head->nd_alen += 1;
return head;

}

It worked. The monster script that generated 4 MB of structured
stuff ran in under six minutes instead of 18. This is almost
acceptable, and it’s a lot better than a g++ compilation that
takes 15 minutes to not work at all.

My immediate problem seems to be solved, but I don’t want to make
my company dependant on a patched version of Ruby forever, so I’d
like some feedback.

I can see this going one of several ways:

  1. Array parsing should be improved. Adding a word to the node
    structure is crude and unimaginitive, but it may be necessary.

  2. Array parsing should be improved, and there’s a better way
    to fix it than by bloating the node size by 33%.

  3. Array parsing could be improved, but don’t expect it to
    happen any time soon. If you insist on having 10,000-element
    arrays, you might consider finding another language.

You mean appending to the end of an Array should be improved, am I
right?

If the answer is #3, I don’t mind. I can deliver what I’ve got
and take the time to try something else. On the other hand,
I’d be a fool to assume anything.

Have you checked out NArray?
http://www.ir.isas.ac.jp/~masa/ruby/index-e.html

It may not be what you want, though.

It certainly seems that the O(n**2) performace for appending an item to
the end of an Array could be improved upon. Why would each node in the list
have to carry around the tail info? Why couldn’t we do it so that Array
object (as defined in C, of course) always knows where it’s tail is? Then
you only add the overhead of one pointer for the whole array (unless I’ve
misunderstood what you did). Of course you add some time overhead
whenever the array grows or shrinks, but I don’t think a new pointer
assignment adds much time.

Or perhaps since we always know how many elements are in an Array (don’t
we, I haven’t looked at the Array C code, I hope we don’t do a n**2
traversal to find out) couldn’t that info be used by list_append?

Thanks for taking the time to read this.

I changed the title of this thread so as to get the attention of Ruby
internals folks (matz, guy, are you listening? :slight_smile:

Opinions…?

I suppose another option would be to define a different kind of Array at
the C level, one that meets your needs. Then you wouldn’t be depending on
a modified version of Ruby, but on a homegrown extension which isn’t so
bad.

Louis Krupp

Phil

···

Louis Krupp lkrupp@pssw.NOSPAMPLEASE.com.invalid wrote:

“Or perhaps the truth is less interesting than the facts?”
Amy Weiss (accusing theregister.co.uk of engaging in ‘tabloid journalism’)
Senior VP, Communications
Recording Industry Association of America

Ooops… following up to my own post again…

In article avporp02sb7@enews1.newsguy.com,

In article 3E1FF274.2090106@pssw.NOSPAMPLEASE.com.invalid,

The problem: Read a structured file (the details are irrelevant)
and generate a program which, when run, would produce the original
file. Modify the program, re-run it, and you have a relatively
easy way to change the original structured file.

For various reasons, C++ seemed like the way to go when I first did
this. It worked, until I was handed structured files that were 4 MB
long and the generated C++ program went on for some 600,000 lines
and g++ crashed trying to compile it.

I needed a scripting language. I should have realized that a long
time ago.

I tried Ruby. Ruby had a political advantage: It wasn’t Perl.
The problem with Perl was that it’s been around long enough to have
a reputation, deserved or not, for being hard to read. I don’t
mind Perl, but my boss was visibly relieved to hear I was using
something else.
I wrote a Ruby script generator that worked for small input files.
It worked for large input files, too, but it was slow, very slow.
It took 18 minutes to reproduce a 4 MB structured file. My
generated C++ program couldn’t do it at all, but 18 minutes was
still too long.

When I took a closer look, I realized that most of those 18 minutes
were spent parsing the script, and most of that time was spent in
list_append.

The problem, I finally figured out, was the arrays. The generated
script had lots of them, and many of them were big, some with more
than 10,000 elements.

list_append (from 1.6.8) looks like this:


static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

if (head == 0) return NEW_LIST(tail);

last = head;
while (last->nd_next) {

last = last->nd_next;
}

last->nd_next = NEW_LIST(tail);
head->nd_alen += 1;
return head;

}

Following nd_next links for each of n array elements takes
O(n**2) time.

Yes, that does seem excessive…

A tail pointer would have been useful, but with
the node structure being as compact as it is, there was no room
for one.

Desperate times called for desperate measures. I don’t expect
anyone to like what I did. I’m not crazy about it myself.

I added another word to the node:


typedef struct RNode {

union {
struct RNode *node;
} u4;
} NODE;

#define nd_tail u4.node

static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

if (head == 0) return NEW_LIST(tail);

if (head->nd_tail) {

last = head->nd_tail;
} else {
last = head;
}

head->nd_tail = last->nd_next = NEW_LIST(tail);
head->nd_alen += 1;
return head;

}

It worked. The monster script that generated 4 MB of structured
stuff ran in under six minutes instead of 18. This is almost
acceptable, and it’s a lot better than a g++ compilation that
takes 15 minutes to not work at all.

My immediate problem seems to be solved, but I don’t want to make
my company dependant on a patched version of Ruby forever, so I’d
like some feedback.

I can see this going one of several ways:

  1. Array parsing should be improved. Adding a word to the node
    structure is crude and unimaginitive, but it may be necessary.

  2. Array parsing should be improved, and there’s a better way
    to fix it than by bloating the node size by 33%.

  3. Array parsing could be improved, but don’t expect it to
    happen any time soon. If you insist on having 10,000-element
    arrays, you might consider finding another language.

Actually, I would hope the answer isn’t 3 - I think cases like yours might
help us improve performance.

You mean appending to the end of an Array should be improved, am I
right?

Actually, now I relize you’re talking about list_append in the parser
(parse.h, parse.c) not in the Array class…

You’re talking about arrays that the parser builds, not Ruby Arrays (am I
right?)

It certainly seems that the O(n**2) performace for appending an item to
the end of an Array could be improved upon. Why would each node in the list
have to carry around the tail info? Why couldn’t we do it so that Array
object (as defined in C, of course) always knows where it’s tail is? Then
you only add the overhead of one pointer for the whole array (unless I’ve
misunderstood what you did). Of course you add some time overhead
whenever the array grows or shrinks, but I don’t think a new pointer
assignment adds much time.

Or perhaps since we always know how many elements are in an Array (don’t
we, I haven’t looked at the Array C code, I hope we don’t do a n**2
traversal to find out) couldn’t that info be used by list_append?

Thanks for taking the time to read this.

I changed the title of this thread so as to get the attention of Ruby
internals folks (matz, guy, are you listening? :slight_smile:

Can the parser be improved in this case?

Phil

···

Phil Tomson ptkwt@shell1.aracnet.com wrote:

Louis Krupp lkrupp@pssw.NOSPAMPLEASE.com.invalid wrote:

“Or perhaps the truth is less interesting than the facts?”
Amy Weiss (accusing theregister.co.uk of engaging in ‘tabloid journalism’)
Senior VP, Communications
Recording Industry Association of America

There was discussion on the list a while back about integrating Judy/SL
or whatever it was called. You might want to look into that.

– Dossy

···

On 2003.01.12, Louis Krupp lkrupp@pssw.NOSPAMPLEASE.com.invalid wrote:

  1. Array parsing could be improved, but don’t expect it to
    happen any time soon. If you insist on having 10,000-element
    arrays, you might consider finding another language.


Dossy Shiobara mail: dossy@panoptic.com
Panoptic Computer Network web: http://www.panoptic.com/
“He realized the fastest way to change is to laugh at your own
folly – then you can let go and quickly move on.” (p. 70)

Louis Krupp,

one option might be to use hashes in place of arrays. you can index them
yourself. instead of [a, b, c] use {0=>a, 1=>b, 2=>c}. hashes, i was suprised
to discover, are around 12x faster than arrays.

other than that, you should bug Matz about your change. speed imporvements are
always welcome. perhaps it will inspire an improved solution.

···

On Saturday 11 January 2003 01:47 pm, Louis Krupp wrote:


tom sawyer, aka transami
transami@transami.net

                               .''.
   .''.      .        *''*    :_\/_:     .
  :_\/_:   _\(/_  .:.*_\/_*   : /\ :  .'.:.'.

.’’.: /\ : ./)\ ‘:’* /\ * : ‘…’. -=:o:=-
:/:’.:::. | ’ ‘’ * ‘.’/.’ (/’.’:’.’
: /\ : ::::: = / -= o =- /)\ ’ *
’…’ ‘:::’ === * /\ * .’/.’. ‘._____
* | : |. |’ .—"|
* | _ .–’| || | _| |
* | .-’| __ | | | || |
.-----. | |’ | || | | | | | || |
__’ ’ /"\ | '-."". ‘-’ ‘-.’ '` |.

Look into NArray:

http://www.ir.isas.ac.jp/~masa/ruby/index-e.html

From the website:

“[NArray] is a class of Numerical N-dimensional Array, whose elements are
1/2/4-byte Integer, single/double-prec Real/Complex, and Ruby Object.
this extension library incorporates fast calculation and easy manipulation
of large numerical arrays”.

So it seems that NArray is mostly intended for arrays whose entries are
numbers. But it can still handle Ruby objects, and it might be a good
option.

Cheers,
Daniel Carrera
Graduate Teaching Assistant. Math Dept.
University of Maryland. (301) 405-5137

···

On Sun, 12 Jan 2003, Louis Krupp wrote:

The problem: Read a structured file (the details are irrelevant)
and generate a program which, when run, would produce the original
file. Modify the program, re-run it, and you have a relatively
easy way to change the original structured file.

For various reasons, C++ seemed like the way to go when I first did
this. It worked, until I was handed structured files that were 4 MB
long and the generated C++ program went on for some 600,000 lines
and g++ crashed trying to compile it.

I needed a scripting language. I should have realized that a long
time ago.

I tried Ruby. Ruby had a political advantage: It wasn’t Perl.
The problem with Perl was that it’s been around long enough to have
a reputation, deserved or not, for being hard to read. I don’t
mind Perl, but my boss was visibly relieved to hear I was using
something else.

I could have tried Python, but the Ruby book (Programming Ruby)
was shorter. Ruby had some other, less tangible, advantages
which I won’t go into.

If what follows has already been discussed, let me know. I looked,
but I couldn’t find anything.

I wrote a Ruby script generator that worked for small input files.
It worked for large input files, too, but it was slow, very slow.
It took 18 minutes to reproduce a 4 MB structured file. My
generated C++ program couldn’t do it at all, but 18 minutes was
still too long.

When I took a closer look, I realized that most of those 18 minutes
were spent parsing the script, and most of that time was spent in
list_append.

The problem, I finally figured out, was the arrays. The generated
script had lots of them, and many of them were big, some with more
than 10,000 elements.

At this point, I realized that mine wasn’t your typical Ruby
application. Normal, handwritten programs don’t have arrays that
huge.

What to do… I could give up and try, say, Python, or I could see
if there was a way to tweak Ruby.

I didn’t have time to learn Python.

Back to Ruby…

list_append (from 1.6.8) looks like this:


static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

 if (head == 0) return NEW_LIST(tail);

 last = head;
 while (last->nd_next) {

last = last->nd_next;
}

 last->nd_next = NEW_LIST(tail);
 head->nd_alen += 1;
 return head;

}

Following nd_next links for each of n array elements takes
O(n**2) time. A tail pointer would have been useful, but with
the node structure being as compact as it is, there was no room
for one.

Desperate times called for desperate measures. I don’t expect
anyone to like what I did. I’m not crazy about it myself.

I added another word to the node:


typedef struct RNode {

union {
struct RNode *node;
} u4;
} NODE;

#define nd_tail u4.node

static NODE*
list_append(head, tail)
NODE *head, *tail;
{
NODE *last;

 if (head == 0) return NEW_LIST(tail);

 if (head->nd_tail) {

last = head->nd_tail;
} else {
last = head;
}

 head->nd_tail = last->nd_next = NEW_LIST(tail);
 head->nd_alen += 1;
 return head;

}

It worked. The monster script that generated 4 MB of structured
stuff ran in under six minutes instead of 18. This is almost
acceptable, and it’s a lot better than a g++ compilation that
takes 15 minutes to not work at all.

My immediate problem seems to be solved, but I don’t want to make
my company dependant on a patched version of Ruby forever, so I’d
like some feedback.

I can see this going one of several ways:

  1. Array parsing should be improved. Adding a word to the node
    structure is crude and unimaginitive, but it may be necessary.

  2. Array parsing should be improved, and there’s a better way
    to fix it than by bloating the node size by 33%.

  3. Array parsing could be improved, but don’t expect it to
    happen any time soon. If you insist on having 10,000-element
    arrays, you might consider finding another language.

If the answer is #3, I don’t mind. I can deliver what I’ve got
and take the time to try something else. On the other hand,
I’d be a fool to assume anything.

Thanks for taking the time to read this.

Opinions…?

Louis Krupp

Louis,

Another approach is to avoid the parser, which was probably not intended
to deal with huge lists of nodes. Ruby has a great feature which allows
a source file to include arbitrary data. For example:

···

====================
p DATA.read.split

END
1 2 3 4 5

If the END occurs on a line with no other characters, then the
parser ignores the rest of the file, and the DATA constant refers to an
IO-like object which contains all the data following the END line.

I’m sure #read and #split will turn out to be more efficient than the
ruby parser, since they have less to do.

I’m not going to second-guess you, because I assume you’ve thought about your
specific problem a heck of a lot, but perhaps there’s a different approach
that would avoid the issue you’ve been hitting altogether.

It might be worthwhile giving a small example of the type of data you’re
playing with and the kind of things you want to do by modifying the program.

There are some very clever people on this list … I’m not one of them :slight_smile:
who may see a completely orthogonal approach to the problem.

Just a thought.

Harry O.

···

On Sun, 12 Jan 2003 07:47, Louis Krupp wrote:

The problem: Read a structured file (the details are irrelevant)
and generate a program which, when run, would produce the original
file. Modify the program, re-run it, and you have a relatively
easy way to change the original structured file.

Make it O(n) for each item; as there’s n items, it’s O(n**2) for the
creation of the whole array.

My first thought on this was using Marshal; as the format is fixed it
should be parsed faster. So here’s my first take:

batsman@kodos:/tmp$ ruby -e ‘print “a=[”; 999.times{ |i| print “#{i},”}; print “999];”’ > tst.rb
batsman@kodos:/tmp$ time ruby tst.rb

real 0m0.025s
user 0m0.020s
sys 0m0.000s
batsman@kodos:/tmp$ cp tst.rb marshaller.rb
batsman@kodos:/tmp$ cat >> marshaller.rb
File.open(“tst2.rb”, “w”) { |f|
f.puts( “a = Marshal.load(#{Marshal.dump(a).inspect})” )
}
batsman@kodos:/tmp$ ruby marshaller.rb
time ruby tst2.rb

real 0m0.044s
user 0m0.040s
sys 0m0.000s

It’s a little bit slower but it should scale better when the size grows
(unless it is badly implemented). However:
batsman@kodos:/tmp$ ruby -e ‘print “a=[”; 9999.times{ |i| print "#{i},
"}; print “9999];”’ > tst.rb
batsman@kodos:/tmp$ time ruby tst.rb

real 0m3.849s
user 0m3.710s
sys 0m0.000s
batsman@kodos:/tmp$ cp tst.rb marshaller.rb
batsman@kodos:/tmp$ cat >> marshaller.rb
File.open(“tst2.rb”, “w”) { |f|
f.puts( “a = Marshal.load(#{Marshal.dump(a).inspect})” )
}
batsman@kodos:/tmp$ ruby marshaller.rb
batsman@kodos:/tmp$ time ruby tst2.rb
tst2.rb:1:in `load’: dump format error(0x24) (ArgumentError)
from tst2.rb:1

real 0m0.066s
user 0m0.040s
sys 0m0.010s

This approach fails, so I’ll try with an intermediate file:
batsman@kodos:/tmp$ cp tst.rb marshaller.rb
batsman@kodos:/tmp$ cat >> marshaller.rb
File.open(“tst.dump”, “w”) { |f|
Marshal.dump(a,f)
}

batsman@kodos:/tmp$ time ruby marshaller.rb

real 0m3.409s
user 0m3.390s
sys 0m0.000s
batsman@kodos:/tmp$ cat tst2.rb
a= nil
File.open(“tst.dump”, “r”) { |f| a = Marshal.load(f) }
batsman@kodos:/tmp$ time ruby tst2.rb

real 0m0.080s
user 0m0.020s
sys 0m0.010s

And now comes the important thing: it should be quite easy to build an
extension (in C) that performs essentially the same thing as
Marshal.dump for such simple (although long) arrays. You could even
build a hash of arrays and dump it all at the same time.
Take a look at w_object in marshal.c.
Another possibility would be playing with _dump and _load, using your
own format, either by defining them in Array or by subclassing. You
could implement these either in C or in Ruby; in the latter case,
although somewhat slower than in C, you’d be able to code quickly and
avoid O(N2) problems (I’d bet a O(N) alg. in Ruby would beat
hands-down a O(N
2) in C with those sizes)

I have another idea to hack the Ruby parser, too. Gotta test it.

···

On Sun, Jan 12, 2003 at 05:47:02AM +0900, Phil Tomson wrote:

It certainly seems that the O(n**2) performace for appending an item to

_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

if (argc > 1 && strcmp(argv[1], "-advice") == 0) {
printf("Don't Panic!\n");
exit(42);
}
-- Arnold Robbins in the LJ of February '95, describing RCS

The problem: Read a structured file (the details are irrelevant)

No, the details are probably important

I added another word to the node:

Well, here is the problem. You have in [ruby-talk:5956]
    http://www.ruby-talk.org/5956

  * currently each internal structure sizes are made strictly less
    than 5 pointer size for space efficiency.

1. Array parsing should be improved. Adding a word to the node
structure is crude and unimaginitive, but it may be necessary.

If you read [ruby-talk:5956], this seems difficult

2. Array parsing should be improved, and there's a better way
to fix it than by bloating the node size by 33%.

Well, apparently you have a very special case where ruby take most of its
times in compile phase rather than runtime phase.

Yes array parsing can be improved and you'll have a faster compile phase,
but if you try this (with the restriction of 5 pointer size) I think that
the problem will be reported at runtime, i.e. you'll have a faster compile
phase but with a slower runtime phase and more the ratio compile/runtime
is low more you'll see a difference.

Guy Decoux

I added another word to the node:

Can you try to do something similar with the second node in the list
(rather than adding a node) ? or send me in private email your script if
you can (I just want to see something).

Guy Decoux

In article 200301111701.12221.transami@transami.net,

···

Tom Sawyer transami@transami.net wrote:

On Saturday 11 January 2003 01:47 pm, Louis Krupp wrote:

Louis Krupp,

one option might be to use hashes in place of arrays. you can index them
yourself. instead of [a, b, c] use {0=>a, 1=>b, 2=>c}. hashes, i was suprised
to discover, are around 12x faster than arrays.

except that he’s talking about list_append as used in the parser… I
don’t think using hashes will help at all. It seems that the issue has to
do with node lists which are constructed by the parser.

Phil

“Or perhaps the truth is less interesting than the facts?”
Amy Weiss (accusing theregister.co.uk of engaging in ‘tabloid journalism’)
Senior VP, Communications
Recording Industry Association of America

ts wrote:

“L” == Louis Krupp lkrupp@pssw.NOSPAMPLEASE.com.invalid writes:
I added another word to the node:

Can you try to do something similar with the second node in the list
(rather than adding a node) ? or send me in private email your script if
you can (I just want to see something).

The second word (as I’m sure you know) is normally a count of how
many nodes are in the list. I tried adding a flag bit and making
that word be either a counter or a tail pointer. I don’t remember
the details, but I couldn’t get it to work in a reasonable amount
of time. So I finally did the brute force thing and added a word.

Louis

Harry Ohlsen wrote:

The problem: Read a structured file (the details are irrelevant)
and generate a program which, when run, would produce the original
file. Modify the program, re-run it, and you have a relatively
easy way to change the original structured file.

I’m not going to second-guess you, because I assume you’ve thought about your
specific problem a heck of a lot, but perhaps there’s a different approach
that would avoid the issue you’ve been hitting altogether.

It might be worthwhile giving a small example of the type of data you’re
playing with and the kind of things you want to do by modifying the program.

The original data is binary, all shapes and sizes – 8 bits, 16 bits,
32 bits, signed and unsigned integers, IEEE floating point. Parsing
it isn’t the problem. Generating a program to reproduce the original
file isn’t the problem. The problem is running the monster once I’ve
created it; g++ won’t touch a program this big, and Ruby takes too
long to parse monster arrays.

We’ve been using generated C++ programs for a few years now, and
people like that approach.

I probably could have done this in Perl, but my boss would have been
less than pleased. I could have learned Python, I suppose, but why
would I want to do that when I could be learning Ruby?

Louis

···

On Sun, 12 Jan 2003 07:47, Louis Krupp wrote:

Phil Tomson wrote:

In article 200301111701.12221.transami@transami.net,

one option might be to use hashes in place of arrays. you can index them
yourself. instead of [a, b, c] use {0=>a, 1=>b, 2=>c}. hashes, i was suprised
to discover, are around 12x faster than arrays.

except that he’s talking about list_append as used in the parser… I
don’t think using hashes will help at all. It seems that the issue has to
do with node lists which are constructed by the parser.

Correct – I think it’s this bit of parse.y:

···

Tom Sawyer transami@transami.net wrote:


args : arg
{
value_expr($1);
$$ = NEW_LIST($1);
}
> args ‘,’ arg
{
value_expr($3);
$$ = list_append($1, $3);
}
;

For the first item in the list, list_append has to check one
node, for the second it looks at two, and
1 + 2 + 3 + … + n = (n**2 + n)/2 traversals for n items.

list_concat does basically the same thing.

Meanwhile, I’m learning some things about Ruby. NArray will almost
certainly be better than using generic arrays for lists of what I
know will be numbers.

Another poster suggested DATA.read.split, and that would work if I
had only one array to worry about. Unfortunately, there are a
bunch of them, and they’re scattered throughout the generated
script.

Thanks to everyone for the replies!

Louis

The second word (as I'm sure you know) is normally a count of how
many nodes are in the list. I tried adding a flag bit and making
that word be either a counter or a tail pointer. I don't remember
the details, but I couldn't get it to work in a reasonable amount
of time. So I finally did the brute force thing and added a word.

Not the second word, the second element. For example actually you have

    last = list;
    while (last->nd_next) {
  last = last->nd_next;
    }

Try to replace it with

    if (list->nd_next) {
  last = list->nd_next->nd_end;
    }
    else {
  last = list;
    }

i.e. the first element give the size, the second give the end.

You just need to adapt one macro in eval.c to make it work (if I'm right)

Guy Decoux

Hello Louis,

Monday, January 13, 2003, 5:11:37 PM, you wrote:

The original data is binary, all shapes and sizes – 8 bits, 16 bits,
32 bits, signed and unsigned integers, IEEE floating point. Parsing
it isn’t the problem. Generating a program to reproduce the original
file isn’t the problem. The problem is running the monster once I’ve
created it; g++ won’t touch a program this big, and Ruby takes too
long to parse monster arrays.

can you auto-split generated program into several files? this must help with g++

with ruby you can write intermediate program which joins several output
files together

···


Best regards,
Bulat mailto:bulatz@integ.ru

Hi Louis,

The original data is binary, all shapes and sizes – 8 bits, 16 bits,
32 bits, signed and unsigned integers, IEEE floating point. Parsing
it isn’t the problem.

OK, so, you have a clean specification of the structure of the data; that
always helps :-).

Generating a program to reproduce the original
file isn’t the problem.

That was what I was getting at. Again, maybe you’ve already looked into this
and the approach your taking as to how that program works is the best that
can be done. However, what I was getting at was that perhaps with some more
knowledge of the problem (ie, the structure of the data that you’re trying to
reconstitute), maybe someone would be able to see an orthogonal approach,
that wouldn’t require such a huge program … thus eliminating the problem,
rather than trying to fix it.

For example … and this really is just an example, to clarify the kind of
thing I’m talking about … maybe when you pull the file to pieces, you could
generate XML. The reconstitution might then be able to be performed by a
much smaller program, using the XML as input.

Anyway, just a thought.

I happen to be doing precisely this at the moment (converting a binary file
into XML and back again). I prototyped it in Ruby, which took me about an
hour, then re-wrote it in C++ for speed … which took me a day! Of course,
that’s partly because I haven’t written a serious piece of C++ in five years,
but I prefer to think it’s because Ruby’s just so much better!

Fortunately, the speed-up was larger than the multiplier for Ruby → C++
conversion. It took me about five times as long to get the C++ going, but I
got a 20-fold improvement in speed.

Harry O.

I have just finished working on a Hierarchical Line parser
that uses regular expressions to identify structure in the file
and puts the structured file in an array or hash, as you desire.

If you think it will help, I will send it to you.

···

On Sunday, 12 January 2003 at 11:47:49 +0900, Louis Krupp wrote:

Meanwhile, I’m learning some things about Ruby. NArray will almost
certainly be better than using generic arrays for lists of what I
know will be numbers.

Another poster suggested DATA.read.split, and that would work if I
had only one array to worry about. Unfortunately, there are a
bunch of them, and they’re scattered throughout the generated
script.

Thanks to everyone for the replies!


Jim Freeze

If you live to the age of a hundred you have it made because very few
people die past the age of a hundred.
– George Burns

ts wrote:

Not the second word, the second element. For example actually you have

last = list;
while (last->nd_next) {

last = last->nd_next;
}

Try to replace it with

if (list->nd_next) {

last = list->nd_next->nd_end;
}
else {
last = list;
}

i.e. the first element give the size, the second give the end.

You just need to adapt one macro in eval.c to make it work (if I’m right)

I think I understand now. I’ll try it when I get a chance.

Thanks!

Louis