Doing some consulting work for a bigger German companies Future Technologies Group I have ported about 6000 lines of Java server side software to Dart. This should help to answer the question whether Dart can efficiently be used on the server. (Which itself would give a green light for Dart due to the searched for advantage of having one language for client and server side programming.)
Learning about Dart (which I really enjoyed working with) gave me an expectation of a performance penalty of 30-50% relative to Java but in any case no worse than 100% (twice as slow) which is the cutoff for the decision process mentioned above.
The port went smoothly. I learned a lot. Unit tests were fine. But the performance turned out to be extremly bad ... SEVEN times slower overall compared to the Java program.
Profiling the code revealed two main culprits: data conversion and file I/O. Maybe I'm doing something wrong? Before I go back to my client and they cancel their Dart research I would like to search some advice on how to improve things. Let's start with data conversion, the conversion of native Dart data types into various binary formats which can be used for effective transfer and storage of data.
Usually these conversions are simple and very fast because nothing has really to be converted from the used internal format but mostly stored into a buffer. I created a benchmark program which somehow reflects the typical use of these conversions in my program:
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();
}
It is based on the benchmark harness from https://github.com/dart-lang/benchmark_harness. For comparisions I used the following Java program based on a port of the Dart benchmark harness from 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();
}
}
The Java code runs almost exactly 10 times faster than the Dart code on my Intel Windows 7 machine. Both run in production mode on their respective VMs.
Is there an obvious error in the code? Or are there different Dart classes available to do the job? Any explanation as to why Dart is so much slower with these simple conversions? Or do I have completely wrong expectations with respect to Dart VM performance?