Need IO Optimization help

Hello:

We’re having a little shoot out here at work with Ruby, Perl and Tcl.
So far, Ruby kicked on a recursive Fibonacci(sp?) sequence with
Perl about 50% slower and Tcl 10x slower.

Next we’re looking at IO. So far, Perl is about as fast
as cp and Ruby is 50% slower and consumes over twice the
CPU (see table below):

ruby 60.07u 21.32s 1:31.62 88.8%
cp 0.01u 6.64s 0:53.34 12.4%
perl 16.79u 7.66s 0:58.76 41.6%

Oh, the Tcl results? The code is still being written. :slight_smile:

The Ruby code is below. I know there have been multiple
posts on ruby-talk about this with discussions about
sysread and read, but not being an IO expert, I have
only been able to follow these at a high level.

Could some expert look at the code below and tell me
what could be done to speed up the code below. Possibly
using #read or #sysread. Or, if someone has some ruby C
code, that would be cool.

#------ rw.rb
file = ARGV.shift

File.open(file + “.out”, “w”) { |of|
File.open(file).each {|line|
# do processing here
of.print line
}
}

···

#-------

Thanks


Jim Freeze

Different all twisty a of in maze are you, passages little.

require ‘ftools’

file = ARGV.shift

File.syscopy(file, “#{file}.out”)

if you want raw IO, you can use the begin … end chunk from ftools.

···

Jim Freeze (jim@freeze.org) wrote:

Could some expert look at the code below and tell me
what could be done to speed up the code below. Possibly
using #read or #sysread. Or, if someone has some ruby C
code, that would be cool.

#------ rw.rb
file = ARGV.shift

File.open(file + “.out”, “w”) { |of|
File.open(file).each {|line|
# do processing here
of.print line
}
}
#-------


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Hi,

···

At Fri, 11 Apr 2003 01:03:08 +0900, Jim Freeze wrote:

The Ruby code is below. I know there have been multiple
posts on ruby-talk about this with discussions about
sysread and read, but not being an IO expert, I have
only been able to follow these at a high level.

You can use File.cp in ftools.rb or FileUtils.cp in
fileutils.rb.


Nobu Nakada

Hi,

···

In message “Need IO Optimization help” on 03/04/11, Jim Freeze jim@freeze.org writes:

We’re having a little shoot out here at work with Ruby, Perl and Tcl.
So far, Ruby kicked on a recursive Fibonacci(sp?) sequence with
Perl about 50% slower and Tcl 10x slower.

Which version of Ruby are you using? Can you show us the whole
scripts?

						matz.

In my experience, the fastest way to access files (by far) is mmap.
I’ve written some C extensions to Ruby that use mmap to read files, and
they is dramatically faster the versions that I wrote using standard,
buffered IO. All of the stuff I have written is for a specific purpose
(e.g., a state machine to read character delimited files). Is there
any generic Ruby Extension that gives provides access within ruby to
mmap?

Following is some code that copied out of I out of one of my C
extensions to give you a quick idea of what it takes to read using mmap:

#include <fcntl.h> /* read/write flags /
#include <errno.h> /
error numbers /
#include <unistd.h> /
open, close functions /
#include <sys/types.h> /
typedefs /
#include <sys/stat.h> /
stat structures /
#include <sys/mman.h> /
mmap */

…snip…

/* the csv is passed as a parameter in the function I yanked this from
/
int len;
char buff;
char
path = RSTRING(csv)->ptr;
int fd = open(path, O_RDONLY, 0);
struct stat buffStat;
/
error control code /
if (fd < 0) {
if (errno == EMFILE || errno == ENFILE) {
rb_gc();
fd = stat(path, &buffStat);
}
if (fd < 0) {
close(fd);
rb_sys_fail(path);
}
}
if (stat(path, &buffStat) < 0) {
if (errno == EMFILE || errno == ENFILE) rb_gc();
if (stat(path, &buffStat)) {
close(fd);
rb_sys_fail(path);
}
}
/
here we actually do the wor /
len = buffStat.st_size;
buff = (char
)mmap(NULL, len, PROT_READ, 0, fd, 0);
close(fd); /* close the file, mmap doesn’t need it open /
if (buff == MAP_FAILED) rb_sys_fail(path); /
one last check for
validity */

From here, you just proceed to rifle through the file at a blazing
speed; e.g., something like:

while (position < len) {
position++;

if (rb_block_given_p() && buff[position] == “\n”) {
rb_yield(…);
}
}

etc.

Pass the line terminator as a parameter in the function, and yield the
accumulated result in between lines.

The only problem here is that mmap is part of the posix standard, so
that it will work fine under Unix, but I have no idea how well (or even
how, for that matter) it will work with Windows.

I hope this was useful,

Best,

Dave

···

On Thursday, April 10, 2003, at 12:03 PM, Jim Freeze wrote:

Hello:

We’re having a little shoot out here at work with Ruby, Perl and Tcl.
So far, Ruby kicked on a recursive Fibonacci(sp?) sequence with
Perl about 50% slower and Tcl 10x slower.

Next we’re looking at IO. So far, Perl is about as fast
as cp and Ruby is 50% slower and consumes over twice the
CPU (see table below):

ruby 60.07u 21.32s 1:31.62 88.8%
cp 0.01u 6.64s 0:53.34 12.4%
perl 16.79u 7.66s 0:58.76 41.6%

Oh, the Tcl results? The code is still being written. :slight_smile:

The Ruby code is below. I know there have been multiple
posts on ruby-talk about this with discussions about
sysread and read, but not being an IO expert, I have
only been able to follow these at a high level.

Could some expert look at the code below and tell me
what could be done to speed up the code below. Possibly
using #read or #sysread. Or, if someone has some ruby C
code, that would be cool.

#------ rw.rb
file = ARGV.shift

File.open(file + “.out”, “w”) { |of|
File.open(file).each {|line|
# do processing here
of.print line
}
}
#-------

Thanks


Jim Freeze

Different all twisty a of in maze are you, passages little.


David King Landrith
(w) 617.227.4469x213
(h) 617.696.7133

One useless man is a disgrace, two
are called a law firm, and three or more
become a congress – John Adams

public key available upon request

I see the syswrite src.sysread(bsize) in copy_stream, but
I need the data line by line to process. It’s just that
for our tests we are not doing any processing.
So, while a direct copy may be fast, it is not exactly
what I want.

def copy_stream( src, dest )
bsize = fu_stream_blksize(src, dest)
begin
while true
dest.syswrite src.sysread(bsize)
end
rescue EOFError
end
end

Is there a system call that reads line by line?

···

On Friday, 11 April 2003 at 1:27:25 +0900, nobu.nokada@softhome.net wrote:

Hi,

At Fri, 11 Apr 2003 01:03:08 +0900, > Jim Freeze wrote:

The Ruby code is below. I know there have been multiple
posts on ruby-talk about this with discussions about
sysread and read, but not being an IO expert, I have
only been able to follow these at a high level.

You can use File.cp in ftools.rb or FileUtils.cp in
fileutils.rb.


Jim Freeze

I can’t understand it. I can’t even understand the people who can
understand it.
– Queen Juliana of the Netherlands.

Sure:

ruby -v
ruby 1.8.0 (2003-04-10) [sparc-solaris2.8]

#- ruby
file = ARGV.shift

File.open(file + “.out”, “w”) { |of|
File.open(file).each {|line|
of << line
}
}

···

On Friday, 11 April 2003 at 2:16:50 +0900, Yukihiro Matsumoto wrote:

Hi,

In message “Need IO Optimization help” > on 03/04/11, Jim Freeze jim@freeze.org writes:

We’re having a little shoot out here at work with Ruby, Perl and Tcl.
So far, Ruby kicked on a recursive Fibonacci(sp?) sequence with
Perl about 50% slower and Tcl 10x slower.

Which version of Ruby are you using? Can you show us the whole
scripts?

#—

#- tcl
set inFileName [lindex $argv 0]

set ifid [open $inFileName r]
set ofid [open ${inFileName}.out w]

while { ! [eof $ifid] } {
gets $ifid newLine
puts $ofid $newLine
}

close $ifid
close $ofid
#—

#- perl
$file=shift;
open( inFile, “+<$file”);
open(outFile, “+>${file}.out”);
while (){
print outFile $_;
}
#-----

rubyprint 60.07u 21.32s 1:31.62 88.8%
ruby<< 58.30u 21.13s 1:30.41 87.8%
cp 0.01u 6.64s 0:53.34 12.4%
perl 16.79u 7.66s 0:58.76 41.6%
tcl 226.49u 8.74s 4:17.72 91.2%
tclcp 0.21u 7.80s 1:17.81 10.2%
rubysys 1.46u 12.18s 1:16.28 17.8%


Jim Freeze

Computers are not intelligent. They only think they are.

One thing I forgot to copy out of my extension: After the loop (and
within error handling routines as appropriate), you’ll want to clean up
by issuing a munmap command:

munmap(buff, len);

···

On Thursday, April 17, 2003, at 06:29 AM, David King Landrith wrote:

In my experience, the fastest way to access files (by far) is mmap.
I’ve written some C extensions to Ruby that use mmap to read files,
and they is dramatically faster the versions that I wrote using
standard, buffered IO. All of the stuff I have written is for a
specific purpose (e.g., a state machine to read character delimited
files). Is there any generic Ruby Extension that gives provides
access within ruby to mmap?

Following is some code that copied out of I out of one of my C
extensions to give you a quick idea of what it takes to read using
mmap:

#include <fcntl.h> /* read/write flags /
#include <errno.h> /
error numbers /
#include <unistd.h> /
open, close functions /
#include <sys/types.h> /
typedefs /
#include <sys/stat.h> /
stat structures /
#include <sys/mman.h> /
mmap */

…snip…

/* the csv is passed as a parameter in the function I yanked this from
/
int len;
char buff;
char
path = RSTRING(csv)->ptr;
int fd = open(path, O_RDONLY, 0);
struct stat buffStat;
/
error control code /
if (fd < 0) {
if (errno == EMFILE || errno == ENFILE) {
rb_gc();
fd = stat(path, &buffStat);
}
if (fd < 0) {
close(fd);
rb_sys_fail(path);
}
}
if (stat(path, &buffStat) < 0) {
if (errno == EMFILE || errno == ENFILE) rb_gc();
if (stat(path, &buffStat)) {
close(fd);
rb_sys_fail(path);
}
}
/
here we actually do the wor /
len = buffStat.st_size;
buff = (char
)mmap(NULL, len, PROT_READ, 0, fd, 0);
close(fd); /* close the file, mmap doesn’t need it open /
if (buff == MAP_FAILED) rb_sys_fail(path); /
one last check for
validity */

From here, you just proceed to rifle through the file at a blazing
speed; e.g., something like:

while (position < len) {
position++;

if (rb_block_given_p() && buff[position] == “\n”) {
rb_yield(…);
}
}

etc.

Pass the line terminator as a parameter in the function, and yield the
accumulated result in between lines.

The only problem here is that mmap is part of the posix standard, so
that it will work fine under Unix, but I have no idea how well (or
even how, for that matter) it will work with Windows.

I hope this was useful,

Best,

Dave

On Thursday, April 10, 2003, at 12:03 PM, Jim Freeze wrote:


David King Landrith
(w) 617.227.4469x213
(h) 617.696.7133

One useless man is a disgrace, two
are called a law firm, and three or more
become a congress – John Adams

public key available upon request

[mmap stuff deleted]

Thanks much. I’ll look into this.
I have already gone from a 18% loss to a 15.5% lead
over perl by switching to rb_io_gets.

Do you think that mmap will get me speeds near cp?

···

On Thursday, 17 April 2003 at 19:29:16 +0900, David King Landrith wrote:

In my experience, the fastest way to access files (by far) is mmap.
I’ve written some C extensions to Ruby that use mmap to read files, and
they is dramatically faster the versions that I wrote using standard,
buffered IO. All of the stuff I have written is for a specific purpose
(e.g., a state machine to read character delimited files). Is there
any generic Ruby Extension that gives provides access within ruby to
mmap?

Following is some code that copied out of I out of one of my C
extensions to give you a quick idea of what it takes to read using mmap:


Jim Freeze

A sine curve goes off to infinity or at least the end of the blackboard
– Prof. Steiner

“Jim Freeze” jim@freeze.org schrieb im Newsbeitrag
news:20030410124342.A7291@freeze.org

Hi,

The Ruby code is below. I know there have been multiple
posts on ruby-talk about this with discussions about
sysread and read, but not being an IO expert, I have
only been able to follow these at a high level.

You can use File.cp in ftools.rb or FileUtils.cp in
fileutils.rb.

I see the syswrite src.sysread(bsize) in copy_stream, but
I need the data line by line to process. It’s just that
for our tests we are not doing any processing.
So, while a direct copy may be fast, it is not exactly
what I want.

def copy_stream( src, dest )
bsize = fu_stream_blksize(src, dest)
begin
while true
dest.syswrite src.sysread(bsize)
end
rescue EOFError
end
end

Is there a system call that reads line by line?

Not as far as I know. But you can simulate this with String#each by
reading the whole file in one go and then iterating:

contents = f.read()
contents.each {|line|

processing…

}

or work directly on contents by using String#sub!

Did you try to use “file.sync= false” in conjunction with “file.flush”?
Did you try reading the whole file in with “contents = file.read()” and
then do line by line processing by doing "contents.each { |line| … }?

And: did you make sure that IO is the problem and not the processing part?

Regards

robert
···

On Friday, 11 April 2003 at 1:27:25 +0900, nobu.nokada@softhome.net wrote:

At Fri, 11 Apr 2003 01:03:08 +0900, > > Jim Freeze wrote:

Jim Freeze wrote:

Hi,

We’re having a little shoot out here at work with Ruby, Perl and Tcl.
So far, Ruby kicked on a recursive Fibonacci(sp?) sequence with
Perl about 50% slower and Tcl 10x slower.

FWIW, here is a benchmark suite you can use:

···

On Friday, 11 April 2003 at 2:16:50 +0900, Yukihiro Matsumoto wrote:

In message “Need IO Optimization help” > > on 03/04/11, Jim Freeze jim@freeze.org writes:

############################################################

iobenchmark.rb - benchmark to test IO read/write methods

############################################################
require “benchmark”
include Benchmark

outfile1 = “test1.out”
outfile2 = “test2.out”
outfile3 = “test3.out”
outfile4 = “test4.out”
outfile5 = “test5.out”
outfile6 = “test6.out”
outfile7 = “test7.out”
outfile8 = “test8.out”
outfile9 = “test9.out”

outfiles = Dir[“test*.out”]

outfiles.each{ |f|
File.delete(f)
}

string = “The quick brown fox jumped over the lazy dog’s back\n”
string_len = string.length # 52
iterations = 100000 # About 5mb per 100,000 iterations

bm do |x|
x.report(“print:”){
iterations.times{
File.open(outfile1,“a+”){ |f| f.print(string) }
}
}

x.report(“write:”){
iterations.times{
File.open(outfile2,“a+”){ |f| f.write(string) }
}
}

x.report(“syswrite:”){
iterations.times{
File.open(outfile3,“a+”){ |f| f.syswrite(string) }
}
}

x.report(“IO.foreach:”){
outfile = File.open(outfile4,“a+”)
IO.foreach(outfile1){ |line|
outfile.syswrite(line)
}
outfile.close
}

x.report(“File::gets:”){
infile = File.open(outfile1)
outfile = File.open(outfile5,“a+”)
while line = infile.gets
outfile.syswrite(line)
end
infile.close
outfile.close
}

x.report(“File::readline:”){
infile = File.open(outfile1)
outfile = File.open(outfile6,“a+”)
begin
while line = infile.readline
outfile.syswrite(line)
end
rescue EOFError
# do nothing
end
infile.close
outfile.close
}

x.report(“File::read:”){
infile = File.open(outfile1)
outfile = File.open(outfile7,“a+”)
begin
while line = infile.read(string_len)
outfile.syswrite(line)
end
rescue EOFError
# do nothing
end
infile.close
outfile.close
}

x.report(“File::sysread:”){
infile = File.open(outfile1)
outfile = File.open(outfile8,“a+”)
begin
while line = infile.sysread(string_len)
outfile.syswrite(line)
end
rescue EOFError
# do nothing
end
infile.close
outfile.close
}

x.report(“File::each:”){
outfile = File.open(outfile9,“a+”)
File.open(outfile1).each{|line|
outfile.syswrite(line)
}
outfile.close
}
end

In my experience, the fastest way to access files (by far) is mmap.

[mmap stuff deleted]

Thanks much. I’ll look into this.
I have already gone from a 18% loss to a 15.5% lead
over perl by switching to rb_io_gets.

The mmap should be dramatically faster than rb_io_gets for a few
reasons. For reasonably sized files, mmap will be as fast as rolling
through a string. For longer files, the information will get paged in
and out of memory as fast as the operating system will allow. So if
you’re writing your code in C, the bottleneck is likely to be either in
the kernel or in the OS, not your application.

By the way, you can also use mmap to write. So that you read from one
mmap and write to another.

mmap has its disadvantages, too. For example, you lose a lot of
granular control over how memory is allocated.

Do you think that mmap will get me speeds near cp?

I’ve never looked at the source for gnu fileutils, so I don’t know how
cp works. [BEGIN SPECULATION] The fact that much of it is a straight
bit copy of binary data may allow for optimizations that the more
general approach we’re using, in which our reading of the data allows
pretty much any use of it. So cp may well remain somewhat faster.
[END SPECULATION] Perhaps someone else on the list can speak on this
topic with more authority. I would, however, be very surprised if it
were dramatically faster than mmap commands.

Best,

Dave

···

On Thursday, April 17, 2003, at 06:45 AM, Jim Freeze wrote:

On Thursday, 17 April 2003 at 19:29:16 +0900, David King Landrith > wrote:


David King Landrith
(w) 617.227.4469x213
(h) 617.696.7133

One useless man is a disgrace, two
are called a law firm, and three or more
become a congress – John Adams

public key available upon request

“Jim Freeze” jim@freeze.org schrieb im Newsbeitrag
news:20030410124342.A7291@freeze.org

On Friday, 11 April 2003 at 1:27:25 +0900, nobu.nokada@softhome.net

Not as far as I know. But you can simulate this with String#each by
reading the whole file in one go and then iterating:

contents = f.read()
contents.each {|line|

processing…

}

or work directly on contents by using String#sub!

Hmm…, that would be a problem since the files are 200MB - 900MB.

Did you try to use “file.sync= false” in conjunction with “file.flush”?

No. I will try.

Did you try reading the whole file in with “contents = file.read()” and
then do line by line processing by doing "contents.each { |line| … }?

And: did you make sure that IO is the problem and not the processing part?

The processing right now is nil.

···

On Friday, 11 April 2003 at 2:25:48 +0900, Robert Klemme wrote:


Jim Freeze

Thanks

···

On Friday, 11 April 2003 at 2:37:05 +0900, Daniel Berger wrote:

Jim Freeze wrote:

FWIW, here is a benchmark suite you can use:


Jim Freeze

Below are the results I get from your benchmarks. I added
the last test which seems to be the fastest, AND is what
I was doing in my tests. To bad perl is 50% faster.

Can this be spead up with a C routine?

  user     system      total        real
  print:  7.680000   6.290000  13.970000 ( 14.107482)
  write:  7.680000   5.870000  13.550000 ( 13.634365)
  syswrite:  7.300000   6.090000  13.390000 ( 13.460545)
  IO.foreach:  2.150000   2.210000   4.360000 (  4.474877)
  File::gets:  2.150000   2.090000   4.240000 (  4.847931)
  File::readline:  2.200000   2.020000   4.220000 (  4.457981)
  File::read:  1.920000   2.160000   4.080000 (  4.214184)
  File::sysread:  2.170000   3.260000   5.430000 (  5.552458)
  File::each:  2.330000   2.040000   4.370000 (  4.375648)
  File::each: <<   1.780000   0.380000   2.160000 (  2.337520)

x.report("File::each: << "){
outfile = File.open(outfile9,“a+”)
File.open(outfile1).each{|line|
outfile << line
}
outfile.close
}

···

On Friday, 11 April 2003 at 2:37:05 +0900, Daniel Berger wrote:

Jim Freeze wrote:


Jim Freeze

“Life to you is a bold and dashing responsibility”
– a Mary Chung’s fortune cookie

We did tests comparing read/write speeds between C and cp
and we could not beat cp. I think it is the mmap that is
giving it the advantage.

So, it seems that an mmapio class would be useful.

require ‘mmapio’
MMapIO.new(writefile, “w”) { |f|
MMapIO.new(readfile).each { |line
# start rockin’
f << line
}
}

Has this been written before I start to do it myself?

···

On Thursday, 17 April 2003 at 22:11:55 +0900, David King Landrith wrote:

On Thursday, April 17, 2003, at 06:45 AM, Jim Freeze wrote:

On Thursday, 17 April 2003 at 19:29:16 +0900, David King Landrith > > wrote:

In my experience, the fastest way to access files (by far) is mmap.

[mmap stuff deleted]

Thanks much. I’ll look into this.
I have already gone from a 18% loss to a 15.5% lead
over perl by switching to rb_io_gets.

The mmap should be dramatically faster than rb_io_gets for a few
reasons. For reasonably sized files, mmap will be as fast as rolling
through a string. For longer files, the information will get paged in
and out of memory as fast as the operating system will allow. So if
you’re writing your code in C, the bottleneck is likely to be either in
the kernel or in the OS, not your application.

By the way, you can also use mmap to write. So that you read from one
mmap and write to another.

mmap has its disadvantages, too. For example, you lose a lot of
granular control over how memory is allocated.

Do you think that mmap will get me speeds near cp?

I’ve never looked at the source for gnu fileutils, so I don’t know how
cp works. [BEGIN SPECULATION] The fact that much of it is a straight
bit copy of binary data may allow for optimizations that the more
general approach we’re using, in which our reading of the data allows
pretty much any use of it. So cp may well remain somewhat faster.
[END SPECULATION] Perhaps someone else on the list can speak on this
topic with more authority. I would, however, be very surprised if it
were dramatically faster than mmap commands.


Jim Freeze

A city is a large community where people are lonesome together
– Herbert Prochnow

I have the source code for FreeBSD’s cp on my machine. If the file is under
8M it mmap’s the source for reading, and calls write() repeatedly on the
target file until it has been written completely. Otherwise it goes into a
simple read() / write() loop with a buffer of MAXBSIZE, which is 65536

There is a comment which says:

    /*
     * Mmap and write if less than 8M (the limit is so we don't totally
     * trash memory on big files.  This is really a minor hack, but it
     * wins some CPU back.
     */

I guess the main difference is that instead of two syscalls for each 64K of
data, you have one syscall for whatever the maximum size write() will accept
at once.

If you’re doing things where this is significant, then Ruby is probably the
wrong language for you - unless you write your entire read-process-write
loop in C as a single function, in which case using Ruby as a convenient way
to start it is fine. If it calls ‘yield’ on each line of the input then that
is almost certain to dominate…

Regards,

Brian.

···

On Thu, Apr 17, 2003 at 10:11:55PM +0900, David King Landrith wrote:

Do you think that mmap will get me speeds near cp?

I’ve never looked at the source for gnu fileutils, so I don’t know how
cp works. [BEGIN SPECULATION] The fact that much of it is a straight
bit copy of binary data may allow for optimizations that the more
general approach we’re using, in which our reading of the data allows
pretty much any use of it. So cp may well remain somewhat faster.
[END SPECULATION] Perhaps someone else on the list can speak on this
topic with more authority.

Looks like file.sync => false be default.

···

On Friday, 11 April 2003 at 2:32:01 +0900, Jim Freeze wrote:

On Friday, 11 April 2003 at 2:25:48 +0900, Robert Klemme wrote:

Did you try to use “file.sync= false” in conjunction with “file.flush”?

No. I will try.


Jim Freeze

Computer Science is merely the post-Turing decline in formal systems
theory.

I did some profiling of a very similar program, results posted at
http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb?key=-pg&cginame=namazu.rb&submit=Search&dbname=ruby-talk&max=50&whence=0

Conclusion: I can see no obvious bottlenecks. When you drill down and work
out how much time is spent in object creation and garbage collection, file
reading and scanning etc. it seems all pretty well balanced to me. The
reading and breaking-into-individual-lines accounts for 1.09 seconds out of
2.53 seconds

Regards,

Brian.

···

On Fri, Apr 11, 2003 at 03:10:27AM +0900, Jim Freeze wrote:

Below are the results I get from your benchmarks. I added
the last test which seems to be the fastest, AND is what
I was doing in my tests. To bad perl is 50% faster.

Can this be spead up with a C routine?

require 'mmapio'
MMapIO.new(writefile, "w") { |f|

   Good luck, for this

  MMapIO.new(readfile).each { |line
    # start rockin'
    f << line
  }
}

Has this been written before I start to do it myself?

yes, but don't expect that it will be faster ...

Guy Decoux