2017-08-05 41 views
-5
public static void main(String[] args) { 
     List<String> data = new ArrayList<>(); 
     for (int i = 0; i < 10000000; i++) { 
      data.add("data" + i); 
     } 
     System.out.println("parallel stream start time" + System.currentTimeMillis()); 
     data.parallelStream().forEach(x -> { 
      System.out.println("data -->" + x); 
     }); 
     System.out.println("parallel stream end time" + System.currentTimeMillis()); 

     System.out.println("simple stream start time" + System.currentTimeMillis()); 
     data.stream().forEach(x -> { 
      System.out.println("data -->" + x); 
     }); 
     System.out.println("simple stream end time" + System.currentTimeMillis()); 

     System.out.println("normal foreach start time" + System.currentTimeMillis()); 
     for (int i = 0; i < data.size(); i++) { 
      System.out.println("data -->" + data.get(i)); 
     } 
     System.out.println("normal foreach end time" + System.currentTimeMillis()); 
    } 

輸出爲什麼迭代列表需要更多的時間,如果Java 8流功能使用?

並行流開始時間1501944014854

並行流結束時間1501944014970

簡單流開始時間1501944014970

簡單流結束時間1501944015036

正常的foreach STA室溫時間1501944015036

正常的foreach結束時間1501944015040

的全部時間

簡單流 - > 66

Parellem流 - > 116

簡單的foreach - > 4

在很多寫過parallelStream的並行執行的博客中,b y內部管理分佈式任務之間的線程和自動收集。

但根據上述實驗,很明顯注意到並行流花費更多的時間,然後簡單的流和正常的foreach。

爲什麼平行執行需要更多時間?在項目中使用這個功能會降低性能嗎?

由於提前

+8

打印已同步。 –

+0

同意管理線程和從線程收集數據也在「映射」操作的情況下..如果並行流處理10000000,然後應該花費較少的時間理想......所以我們應該避免使用並行流。 –

+0

雖然代碼的基礎很快,但在每次迭代中都包含一個打印。與其他計算相比,打印是一個速度緩慢的命令。它與常規的I/O命令甚至互聯網連接一樣。所以基本上這個印刷報表非常主宰你的時間測量的其他部分。再加上你總是有管理多線程的開銷。但是在大多數情況下,這種開銷比多線程的好處要小。但正如所說的,您的打印主宰了一切,嘗試去除它以接收更準確的時間測量。 – Zabuza

回答

2
  1. 你的測試基於I/O操作(最昂貴的操作)
  2. 如果你想使用並行流,你必須採取線程創建的時間開銷考慮到了。所以,只有當你的操作從中受益,然後使用它(這是重型操作的情況)。如果不是,那麼只需使用普通流或常規的for-loop。

測量的基本規則:

  1. 不要使用I/O操作。
  2. 重複相同的測試更多然後只是一次

因此,如果我們不得不再次重新制定測試方案,那麼我們可能有一個測試輔助類定義如下:

import java.util.HashMap; 
import java.util.Map; 
import java.util.UUID; 

public class Benchmark { 

    public static <T> T performTest(Callable<T> callable, int iteration, String name) throws Exception { 
     Map<String, Iteraion> map = new HashMap<>(); 
     T last = null; 
     for (int i = 0; i < iteration; i++) { 
      long s = System.nanoTime(); 
      T temp = callable.call(); 
      long f = System.nanoTime(); 
      map.put(UUID.randomUUID().toString(), new Iteraion(s, f)); 
      if (i == iteration - 1) { 
       last = temp; 
      } 
     } 
     System.out.print("TEST :\t" + name + "\t\t\t"); 
     System.out.print("ITERATION: " + map.size()); 
     long sum = 0l; 
     for (String i : map.keySet()) { 
      sum += (map.get(i).finish - map.get(i).start); 
     } 
     long avg = (sum/map.size())/1000000; 
     System.out.println("\t\t\tAVERAGE: " + avg + " ms"); 
     return last; 

    } 

    public interface Callable<T> { 
     T call() throws Exception; 
    } 

    static class Iteraion { 
     Long start; 
     Long finish; 

     public Iteraion(Long s, Long f) { 
      start = s; 
      finish = f; 
     } 
    } 
} 

現在我們可以使用不同的執行相同的測試,曾多次操作。以下代碼顯示使用兩種不同方案執行的測試。

import java.util.ArrayList; 
import java.util.List; 
import static java.lang.Math.*; 

@SuppressWarnings("unused") 
public class Test { 

    public static void main(String[] args) { 
     try { 
      final int iteration = 100; 
      final List<String> data = new ArrayList<>(); 

      for (int i = 0; i < 10000000; i++) { 
       data.add("data" + i); 
      } 

      /** 
      * Scenario 1 
      */ 
      Benchmark.performTest(new Callable<Void>() { 

       @Override 
       public Void call() throws Exception { 
        data.parallelStream().forEach(x -> { 
         x.trim(); 
        }); 
        return (Void) null; 
       } 

      }, iteration, "PARALEL_STREAM_ASSIGN_VAL"); 

      Benchmark.performTest(new Callable<Void>() { 

       @Override 
       public Void call() throws Exception { 
        data.stream().forEach(x -> { 
         x.trim(); 
        }); 
        return (Void) null; 
       } 
      }, iteration, "NORMAL_STREAM_ASSIGN_VAL"); 

      Benchmark.performTest(new Callable<Void>() { 

       @Override 
       public Void call() throws Exception { 
        for (int i = 0; i < data.size(); i++) { 
         data.get(i).trim(); 
        } 
        return (Void) null; 
       } 
      }, iteration, "NORMAL_FOREACH_ASSIGN_VAL"); 

      /** 
      * Scenario 2 
      */ 
      Benchmark.performTest(new Callable<Void>() { 

       @Override 
       public Void call() throws Exception { 
        data.parallelStream().forEach(x -> { 
         Integer i = Integer.parseInt(x.substring(4, x.length())); 
         double d = tan(atan(tan(atan(i)))); 
        }); 
        return (Void) null; 
       } 

      }, iteration, "PARALEL_STREAM_COMPUTATION"); 

      Benchmark.performTest(new Callable<Void>() { 

       @Override 
       public Void call() throws Exception { 
        data.stream().forEach(x -> { 
         Integer i = Integer.parseInt(x.substring(4, x.length())); 
         double d = tan(atan(tan(atan(i)))); 
        }); 
        return (Void) null; 
       } 
      }, iteration, "NORMAL_STREAM_COMPUTATION"); 

      Benchmark.performTest(new Callable<Void>() { 

       @Override 
       public Void call() throws Exception { 
        for (int i = 0; i < data.size(); i++) { 
         Integer x = Integer.parseInt(data.get(i).substring(4, data.get(i).length())); 
         double d = tan(atan(tan(atan(x)))); 
        } 
        return (Void) null; 
       } 
      }, iteration, "NORMAL_FOREACH_COMPUTATION"); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 
} 
  1. 第一種方案使用所trim()方法相同的測試100次爲包含10_000_000元件,因此它使用並行流,則普通流和最後的一個列表老學校for循環
  2. 第二種方案使用與第一種情況相同的技術對相同列表執行一些相對較重的操作,如tan(atan(tan(atan(i))))

的結果是:

// First scenario, average times 
Parallel stream: 78 ms 
Regular stream: 113 ms 
For-loop:  110 ms 

// Second scenario, average times 
Parallel stream: 1397 ms 
Regular stream: 3866 ms 
For-loop:   3826 ms 

請注意,您可以調試上面的代碼,那麼您注意到並行數據流的程序下名字[ForkJoinPool-1]創建三個額外的線程[ForkJoinPool-2][ForkJoinPool-3]

編輯: 順序流和for循環使用調用者線程

+0

還要注意,從並行化操作中獲得的好處很大程度上取決於您的操作系統**調度程序**,當然還有**您的機器實際擁有的核心數量。單個核心機器可能不會受益於很多這些技術...... – Zabuza

+1

順序流在調用者線程上執行,就像'for'循環一樣。 – Holger

+0

@Holger你是對的,經過幾次調試,現在我確定順序流使用調用者的線程。也許我被另一個暫停的調試會話弄糊塗了。謝謝 –

相關問題