Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient execution and output stream redirection of process spawned with Runtime.exec()

I have a script which executes a program several times, producing about 350 lines of output to both STDERR and STDOUT. Now, I need to execute the script in Java, thereby printing the output streams to their original destinations. So, basically, I execute the script from inside a Java class, maintaining the original behavior for the user.

The way I do this is inspired from suggestions like Reading streams from java Runtime.exec and, functionally, works fine.

Process p = Runtime.getRuntime().exec(cmdarray);
new Thread(new ProcessInputStreamHandler(p.getInputStream(), System.out)).start();
new Thread(new ProcessInputStreamHandler(p.getErrorStream(), System.err)).start();
return p.waitFor();

And the class ProcessInputStreamHandler:

class ProcessInputStreamHandler implements Runnable {
    private BufferedReader in_reader;
    private PrintStream out_stream;

    public ProcessInputStreamHandler(final InputStream in_stream, final PrintStream out_stream) {
        this.in_reader  = new BufferedReader(new InputStreamReader(in_stream));
        this.out_stream = out_stream;
    }

    @Override public void run() {
        String line;
        try {
            while ((line = in_reader.readLine()) != null) {
                out_stream.println(line);
            }
        } catch (Exception e) {throw new Error(e);}

        out_stream.flush();
    }
}

Now regarding my problem statement: While the execution of the script takes about 17 seconds, the "encapsulated" execution takes at least 21 seconds. Where do I lose these 4 or more seconds?

I already tried using a ProcessBuilder with redirection of STDERR to STDOUT, using POSIX vfork with libraries like https://github.com/axiak/java_posix_spawn, using a byte buffer instead of a BufferedReader... everything with no positive result at all.

Are there any suggestings? I understand that there will be some performance loss, but 4 seconds seem to be a bit much to me...

Appreciate any suggestions!

Best Regards and Thanks in Advance.

like image 676
dsteinhoefel Avatar asked Oct 29 '13 10:10

dsteinhoefel


2 Answers

The fastest way for your task is to use Java 7 and

return new ProcessBuilder(cmdarray).inheritIO().start().waitFor();

If that doesn’t help, I think there’s nothing you can do as every other approach would add even more code to your runtime environment that has to be processed.

like image 67
Holger Avatar answered Nov 20 '22 10:11

Holger


Don't know if it will improve performance or not, but you can try the NuProcess library which while also providing non-blocking (asynchronous) I/O will also use vfork on Linux, which does decrease process launch times (and memory overhead) quite a bit.

like image 1
brettw Avatar answered Nov 20 '22 10:11

brettw