5 Stimmen

Effizientes strtod in Java?

Also, ich habe dieses Java-Programm, mit dem ich mehrere Terabyte an Daten verarbeite. Leistung ist ein Anliegen.

Ich habe die App analysiert, und ein großer Teil aller Speicherzuweisungen sowie eine große Menge an CPU-Zeit stammen von der Durchführung einer einfachen Operation:

Ich habe ein Array von ASCII-Zeichen. Ich weiß, dass die Zeichen von Offset i bis Offset j eine Gleitkommazahl darstellen. Ich muss diese Gleitkommazahl in ein double extrahieren.

Der naive Double.parseDouble(new String(buf, i, j - i)) erledigt die Aufgabe. Allerdings wird hier viel Zeit verbracht und viele Speicherzuweisungen erfolgen, wahrscheinlich weil:

  • new String() ein neues Objekt erstellt, ein internes char[] Array erstellt und die Zeichen in das Array kopiert;
  • Double.parseDouble() erstellt ein FloatingDecimal Objekt und erstellt auch ein char[] Array, wobei die Zeichen ebenfalls hineinkopiert werden.

All diese Zuweisungen und Kopiervorgänge sind nicht wirklich notwendig. Kann ich sie vermeiden?

Was ich wirklich möchte, ist eine strtod-ähnliche Funktion, die ein char[] (oder ein byte[]) sowie Start-/End-Offsets akzeptiert und einen double zurückgibt.

Irgendwelche Vorschläge? Sollte ich meine eigene Lösung entwickeln? Sollte ich einen JNI-Wrapper um strtod schreiben? Sollte ich eine bereits vorhandene Java-Bibliothek verwenden?

5voto

Thilo Punkte 248982

Ich würde den Quellcode für java.lang.Double betrachten, den Code kopieren, der parseDouble ausführt, in meine eigene Hilfsklasse einfügen und ihn so modifizieren, dass er direkt mit char[], offset und length funktioniert.

5voto

Peter Lawrey Punkte 511323

Was ich in der Vergangenheit gemacht habe, ist ein Parser für ByteBuffer schreiben (um die Konvertierung von Byte in Zeichen zu vermeiden) zu Double und umgekehrt. Wenn Sie vermeiden können, Objekte zu erstellen, kann es viel schneller sein. Dieser Ansatz funktioniert für dateigekoppelte Dateien und vermeidet einige Kopierkosten.

Der Kerncode sieht folgendermaßen aus. Er behandelt keine Exponenten, aber Sie können das hinzufügen.

@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);
}

Der vollständige Code

Es stoppt, sobald es einen Byte erhält, den es nicht erwartet, z.B. ein , oder \n

2voto

styken Punkte 74

Aus Neugier habe ich die strtod-Funktion in Java kopiert und einen ~10-fachen Geschwindigkeitsgewinn im Vergleich zur Double.parseDouble(String)-Methode erzielt (selbst ohne das Erstellen neuer Strings in der Schleife). Aber vielleicht reicht das für Ihre Implementierung nicht aus.

Das Mikrobenchmarking ergibt:

Double.parseDouble(): 1,6 Millionen Konvertierungen pro Sekunde
Java strtod() Methode: 10,5 Millionen Konvertierungen pro Sekunde

1voto

Wouter Lievens Punkte 3921

Wenn Sie eine effiziente C-Implementierung kennen, könnten Sie einen Wrapper dafür mit JNI schreiben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X