2011-09-07 14 views
5

Bu yüzden, birkaç terabayt veriyi karıştırmak için kullandığım bu Java programına sahibim. Performans bir endişe.Java'da verimli strtod?

Ben app profilli ettik ve tüm bellek ayırmalarını büyük bir bölümünün yanı sıra CPU zamanının büyük bir bölümünün basit bir işlemi gerçekleştiren gelir: Ben ASCII karakter dizisi var

. i ofsetinden j ofsetini karakterlerin kayan nokta sayısını temsil ettiğini biliyorum. Bu kayan nokta sayısını double'a ayıklamam gerekiyor.

Nakil Double.parseDouble(new String(buf, i, j - i)) işini yapıyor.

  • new String() yeni bir nesne oluşturur oluşturur dahili char[] dizi ve kopya diziye karakterler;: çok zaman harcanmaktadır ve hafıza tahsisleri bir sürü nereden geldiğini Ancak bu çünkü muhtemelen vardır
  • Double.parseDouble() bir FloatingDecimal nesne oluşturur ve de bir char[] dizi da içine karakterleri kopyalama oluşturur.

Tüm bu ayırmalar ve tüm bu kopyalar gerçekten gerekli değildir. Onlardan sakınabilir miyim? Gerçekten ediyorum nasıl bir

bir char[] (veya byte[]) yanı sıra başlangıç ​​/ bitiş uzaklıklar almak ve bir double dönecekti bir strtod benzeri fonksiyonudur.

Herhangi bir öneriniz var mı? Kendim mi çıkarmalıyım? strtod etrafında bir JNI sarmalayıcı mı yazmalıyım? Zaten orada olan bazı Java kütüphanelerini kullanmalı mıyım?

+0

ikinci 10,5 metre dönüşümleri /, String.substring yöntemi kopyalamaz başlangıç ​​dizisi. String kurucu bir darboğaz ise yararlı olabilir. –

cevap

5

Geçmişte yaptığım şey, ByteBuffer'ın (kodlama dönüşümü için bayttan kaçınmak için) çift ve vize için bir ayrıştırıcı yazmasıdır. Herhangi bir nesne oluşturmamanız durumunda daha hızlı olabilir. Bu yaklaşım, bazı kopyalama masraflarından da kaçınarak bellek eşlemeli dosyalar için çalışır.

Temel kod aşağıdaki gibi görünür. Bu üsleri ele almıyor, ama bunu ekleyebilirsin.

@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

Bunu en kısa sürede buna mesela beklemediğini herhangi bir bayt alır gibi durur Bir , veya \n

+0

(+1) Nice, paylaştığınız için teşekkürler! – NPE

+0

Bir Double'i ByteBuffer olarak da kodlamak için kod vardır. –

5

Ben java.lang.Double için kaynak bakarsınız, benim kendi yardımcı sınıfına parseDouble yapar kodunu kopyalayıp doğrudan offset ve length ile char[] üzerinde çalışmak için değiştirmek.

+0

Bu seçenek, temel olarak bu 'FloatingDecimal' işlevinin ne olduğu ve yaklaşık 3K satırlık kod boyunca dağılmış bellek ayırmalarıyla ilgilidir. Ona yardım edebilecek olursam gerçekten hacklemiyorum (JNI rotası çok daha çekici geliyor). – NPE

1

Etkin bir C uygulaması biliyorsanız, bunun için JNI ile bir sarıcı yazabilirsiniz.

+0

Yine de JNI ek yükünü ekliyor olmalısınız (bunun bir miktar maliyeti var). – Thilo

+0

Statik bir işlevse, ek yük muhtemelen oldukça mantıklı. Öğrenmenin tek yolu denemek! –

2

Merak etme strtod işlevini Java'ya kopyaladım ve Double.parseDouble (String) yöntemiyle (döngü içinde yeni Dizeler oluşturmadan bile) karşılaştırmak için ~ 10 hıza ulaştım. Ama belki bu sizin uygulamanız için yeterli değil.

Mikro kıyaslama verir:

Double.parseDouble(): İkinci 1.6M dönüşümleri/
Java strtod() yöntemi: Aslında

+0

(+1) Başar, bunu yaptığınız için teşekkürler. Ilgisiz, hangi strtod'un uygulamasını aldınız? – NPE

+1

bu bağlantıdan: [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