在为一家更大的德国公司Future Technologies Group做一些咨询工作时,我已经将大约6000行Java服务器端软件移植到DART.这应该有助于回答是否可以在服务器上高效使用DART的问题.(这本身就为DART开了绿灯,因为人们一直在寻找一种语言用于客户端和服务器端编程的优势.)

了解Dart(我非常喜欢使用Dart)后,我预计相对于Java,性能会下降30-50%,但无论如何都不会低于100%(速度是Java的两倍),这是上述决策过程的截止点.

港口进展顺利.我学到了很多.单元测试很好.但是表演结果是非常糟糕的.总体上比Java程序慢7倍.

分析代码发现了两个主要的罪魁祸首:数据转换和文件I/O.也许我做错了什么?在我回到我的客户那里,他们取消了他们的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程序,该程序基于来自https://github.com/bono8106/benchmark_harness_java的DART基准线束的一个端口:

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

在我的英特尔Windows 7计算机上,Java代码的运行速度几乎正好是DART代码的10倍.两者都在各自的虚拟机上以生产模式运行.

代码中有没有明显的错误?或者是否有不同的DART类可用于执行此工作?有没有解释为什么DART在这些简单的转换中要慢得多?或者,我对DART VM性能的期望是完全错误的吗?

推荐答案

与直接类型化数组访问相比,字节数据方法(ByteData.setXYZByteData.getXYZ)在DART VM上的性能确实相当差.我们开始研究这个问题,初步结果令人振奋[1].

同时,您可以通过使用类型化数组(完整代码位于[2])将自己的转换转换为高字节顺序来解决这种令人遗憾的性能回归问题:

/// Writer wraps a fixed size Uint8List and writes values into it using
/// big-endian byte order.
class Writer {
  /// Output buffer.
  final Uint8List out;

  /// Current position within [out].
  var position = 0;

  Writer._create(this.out);

  factory Writer(size) {
    final out = new Uint8List(size);
    if (Endianness.HOST_ENDIAN == Endianness.LITTLE_ENDIAN) {
      return new _WriterForLEHost._create(out);
    } else {
      return new _WriterForBEHost._create(out);
    }
  }

  writeFloat64(double v);

}

/// Lists used for data convertion (alias each other).
final Uint8List _convU8 = new Uint8List(8);
final Float32List _convF32 = new Float32List.view(_convU8.buffer);
final Float64List _convF64 = new Float64List.view(_convU8.buffer);

/// Writer used on little-endian host.
class _WriterForLEHost extends Writer {
  _WriterForLEHost._create(out) : super._create(out);

  writeFloat64(double v) {
    _convF64[0] = v;
    out[position + 7] = _convU8[0];
    out[position + 6] = _convU8[1];
    out[position + 5] = _convU8[2];
    out[position + 4] = _convU8[3];
    out[position + 3] = _convU8[4];
    out[position + 2] = _convU8[5];
    out[position + 1] = _convU8[6];
    out[position + 0] = _convU8[7];
    position += 8;
  }
}

在您的测试中对此手动转换进行基准测试,可将性能提高约6倍:

import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'writer.dart';

class ConversionBenchmarkManual extends BenchmarkBase {

  Uint8List result;

  ConversionBenchmarkManual() : super("Conversion (MANUAL)");

  // The benchmark code.
  void run() {
    const int BufSize = 262144; // 256kBytes
    const int SetSize = 64;     // one "typical" set of data, gets repeated

    final w = new Writer(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 < (BufSize / SetSize); j++) {
      // The following represents some "typical" conversion mix:
      w.writeFloat64(doubleContent); doubleContent += 0.123;
      for (int k = 0; k < 8; k++) { // main use case
        w.writeFloat32(doubleContent); doubleContent += 0.123;
      }
      w.writeInt32(intContent); intContent++;
      w.writeInt32(intContent); intContent++;
      w.writeInt16(intContent); intContent++;
      w.writeInt16(intContent); intContent++;
      w.writeInt8(intContent);  intContent++;
      w.writeInt8(intContent);  intContent++;
      w.writeString("AsciiStrng");
      assert((offset % SetSize) == 0); // ensure the example content fits [SetSize] bytes
    }
    result = w.out; // only this can be used for further processing
  }
}

[1]https://code.google.com/p/dart/issues/detail?id=22107

[2]https://gist.github.com/mraleph/4eb5ccbb38904075141e

Dart相关问答推荐

基本数学函数的DART源代码

Flutter Web:无法用鼠标向下滚动(drag)(Flutter 2.5+)

Asset图像作为Card背景,Flutter 中顶部有文本

Flutter Expansion Tile -- 标题 colored颜色 变化和尾随动画箭头 colored颜色 变化

Flutter:Firebase FieldValue.serverTimestamp() 到 DateTime 对象

我需要在使用之前将我的 Dart 包发布到 pub.dartlang.org 吗?

try 部署到 Google AppEngine 时出现 Dev_appserver.py 错误

Dart VM 的性能与 Node.js 相比如何?

IndexedDB访问速度和效率

不推荐使用AnteStorStateofType,请改用findAncestorStateOfType

如何将整数中的ascii值转换为Flutter中的等效字符?

如何判断设备是否需要 SafeArea?

如何判断switch/case中对象的类型?

在 Dart 中使用固定长度列表有性能优势吗?

Dart 库中part和part of背后的原因是什么?

从 Dart 调用 javascript

Dart 中的多行字符串

用于 dartlang 的 REPL

Dart 如何匹配然后替换正则表达式

如何在 Dart 中获取文件名?