2011-09-07 44 views
5

Vì vậy, tôi có chương trình Java này mà tôi sử dụng để nhai qua vài terabyte dữ liệu. Hiệu suất là một mối quan tâm.Hiệu quả strtod trong Java?

Tôi đã cấu hình ứng dụng, và một phần lớn của tất cả các cấp phát bộ nhớ cũng như một phần lớn thời gian CPU đến từ thực hiện một thao tác đơn giản:

Tôi có một mảng các ký tự ASCII. Tôi biết rằng các ký tự từ offset i để bù đắp j đại diện cho một số dấu phẩy động. Tôi cần trích xuất số dấu phẩy động đó thành double.

Ngây thơ Double.parseDouble(new String(buf, i, j - i)) thực hiện công việc. Tuy nhiên, đây là nơi rất nhiều thời gian được chi và rất nhiều cấp phát bộ nhớ từ đâu đến, có lẽ vì:

  • new String() tạo ra một đối tượng mới, tạo ra một nội char[] mảng và bản sao các nhân vật vào mảng;
  • Double.parseDouble() tạo đối tượng FloatingDecimal và cũng tạo ra một mảng char[], cũng sao chép các ký tự vào đó.

Tất cả các phân bổ này và tất cả việc sao chép này không thực sự cần thiết. Tôi có thể tránh chúng không?

Những gì tôi thực sự muốn là một chức năng strtod -like rằng sẽ mất một char[] (hoặc một byte[]) cũng như hiệu số bắt đầu/kết thúc, và trả về một double.

Mọi đề xuất? Tôi có nên tự mình triển khai không? Tôi có nên viết trình bao bọc JNI xung quanh strtod không? Tôi có nên sử dụng một số thư viện Java đã có ở đó không?

+0

Trên thực tế, phương pháp String.substring không sao chép mảng ban đầu. Nó có thể hữu ích nếu String constructor là một nút cổ chai. –

Trả lời

5

Những gì tôi đã làm trong quá khứ là viết một trình phân tích cú pháp cho ByteBuffer (để tránh chuyển đổi byte sang byte mã hóa) thành gấp đôi và ngược lại. Nếu bạn có thể tránh tạo ra bất kỳ đối tượng nào thì nó có thể nhanh hơn nhiều. Cách tiếp cận này hoạt động cho các tập tin được ánh xạ bộ nhớ tránh một số chi phí sao chép.

Mã lõi trông giống như sau. Nó không xử lý số mũ, nhưng bạn có thể thêm số đó.

@Override 
public double read() throws BufferUnderflowException { 
    long value = 0; 
    int exp = 0; 
    boolean negative = false; 
    int decimalPlaces = Integer.MIN_VALUE; 
    while (true) { 
    byte ch = buffer.get(); 
    if (ch >= '0' && ch <= '9') { 
     while (value >= MAX_VALUE_DIVIDE_10) { 
     value >>>= 1; 
     exp++; 
     } 
     value = value * 10 + (ch - '0'); 
     decimalPlaces++; 
    } else if (ch == '-') { 
     negative = true; 
    } else if (ch == '.') { 
     decimalPlaces = 0; 
    } else { 
     break; 
    } 
    } 

    return asDouble(value, exp, negative, decimalPlaces); 
} 

The full code

Nó dừng lại ngay sau khi nó được bất kỳ byte nó không mong đợi ví dụ a , hoặc \n

+0

(+1) Rất tốt, cảm ơn bạn đã chia sẻ! – NPE

+0

Có mã để mã hóa đôi thành ByteBuffer. –

5

Tôi muốn nhìn vào các nguồn cho java.lang.Double, sao chép ra các mã mà không parseDouble đến lớp helper của riêng tôi và sửa đổi nó để làm việc trên char[] với offsetlength trực tiếp.

+0

Đây là một tùy chọn, ngoại trừ điều này về cơ bản là những gì 'FloatingDecimal' làm và nó về ~ 3K dòng mã với rất nhiều phân bổ bộ nhớ nằm rải rác trong suốt. Không thực sự ưa thích hack nếu tôi có thể giúp nó (các tuyến đường JNI âm thanh hấp dẫn hơn rất nhiều). – NPE

1

Nếu bạn biết triển khai C hiệu quả, bạn có thể viết trình bao bọc cho nó bằng JNI.

+0

Bạn muốn thêm chi phí JNI mặc dù (tôi cho rằng có một số chi phí cho nó). – Thilo

+0

Nếu đó là một chức năng tĩnh, chi phí có thể khá hợp lý. Cách duy nhất để tìm ra là thử nó! –

2

Hết sức tò mò Tôi đã sao chép hàm strtod vào Java và có ~ 10 lần tăng tốc so với phương thức Double.parseDouble (String) (thậm chí không tạo chuỗi mới trong vòng). Nhưng có lẽ điều đó không đủ cho việc triển khai của bạn.

Micro điểm chuẩn cho:

Double.parseDouble(): chuyển đổi 1,6 triệu/giây
Java strtod() phương pháp: 10.5M chuyển đổi/giây

+0

(+1) Tuyệt vời, cảm ơn bạn đã thực hiện việc này. Bạn không quan tâm đến việc triển khai 'strtod' nào? – NPE

+1

từ liên kết này: [http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8/missing/strtod.c](http://svn.ruby-lang.org/repos/ruby/branches /ruby_1_8/missing/strtod.c) – styken

Các vấn đề liên quan