2017-06-19 44 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緩衝區(您的操作系統爲您執行的操作),也可能會限制內存帶寬。 –