I'm writing a GA in Ruby just for fun, but as I ran it for the first time, I realized that my fitness function (a game simulation) was far too slow. So I rewrote my fitness function in C and tried unsuccessfully to call it inline in Ruby using the RubyInline gem. Instead, I compiled it as a separate program and called it from Ruby, using the backtick operator.
Of course, I have to call the program once for every individual in the population, and initializing a program in a tight loop is very CPU intensive. What makes more sense is to move the loop to the C program, open a connection once in Ruby, and send a message to the C program telling it to run a simulation. The C program runs, outputs the fitness, and waits for more input. This way, initialization is only done once, reducing the overhead to create the address space for a new process every single time through the loop.
Here is how I accomplished this in Ruby:
#!/usr/bin/ruby
class Simulator
def initialize
@fh = IO.popen("./c_simulator", "w+")
end
def simulate(parameters)
@fh.puts parameters
@fh.gets.to_i
end
end
...
sim = Simulator.new
population.each{|p|
p.fitness = sim.simulate(p.getParameters)
}
IO.popen(prog, mode) opens a persistent connection to another process. The mode "w+" means an IO stream is opened for reading and writing to/from the process. You can then puts and gets to the handle just like you would from a file. And then on the C side:
int main(int argc, char** argv) {
char line[4096];
char *cptr;
do {
cptr = fgets(line, 4096, stdin);
if (cptr) {
// Do processing, assign fitness to fitness
printf("%d\n", fitness);
fflush(stdout);
}
} while (cptr != NULL);
return 0;
}
Fairly efficient way to call an external program from a Ruby loop, provided you are writing the external program yourself.
No comments:
Post a Comment