Выполняя некоторую консультационную работу для более крупных немецких компаний Future Technologies Group, я перенес около 6000 строк программного обеспечения на стороне сервера Java в Dart. Это должно помочь ответить на вопрос, можно ли эффективно использовать Dart на сервере. (Что само по себе дало бы зеленый свет Dart из-за того, что искали преимущество наличия одного языка для программирования на стороне клиента и сервера.)
Узнав о Dart (с которым мне очень понравилось работать), я ожидал снижения производительности на 30-50% по сравнению с Java, но в любом случае не хуже 100% (вдвое медленнее), что является отсечкой для упомянутого процесса принятия решения. выше.
Порт прошел гладко. Я многому научился. Модульные тесты были в порядке. Но производительность оказалась крайне плохой ... в СЕМЬ раз медленнее по сравнению с программой на Java.
Профилирование кода выявило двух основных виновников: преобразование данных и файловый ввод-вывод. Может я что не так делаю? Прежде чем я вернусь к своему клиенту, и он отменит свое исследование Dart, я хотел бы поискать несколько советов о том, как улучшить ситуацию. Начнем с преобразования данных, преобразования собственных типов данных 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 намного медленнее с этими простыми преобразованиями? Или у меня совершенно неверные ожидания относительно производительности Dart VM?
Endianness.HOST_ENDIAN
повысит производительность в 3 раза. В настоящее время я просматриваю код, чтобы увидеть, где скрывается еще один фактор, равный 3, и вернусь к вам, когда узнаю больше. - person Vyacheslav Egorov   schedule 19.01.2015slow
нативных вызова (ByteData_ToEndianIntXX и TypedData_SetIntXX)? Вы можете поменять местами байты за один вызов. Или вы можете просто поменять их местами в Dart. И зачем нужно столько проверок, если собственный код вызывается только из доверенного кода Dart? - person mezoni   schedule 20.01.2015toEndianXYZ
. Другой встроен оптимизатором. Проверки существуют, потому что лучше безопасно, чем сожалеть, у Dart есть отражение, поэтому лучше проверять все собственные записи, чтобы предотвратить случайную атаку через отражение. Оптимизатор предназначен для встраивания этого материала и исключения проверок._toEndianXYZ
существует потому, что а) он не оптимизирован, б) в Dart нет неродного способа обмена байтами. - person Vyacheslav Egorov   schedule 20.01.2015