哪个更快:从控制台读取或写入文件和阅读?

时间:2017-06-19 10:55:19

标签: java multithreading io parallel-processing

我有一个目录中非常大(~300 mb)的文件列表,需要使用awk脚本多次过滤,每次使用不同的搜索参数。 我编写了一个使用fixedThreadPool执行器来生成多个线程的程序,每个线程内的任务实现都是这样的,它创建一个新的Runtime()对象,并通过一个使用bash shell执行的新进程执行awk脚本。脚本

以下是示例代码:

Class MultiThreadingImpl:

public class MultiThreadingImpl {
    static List<File> filesList = new ArrayList<File>();

    public static void main(String[] args) {
        int numThreads = Runtime.getRuntime().availableProcessors();
        ExecutorService executor = Executors.newFixedThreadPool(numThreads);//creating a pool of 5 threads  

        File logsDir = new File("TestFilesDir");
        getLogFiles(logsDir);
        String[] searchKeys = {"123456","PAT1"};

        for (int i = 0; i < filesList.size() ; i++) {  
            Runnable worker = new WorkerThread(filesList.get(i),searchKeys[i]);  
            executor.execute(worker);//calling execute method of ExecutorService  
          }  
        executor.shutdown();  

        while (!executor.isTerminated()) {   }  

        System.out.println("Finished all threads"); 

    }

    private static void getLogFiles(File logsDir) {
        assert(logsDir.isDirectory());

        for(File f : logsDir.listFiles(
                new  FilenameFilter(){
                    public boolean accept(File dir, String name) {

                        return !name.endsWith("_result.txt");
                    }

                }
                )){
            filesList.add(f);
        }

    }
}

Class WorkerThread:

class WorkerThread implements Runnable {  
    private String outputFile; 
    private String searchKey;
    private File logFile;

    public WorkerThread(File logFile,String searchKey){  
        this.logFile = logFile; 
        this.searchKey = searchKey;
        this.outputFile = String.format(logFile.getName().replace(".txt", "") + "_result.txt");
    }  

    public void run() {  
        int res = 0;
        Runtime runtime = Runtime.getRuntime();
        String awkRegex = new StringBuilder("'/([0-9]{1}|[0-9]{2})[[:space:]][[:alpha:]]+[[:space:]][0-9]{4}/{n=0}")
                            .append("/"+searchKey+"/").append("{n=1} n' ").toString();
        String awkCommand = new StringBuilder("/usr/bin/awk ").append(awkRegex)
                .append(logFile.getAbsolutePath()).append(" &> ").append("/TestFilesDir").append(outputFile).toString();
        System.out.println(Thread.currentThread().getName() + ":: Command : " + awkCommand);
        String[] cmdList =  { "/bin/bash", "-c", awkCommand};

        try {
            final Process process = runtime.exec(cmdList);

            res = process.waitFor();

            BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
            BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));


            while (stdInput.readLine() != null) {
                //Emptying stream
            }

            StringBuffer strerror = new StringBuffer();
            String serror = null;
            while ((serror = stdError.readLine()) != null) {
                strerror.append(serror + "\n");
            }

            System.out.println(Thread.currentThread().getName() + ":: Process Exit value: " + res);


        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }  

} 

在这里,我可以选择为每个输入文件写入唯一的输出文件,然后使用cat合并它们,最后读取合并的文件。

我还可以选择阅读每个流程的输出。将流输出到字符串并合并所有字符串。

哪种机制更快?

还建议是否有办法让整个事情更快?

2 个答案:

答案 0 :(得分:0)

从运营的角度来看:这不应该有所作为。事实上,对于许多现代操作系统具有的splice系统调用,它实际上不应该有任何开销。但是,你在Java中做了一些可能有一些开销的事情(整个缓冲的读者业务:为什么?)

  

还建议是否有办法让整个事情更快?

为什么从Java调用一个调用awk的shell来解析你的表达式?

只需在Java中使用字符串/正则表达式引擎。

Java确实有一些速度限制,但我相信它们可能不会很严重; BufferedStreamReader(InputStreamReader)构造中存在一些开销,所以如果您真的在这方面挤出最后一点性能,那么您肯定只需继续并在本机代码中实现所有这些功能。 ;再说一次,与仅使用Java带来的工具相比,我不相信你会赢得更多。

从算法上讲,你正在做的事情很糟糕:一次浏览每个文件,一次完成所有过滤,不要多次浏览每个文件。不必要地产生新的过程也会产生额外的开销。

多线程在这里没有帮助。你绝对不是CPU限制,但IO绑定,多线程不能增加你的存储带宽 - 相反,它通常甚至会破坏线性访问并使速度变慢。

这一切都感觉它需要10行shell脚本而不是复杂的多线程Java应用程序,并且启动和执行起来会更快。

答案 1 :(得分:-1)

  • 不要使用Runtime()和&#39; awk&#39;脚本。而是翻译'awk&#39;脚本到Java。即使Java版本的运行速度略慢于awk&#39;,也要混合使用awk&#39;而Java只会使程序复杂化。

  • 此外,不要为每个要处理的文件创建一个线程(每次创建新线程时都会产生开销)。相反,只使用固定数量的线程,并使用一些逻辑在这些线程之间平均分配文件。每个线程将按顺序处理几个文件。 (你需要更高的速度,然后将文件放在一些共享文件系统中 - 例如S3 - 然后使用多台计算机处理你的文件。