Reduce number of Wrapper Objects in Dart Reduce number of Wrapper Objects in Dart dart dart

Reduce number of Wrapper Objects in Dart


I have a similar situation where I need to connect additional information with basic data types like String, int, double, ...
I didn't find a solution other than wrapping them.

What you can try is to optimize these wrapper classes by

  • making them const (with a const constructor
  • making the fields final where possible
  • others might be possible

EDIT
- You definitely want to get rid of all those switch statements.

  1. 0.134285 sec: without wrappers
  2. 0.645971 sec: with simplified constructor, operator <= (see below)
    using const constructor didn't make a notable difference (propably more important when converted to JS)
  3. 1.449707 sec: with simplified constructor
  4. 3.792590 sec: your code
class $PyNum2 {  final num _value;  const $PyNum2(this._value);  factory $PyNum2.from(value) {    switch ($getType2(value)) {      case 6:        return new $PyNum2(value);        break;      case 7:        try {          return new $PyNum2(num.parse(value));        } catch (ex) {          print("Invalid string literal for num parsing");          exit(1);        }        break;      case 5:        return new $PyNum2(value.value());        break;      default:        throw "Invalid input for num conversion";    }  }  value() => _value;  toString() => _value.toString();  operator +(other) => new $PyNum2(_value + other.value());  operator -(other) => new $PyNum2(_value - other.value());  operator *(other) => new $PyNum2(_value * other.value());  operator ~/(other) => new $PyNum2(_value ~/ other.value());  operator %(other) => new $PyNum2(_value % other.value());  operator ==(other) {    switch ($getType2(other)) {      case 6:        return _value == other;      case 5:        return _value == other.value();      default:        return false;    }  }  operator <(other) {    switch ($getType2(other)) {      case 6:        return _value < other;      case 5:        return _value < other.value();      default:        return true;    }  }  operator >(other) => !(this < other) && (this != other);  operator <=(other) => this.value() <= other.value(); //(this < other) || (this == other);  operator >=(other) => (this > other) || (this == other);}

see also:


I haven't tried this, but how about never wrapping the object and for any method you need in Python implement it as a standalone function with a type-case instead. So anything that is common between the two would run at full speed. Methods only implemented in one python class would be pretty quick, and you'd only be doing a lot of type tests for things that were megamorphic in Python.

Or just write a Python interpreter in Dart.

Even in the examples you give you'd probably do a lot better if you used const objects instead of allocating a new PyNum every time.


Sample code where the wrappers 6.3 times slower:

  1. Constructor only wrapps value. If you need consversion use anothers approach, eg. other additional constructors.
  2. Comparison operators splitted for reducing unnecessary types checks.
  3. Arithmetic operators improved, added type checks.
  4. Python types combined into group $PyType. This reduces types checks.

Unnecessary code removed from this sample.

import 'dart:io';fib(n) {  if (n <= 2) {    return 1;  } else {    return (fib((n - 1)) + fib((n - 2)));  }}fib2(n) {  if (n <= new $PyNum(2)) {    return new $PyNum(1);  } else {    return (fib2((n - new $PyNum(1))) + fib2((n - new $PyNum(2))));  }}main() {  measure("fib", () => stdout.writeln(fib(42)));  measure("fib2", () => stdout.writeln(fib2(new $PyNum(42))));}void measure(String msg, f()) {  var sw = new Stopwatch();  sw.start();  f();  sw.stop();  print("$msg: ${sw.elapsedMilliseconds}");}class $PyTypes {  static const $PyTypes NUM = const $PyTypes("NUM");  final String name;  const $PyTypes(this.name);}abstract class $PyType {  $PyTypes get pyType;}class $PyNum extends $PyType {  final int value;  $PyTypes get pyType => $PyTypes.NUM;  $PyNum(this.value);  operator +(other) {    if (other is $PyType) {      switch (other.pyType) {        case $PyTypes.NUM:          $PyNum pyNum = other;          return new $PyNum(value + pyNum.value);      }    } else if (other is int) {      return new $PyNum(value + other);    }    throw new ArgumentError("other: $other");  }  operator -(other) {    if (other is $PyType) {      switch (other.pyType) {        case $PyTypes.NUM:          $PyNum pyNum = other;          return new $PyNum(value - pyNum.value);      }    } else if (other is int) {      return new $PyNum(value - other);    }    throw new ArgumentError("other: $other");  }  operator ==(other) {    if (other is $PyType) {      switch (other.pyType) {        case $PyTypes.NUM:          $PyNum pyNum = other;          return value == pyNum.value;      }    } else if (other is int) {      return value == other;    }    throw new ArgumentError("other: $other");  }  operator <(other) {    if (other is $PyType) {      switch (other.pyType) {        case $PyTypes.NUM:          $PyNum pyNum = other;          return value < pyNum.value;      }    } else if (other is int) {      return value < other;    }    throw new ArgumentError("other: $other");  }  operator <=(other) {    if (other is $PyType) {      switch (other.pyType) {        case $PyTypes.NUM:          $PyNum pyNum = other;          return value <= pyNum.value;      }    } else if (other is int) {      return value <= other;    }    throw new ArgumentError("other: $other");  }  String toString() => value.toString();}