2013-04-17 5 views
7

Kiểm tra Code:Java tập tin hiệu suất IO giảm hơn 30% khi thêm nhiều lọ vào classpath

import java.io.BufferedReader; 
import java.io.FileReader; 
import java.io.IOException; 
import java.util.Map.Entry; 

public class ReadLine { 

    /** 
    * @param args 
    * @throws IOException 
    */ 
    public static void main(String[] args) throws IOException { 
     System.getenv(); 
     System.getProperties(); 

     BufferedReader br = new BufferedReader(new FileReader(args[0]), 2 << 17); 
     int lineTotal = 0; 
     int lineDone = 0; 
     long start = System.currentTimeMillis(); 
     long totalSincePre = System.currentTimeMillis(); 
     while (br.readLine() != null) { 
      lineTotal++; 
      if (lineTotal % 100000 == 0) { 
       long end = System.currentTimeMillis(); 
       System.out.println("total speed=" + lineTotal/(end - totalSincePre) + "k/s. curr speed=" 
         + (lineTotal - lineDone)/(end - start)); 
       start = end; 
       lineDone = lineTotal; 
      } 
     } 
     printEnv(); 
    } 

    static void printEnv() { 

     for (Entry<?, ?> e : System.getenv().entrySet()) { 
      System.out.println(e.getKey() + ":" + e.getValue()); 
     } 

     for (Entry<?, ?> e : System.getProperties().entrySet()) { 
      System.out.println(e.getKey() + ":" + e.getValue()); 
     } 
    } 
} 

thử nghiệm Env: Hệ điều hành: Linux, File Size: 7.2G (file văn bản csv, hơn 1k mỗi dòng), phiên bản java "1.6.0_32"

Test Run:

mã này được đóng gói vào một jar. Tôi chạy theo hai thử nghiệm trên cùng một máy chủ, sử dụng cùng một cái lọ, đọc cùng một tệp.

1) Chạy ./java -cp=my.jar ReadLine TestFile.txt Hiệu suất ổn định ở khoảng 150k dòng/s.

điều khiển đầu ra giống như:

total speed=251k/s. curr speed=251 
total speed=304k/s. curr speed=384 
total speed=323k/s. curr speed=371 
total speed=337k/s. curr speed=387 
total speed=350k/s. curr speed=414 
total speed=358k/s. curr speed=401 
total speed=363k/s. curr speed=395 
total speed=349k/s. curr speed=277 
total speed=304k/s. curr speed=150 
total speed=277k/s. curr speed=153 
total speed=258k/s. curr speed=154 
total speed=244k/s. curr speed=152 
total speed=233k/s. curr speed=152 
total speed=225k/s. curr speed=154 
total speed=218k/s. curr speed=153 
total speed=196k/s. curr speed=149 
total speed=193k/s. curr speed=146 
......stabled....... 
total speed=163k/s. curr speed=150 
total speed=162k/s. curr speed=155 
total speed=162k/s. curr speed=155 
total speed=162k/s. curr speed=149 
total speed=162k/s. curr speed=147 
total speed=162k/s. curr speed=150 
total speed=161k/s. curr speed=148 
total speed=161k/s. curr speed=145 
total speed=161k/s. curr speed=151 
total speed=161k/s. curr speed=154 
total speed=161k/s. curr speed=163 
total speed=161k/s. curr speed=179 

2) Không có thay đổi mã, lọ chỉ gia tăng (sử dụng trong môi trường sản xuất) vào classpath (cần thiết trong sản xuất, nhưng không phải nó chương trình này), như ./java -cp=my.jar:hundreds_of_other_jars ReadLine TestFile.txt. Việc thực hiện giảm xuống còn khoảng 90k dòng/s

total speed=312k/s. curr speed=383 
total speed=335k/s. curr speed=393 
total speed=348k/s. curr speed=395 
total speed=361k/s. curr speed=423 
total speed=369k/s. curr speed=414 
total speed=374k/s. curr speed=404 
total speed=342k/s. curr speed=214 
total speed=264k/s. curr speed=93 
total speed=224k/s. curr speed=95 
total speed=200k/s. curr speed=95 
total speed=182k/s. curr speed=94 
total speed=170k/s. curr speed=94 
total speed=161k/s. curr speed=95 
total speed=154k/s. curr speed=95 
total speed=148k/s. curr speed=93 
.....stabled..... 
total speed=139k/s. curr speed=92 
total speed=135k/s. curr speed=92 
total speed=132k/s. curr speed=92 
total speed=129k/s. curr speed=92 
total speed=127k/s. curr speed=92 
total speed=125k/s. curr speed=90 
total speed=123k/s. curr speed=91 
total speed=121k/s. curr speed=92 
total speed=120k/s. curr speed=89 
total speed=118k/s. curr speed=92 
total speed=117k/s. curr speed=91 
total speed=116k/s. curr speed=91 
total speed=115k/s. curr speed=91 
total speed=114k/s. curr speed=90 
total speed=113k/s. curr speed=91 

phân tích của tôi:

Sự khác biệt duy nhất là classpath. Đường dẫn lớp của bài kiểm tra thứ hai có hàng trăm JAR trong classpath. Nhưng không ai trong số họ được sử dụng trong chương trình này.

  • Không liên quan đến môi trường. Mã này được đóng gói vào JAR và cả hai thử nghiệm đều sử dụng cùng một JAR. Cả hai thử nghiệm chạy trên cùng một máy chủ, đọc cùng một tệp, sử dụng cùng một mã. Tôi cũng so sánh System.getEnv và System.getProperties, không có sự khác biệt nào ngoài classpath.
  • Không phải bộ nhớ cache của hệ điều hành. Điều này có thể được sao chép. Sau nhiều lần kiểm tra, kết quả là như nhau. Không có vấn đề mà thử nghiệm chạy đầu tiên.
  • jmap cho thấy việc sử dụng bộ nhớ không có sự khác biệt lớn và tất cả các thế hệ không được sử dụng nhiều.
  • jstack cho thấy các cuộc gọi stack cho cả thử nghiệm là nhiều khả năng được như

này

at java.io.FileInputStream.available(Native Method) 
at sun.nio.cs.StreamDecoder.inReady(StreamDecoder.java:343) 
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:304) 
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158) 
- locked <0xb4220388> (a java.io.FileReader) 
at java.io.InputStreamReader.read(InputStreamReader.java:167) 
at java.io.BufferedReader.fill(BufferedReader.java:136) 
at java.io.BufferedReader.readLine(BufferedReader.java:299) 
- locked <0xb4220388> (a java.io.FileReader) 
at java.io.BufferedReader.readLine(BufferedReader.java:362) 
at com.amazon.invhealth.metrics.transform.topasin.RL.main(RL.java:24) 

hoặc

at sun.nio.cs.UTF_8$Decoder.decodeArrayLoop(UTF_8.java:240) 
at sun.nio.cs.UTF_8$Decoder.decodeLoop(UTF_8.java:305) 
at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:544) 
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:298) 
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158) 
- locked <0xb4220388> (a java.io.FileReader) 
at java.io.InputStreamReader.read(InputStreamReader.java:167) 
at java.io.BufferedReader.fill(BufferedReader.java:136) 
at java.io.BufferedReader.readLine(BufferedReader.java:299) 
- locked <0xb4220388> (a java.io.FileReader) 
at java.io.BufferedReader.readLine(BufferedReader.java:362) 
at com.amazon.invhealth.metrics.transform.topasin.RL.main(RL.java:24) 

Nhìn vào các cuộc gọi stack những trường hợp thử nghiệm có khả năng sử dụng cùng một mã.

  • Điều này không được gây ra bởi một JAR nhất định trong đường dẫn lớp. Tôi đã cố gắng để loại bỏ 50% đầu tiên của classpath để chạy thử nghiệm, hiệu suất là khoảng 110k line/s. Sau đó, tôi loại bỏ 50% cuối cùng để chạy thử nghiệm, hiệu suất cũng là khoảng 110k dòng/s. Nếu loại bỏ hơn 2/3 lọ từ classpath, hiệu suất là khoảng 120k line/s. Vì vậy, từ bài kiểm tra, vấn đề hiệu suất này chỉ liên quan đến bao nhiêu JAR nằm trong classpath.
  • Sau đó, tôi đã cố gắng đóng gói tất cả các JAR này vào một JAR lớn. Đáng buồn là hiệu suất giảm từ 90k xuống 60k .... Vì vậy, để được chính xác, dựa trên thử nghiệm của tôi, hiệu suất giảm này là do có bao nhiêu lớp trong classpath.
  • Tôi đã chạy hai thử nghiệm trên một máy khác với một tệp khác (kích thước và định dạng tệp tương tự), kết quả giống nhau. Vì vậy, điều này chắc chắn có thể được sao chép.

NHƯNG tôi nghĩ điều này không có ý nghĩa. Tôi đã bỏ lỡ bất cứ điều gì? Nếu điều này thực sự là đúng nguyên nhân gốc rễ là gì?

---------- More gỡ lỗi -----------------

GC và Perm Kích

gia tăng -Xmx2432m -Xms256m -XX:MaxNewSize=700m -XX:MaxPermSize=1024m -XX:+PrintGCDetails cho cả hai bài kiểm tra. Có tất cả chỉ có PSYoungGen GC. Và hiệu suất là như nhau cho cả hai.

Output Chi tiết

dài CP một:

total speed=114k/s. curr speed=91 
[GC [PSYoungGen: 247888K->16K(238272K)] 248810K->938K(413056K), 0.0003290 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=113k/s. curr speed=92 
[GC [PSYoungGen: 238096K->16K(228864K)] 239018K->938K(403648K), 0.0003840 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=113k/s. curr speed=92 
[GC [PSYoungGen: 228816K->16K(220096K)] 229738K->938K(394880K), 0.0006030 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=112k/s. curr speed=92 
[GC [PSYoungGen: 219984K->16K(211584K)] 220906K->938K(386368K), 0.0004380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=111k/s. curr speed=93 
[GC [PSYoungGen: 211536K->16K(203584K)] 212458K->938K(378368K), 0.0005160 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=111k/s. curr speed=92 
[GC [PSYoungGen: 203472K->16K(195840K)] 204394K->938K(370624K), 0.0005920 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=110k/s. curr speed=94 
[GC [PSYoungGen: 195792K->16K(188608K)] 196714K->938K(363392K), 0.0004010 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 188496K->16K(181568K)] 189418K->938K(356352K), 0.0004440 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

...................... 

Heap 
PSYoungGen  total 145984K, used 81767K [0xc8560000, 0xd7780000, 0xf4160000) 
    eden space 145920K, 56% used [0xc8560000,0xcd535d18,0xd13e0000) 
    from space 64K, 25% used [0xd7760000,0xd7764000,0xd7770000) 
    to space 64K, 0% used [0xd7770000,0xd7770000,0xd7780000) 
PSOldGen  total 174784K, used 922K [0x5c160000, 0x66c10000, 0xc8560000) 
    object space 174784K, 0% used [0x5c160000,0x5c246ae8,0x66c10000) 
PSPermGen  total 16384K, used 2032K [0x1c160000, 0x1d160000, 0x5c160000) 
    object space 16384K, 12% used [0x1c160000,0x1c35c260,0x1d160000) 

Một Jar CP:

total speed=180k/s. curr speed=148 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0005300 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0004950 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0005020 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=179k/s. curr speed=150 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0005360 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0005190 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=178k/s. curr speed=151 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0005360 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0005400 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 87248K->16K(87296K)] 87904K->672K(262080K), 0.0003510 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
total speed=177k/s. curr speed=150 

Heap 
PSYoungGen  total 87296K, used 83826K [0xc8580000, 0xcdad0000, 0xf4180000) 
    eden space 87232K, 96% used [0xc8580000,0xcd758928,0xcdab0000) 
    from space 64K, 25% used [0xcdab0000,0xcdab4000,0xcdac0000) 
    to space 64K, 0% used [0xcdac0000,0xcdac0000,0xcdad0000) 
PSOldGen  total 174784K, used 656K [0x5c180000, 0x66c30000, 0xc8580000) 
    object space 174784K, 0% used [0x5c180000,0x5c224080,0x66c30000) 
PSPermGen  total 16384K, used 2022K [0x1c180000, 0x1d180000, 0x5c180000) 
    object space 16384K, 12% used [0x1c180000,0x1c379bb0,0x1d180000) 

JVM Initial

Tôi rất mong đợi này là nguyên nhân vì đây là hợp lý. Nhưng sau khi sử dụng mã sau:

String filepath = args[0]; 
while (true) { 
    BufferedReader br = new BufferedReader(new FileReader(filepath), 2 << 17); 
    System.out.println("Press Enter to start..."); 
    while (System.in.read() != '\n') 
     ; 
    int lineTotal = 0; 
    int linePre = 0; 
    long start = System.currentTimeMillis(); 
    long totalStart = System.currentTimeMillis(); 
    while (br.readLine() != null) { 
     lineTotal++; 
     if (lineTotal % 100000 == 0) { 
      long end = System.currentTimeMillis(); 
      System.out.println("total speed=" + lineTotal/(end - totalStart) + "k/s. curr speed=" 
        + (lineTotal - linePre)/(end - start)); 
      start = end; 
      linePre = lineTotal; 
     } 
    } 
} 

Chạy và nhấn vào thời gian chính, hiệu suất không thay đổi.

+0

Tôi đã thử phiên bản java "1.6.0_37", nó có nhiều cải tiến. Đối với "rất nhiều lọ trong cp" một, tốc độ là 150k line/s. Và đối với "jar đơn trong cp", tốc độ là 180k line/s. Khoảng 20% ​​hiệu suất giảm và vẫn có thể được sao chép, vẫn không làm cho sence cho tôi ... – DeepNightTwo

+1

cũng có giá trị nhìn vào biểu đồ gc với jconsole hoặc một số công cụ tương tự. cố gắng tăng kích thước permgen, có thể nó được đầy đủ vì tất cả những lọ và gc liên tục cố gắng để làm sạch nó. –

+1

Tôi tự hỏi liệu hệ thống IO vẫn đang bị đẩy trong quá trình khởi động JVM khi điểm chuẩn của bạn bắt đầu (ví dụ: bằng cách tìm kiếm danh sách dài các lọ và quét chúng); do đó JVM và điểm chuẩn của bạn đang cạnh tranh để truy cập tệp? Một cách để kiểm tra điều này là chạy điểm chuẩn của bạn hai hoặc ba lần, ngủ đủ lâu để bụi lắng xuống và sau đó thực hiện điểm chuẩn của bạn để loại bỏ các giá trị đầu tiên. –

Trả lời

1

Tôi đã tìm ra giải pháp nhưng vẫn không biết tại sao. Tôi chạy thử nghiệm một lần nữa bằng cách sử dụng JDK 1.7.0_21, hiệu suất của hai trường hợp thử nghiệm là như nhau, stabled tại 180k dòng/giây.

Như đã đề cập trong câu hỏi, một thử nghiệm khác đang sử dụng JDK 1.6.0_37, nó có hiệu suất được cải thiện nhưng vẫn có khoảng cách.

Vì vậy, hãy nâng cấp JDK nếu có thể.

Tôi sẽ rất cảm kích nếu ai đó biết nguyên nhân gốc rễ và cho tôi biết lý do :-)

0

Tôi nghĩ rằng nó giảm xuống theo cách bạn đang nhập tệp.

Nếu tốc độ là vấn đề, hãy luôn chỉ nhập các lớp mà bạn sử dụng.

Cách suy nghĩ của nó là máy tính đang làm nhiều hơn để làm những gì nó đã làm ít hơn để làm trước đây.

+0

Làm nhiều hơn những gì, mặc dù? Hầu như không có hoạt động nào trong vòng lặp đọc mà sẽ phụ thuộc vào classpath, và những cái hiển nhiên (tạo chuỗi mới chứa dòng mới) không nên có khả năng can thiệp vào. – Rup

+0

Thực ra mã không nhập bất kỳ lớp chưa sử dụng nào. – DeepNightTwo