Einige Beratungsarbeit für eine größere deutsche Firma Future Technologies Group Ich habe etwa 6000 Zeilen Java-Server-Software auf Dart portiert. Dies sollte helfen, die Frage zu beantworten, ob Dart effizient auf dem Server verwendet werden kann. (Die selbst würde ein grünes Licht für Dart geben aufgrund der nach Vorteil einer Sprache für die Client- und Server-Seite-Programmierung.)Wie verbessert man die Dart-Performance der Datenkonvertierung in/aus der Binärdatei?
über Dart Lernen hat mir (was ich wirklich genossen die Arbeit mit) eine Erwartung einer Leistung Strafe von 30-50% relativ zu Java, aber in jedem Fall nicht schlechter als 100% (doppelt so langsam), was der Grenzwert für den oben erwähnten Entscheidungsprozess ist.
Der Port lief reibungslos. Ich habe viel gelernt. Unit Tests waren in Ordnung. Aber die Performance erwies sich als extrem schlecht ... SEVEN mal langsamer im Vergleich zum Java-Programm.
Das Profiling des Codes ergab zwei Hauptschuldige: Datenkonvertierung und Datei-I/O. Vielleicht mache ich etwas falsch? Bevor ich zu meinem Kunden zurückkehre und die Dart-Forschung abbricht, möchte ich einen Ratschlag zur Verbesserung der Dinge suchen. Beginnen wir mit der Datenkonvertierung, der Umwandlung von nativen Dart-Datentypen in verschiedene Binärformate, die für die effektive Übertragung und Speicherung von Daten verwendet werden können.
Normalerweise sind diese Konvertierungen einfach und sehr schnell, da nichts wirklich vom verwendeten internen Format konvertiert werden muss, sondern größtenteils in einem Puffer gespeichert wird. Ich habe ein Benchmark-Programm, das irgendwie die typische Verwendung dieser Umwandlungen in meinem Programm widerspiegelt:
import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart';
// Create a new benchmark by extending BenchmarkBase
class ConversionBenchmark extends BenchmarkBase {
Uint8List result;
ConversionBenchmark() : super("Conversion");
// The benchmark code.
void run() {
const int BufSize = 262144; // 256kBytes
const int SetSize = 64; // one "typical" set of data, gets repeated
ByteData buffer = new ByteData(BufSize);
double doubleContent = 0.0; // used to simulate double content
int intContent = 0; // used to simulate int content
int offset = 0;
for (int j = 0; j < buffer.lengthInBytes/SetSize; j++) {
// The following represents some "typical" conversion mix:
buffer.setFloat64(offset, doubleContent); offset += 8; doubleContent += 0.123;
for (int k = 0; k < 8; k++) { // main use case
buffer.setFloat32(offset, doubleContent); offset += 4; doubleContent += 0.123;
}
buffer.setInt32(offset, intContent); offset += 4; intContent++;
buffer.setInt32(offset, intContent); offset += 4; intContent++;
buffer.setInt16(offset, intContent); offset += 2; intContent++;
buffer.setInt16(offset, intContent); offset += 2; intContent++;
buffer.setInt8(offset, intContent); offset += 1; intContent++;
buffer.setInt8(offset, intContent); offset += 1; intContent++;
buffer.buffer.asUint8List(offset).setAll(0, "AsciiStrng".codeUnits); offset += 10;
// [ByteData] knows no other mechanism to transfer ASCII strings in
assert((offset % SetSize) == 0); // ensure the example content fits [SetSize] bytes
}
result = buffer.buffer.asUint8List(); // only this can be used for further processing
}
}
main() {
new ConversionBenchmark().report();
}
Es ist auf dem Benchmark-Gurtzeug von https://github.com/dart-lang/benchmark_harness basiert. Für comparisions verwendete ich das folgende Java-Programm basiert auf einem Port des Geschirrs Dart-Benchmark https://github.com/bono8106/benchmark_harness_java:
package ylib.tools;
import java.nio.ByteBuffer;
public class ConversionBenchmark extends BenchmarkBase {
public ByteBuffer result;
public ConversionBenchmark() { super("Conversion"); }
// The benchmark code.
@Override protected void run() {
final int BufSize = 262144; // 256kBytes
final int SetSize = 64; // one "typical" set of data, gets repeated
ByteBuffer buffer = ByteBuffer.allocate(BufSize);
double doubleContent = 0.0; // used to simulate double content
int intContent = 0; // used to simulate int content
for (int j = 0; j < (buffer.capacity()/SetSize); j++) {
// The following represents some "typical" conversion mix:
buffer.putDouble(doubleContent); doubleContent += 0.123;
for (int k = 0; k < 8; k++) { // main use case
buffer.putFloat((float)doubleContent); doubleContent += 0.123;
}
buffer.putInt(intContent); intContent++;
buffer.putInt(intContent); intContent++;
buffer.putShort((short)intContent); intContent++;
buffer.putShort((short)intContent); intContent++;
buffer.put((byte)intContent); intContent++;
buffer.put((byte)intContent); intContent++;
buffer.put("AsciiStrng".getBytes());
//assert((buffer.position() % SetSize) == 0); // ensure the example content fits [SetSize] bytes
}
buffer.flip(); // needed for further processing
result = buffer; // to avoid the compiler optimizing away everything
}
public static void main(String[] args) {
new ConversionBenchmark().report();
}
}
Der Java-Code läuft fast genau 10-mal schneller als die Dart-Code auf meinem Intel Windows 7 Maschine. Beide laufen im Produktionsmodus auf ihren jeweiligen VMs.
Gibt es einen offensichtlichen Fehler im Code? Oder gibt es verschiedene Dart-Klassen für diese Aufgabe? Irgendeine Erklärung, warum Dart mit diesen einfachen Konvertierungen so viel langsamer ist? Oder habe ich völlig falsche Erwartungen in Bezug auf die Performance von Dart VM?
ist Umstellung auf Big-Endian wichtig für Deine Ziele? wir haben es nicht optimiert. Das Übergeben von 'Endianness.HOST_ENDIAN' würde die Leistung um den Faktor 3 verbessern. Ich schaue mir gerade den Code an, um zu sehen, wo ein weiterer Faktor von 3 sich versteckt. Ich melde mich wieder, wenn ich mehr weiß. –
In der portierten Software ist Big Endian eine Vorbedingung, um mit Java generierten Datensätzen kompatibel zu bleiben. Für neue Software ist es vielleicht nicht so wichtig. Aber warum macht der Austausch von 2 oder 4 Bytes einen so großen Leistungsunterschied? Das kann ohne zusätzliche Prüfung gemacht werden, oder? –
Ich sehe, fair genug. Es hat einen Treffer, weil wir die Konvertierung nicht optimiert haben (niemand hat es vorher benutzt oder uns nicht über die Langsamkeit informiert): um einen Wert BE -> LE zu konvertieren, würden wir durch viele Hoops springen. Dies ist jedoch nicht schwer zu optimieren - ich habe damit begonnen und sollte heute vorläufige Zahlen erhalten [https://code.google.com/p/dart/issues/detail?id=22107]. –