Выполняя некоторые консалтинговые работы для более крупных немецких компаний Future Technologies Group, я портировал около 6000 строк программного обеспечения на стороне сервера Java на Dart. Это должно помочь ответить на вопрос, может ли Dart эффективно использоваться на сервере. (Что само по себе дало бы зеленый свет для Дарта из-за поиска в пользу наличия одного языка для программирования на стороне клиента и на стороне сервера.)
Изучение Dart (с которым мне действительно понравилось работать) дало мне надежду на снижение производительности на 30-50% относительно Java, но в любом случае не хуже 100% (в два раза медленнее), что является отсечкой для вышеупомянутый процесс принятия решений.
Порт прошел гладко. Я многому научился. Единичные тесты были прекрасными. Но производительность оказалась крайне плохой... СЕМЬ времени медленнее по сравнению с Java-программой.
Профилирование кода выявило двух основных виновников: преобразование данных и ввода-вывода файлов. Может быть, я делаю что-то не так? Прежде чем я вернусь к своему клиенту, и они отменили исследование Дарта, я хотел бы найти несколько советов о том, как улучшить ситуацию. Позвольте начать с преобразования данных, преобразование собственных типов данных Dart в различные двоичные форматы, которые могут быть использованы для эффективной передачи и хранения данных.
Обычно эти преобразования просты и очень быстры, потому что ничего не нужно переводить из используемого внутреннего формата, но в основном хранятся в буфере. Я создал тестовую программу, которая каким-то образом отражает типичное использование этих преобразований в моей программе:
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();
}
Он основан на контрольной привязке от https://github.com/dart-lang/benchmark_harness. Для сравнения я использовал следующую Java-программу, основанную на порте тестовой привязки Dart от 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();
}
}
Код Java работает почти в 10 раз быстрее, чем код Dart на моей машине Intel Windows 7. Оба запускаются в режиме производства на своих виртуальных машинах.
Есть ли явная ошибка в коде? Или существуют различные классы Дарт, которые могут выполнять эту работу? Любое объяснение того, почему Дарт настолько медленнее с этими простыми преобразованиями? Или я полностью ошибаюсь в отношении производительности Dart VM?