2017-06-19 90 views
-1

我有一个目录中的非常大(〜300 MB)文件的列表,需要使用awk脚本多次过滤,每次使用不同的搜索参数。 我已经编写了一个程序,它使用fixedThreadPool执行程序生成多个线程,并且每个线程内的任务实现都会创建一个新的Runtime()对象,并通过一个使用bash shell执行的新Process来执行awk脚本脚本哪一个更快:从控制台读取或写入文件和阅读?

下面是一个示例代码:

类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); 
     } 

    } 
} 

类的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合并它们,最后读取合并的文件。

而且我也可以选择将每个Process的输出流的输出读入一个字符串并合并所有字符串。

哪种机制更快?

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

+0

为什么不自己尝试一下,看看哪个更快? – Cristina

回答

-1
  • 不使用Runtime()和'awk'脚本。而是将'awk'脚本翻译成Java。即使Java版本的运行速度比'awk'慢一点,混合'awk'和Java也会使程序复杂化。

  • 另外,不要为每个要处理的文件创建一个线程(每次创建新线程时都会有开销)。相反,只能使用固定数量的线程,并通过一些逻辑在这些线程之间平均分配文件。每个线程将按顺序处理几个文件。 (它需要更多的速度,然后把文件在某些​​共享文件系统 - 例如S3 - 然后使用多台电脑处理文件

+0

对于中等大小的文件(基本上,我认为300MB是“中等大小”的低端),awk肯定比在Java中实现该正则表达式要快得多 - Java实际上并未针对字符串操作进行优化。我同意“使用执行regexing的库(但以本地代码执行)”,而不是“尝试比手动实现的Java中的大量优化的本地程序的字符串操作更快”。 –

0

从视图的操作点:这不应该有事实上,很多现代操作系统都有系统调用,但实际上不应该有任何开销,但是,你在Java中做了一些可能会有一些开销的事情(整个缓冲读取器业务:为什么?)

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

为什么从Java中调用一个叫做awk的shell来解析表达式来过滤事物?

只需在Java中使用字符串/正则表达式引擎即可。 Java本身确实有一些速度限制,但我相信它们可能并不严重;在BufferedStreamReader(InputStreamReader)构造中有一些开销,所以如果你真的把性能的最后一点挤出来,你肯定会继续,并在本地代码中实现所有这些;再次,我不相信你会比使用Java带来的工具赢得更多。

算法上,你在做什么是坏的:通过每个文件一次,一次做所有的过滤,不要多次遍历每个文件。产生不必要的新进程也会产生额外的开销。

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

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

+0

**你肯定没有CPU绑定** 你的意思是说,由awk完成的过滤不是CPU绑定? 如果是的话,我可以在哪里学习如何编写这10行shell脚本? – gitmorty

+0

你基本上已经写过了。只需将从Java执行的所有AWK调用直接写入文本文件即可。我的意思是,我假设你熟悉'bash',因为你正在使用它来在你的java程序中执行脚本! –

+0

为什么在寻找这些微不足道的字符串模式这么简单的操作时会受CPU限制?你是否考虑过永久存储与CPU相比的缓慢程度?即使将所有这些文件放入RAM缓冲区(您的操作系统为您执行的操作),也可能会限制内存带宽。 –