json_serializable.dart icon indicating copy to clipboard operation
json_serializable.dart copied to clipboard

enumDecode performance

Open mtc-jed opened this issue 3 years ago • 2 comments
trafficstars

I'm noticing json_serializable's enumDecode function iterates over the values of a map containing the enum values. Wouldn't it be better to generate a reversed map and use it for this purpose ? It could possibility impact performance over large quantities of data. Has anyone investigated this performance impact before ?

Relevant code at enum_helpers.dart line 76 :

for (var entry in enumValues.entries) {
    if (entry.value == source) {
      return entry.key;
    }
  }

mtc-jed avatar Apr 28 '22 07:04 mtc-jed

In theory, yes. One could imagine creating the inverse map from values to keys, but then you're increasing your compilation output – and adding complexity.

kevmoo avatar Apr 28 '22 18:04 kevmoo

Running this code on a Flutter release app :


void testMap() {
  var num = 'five';
  print(DateTime.now());
  for(int i = 0; i < 1000000; i++) {
    var res = $enumDecode(_$TestEnumEnumMap, num);
  }
  print(DateTime.now());
  for(int i = 0; i < 1000000; i++) {
    var res = $enumDecodeInverted(_$TestEnumEnumMapInverted, num);
  }
  print(DateTime.now());
}

const _$TestEnumEnumMap = {
  TestEnum.one: 'one',
  TestEnum.two: 'two',
  TestEnum.three: 'three',
  TestEnum.four: 'four',
  TestEnum.five: 'five',
  TestEnum.six: 'six',
  TestEnum.seven: 'seven',
  TestEnum.eigth: 'eigth',
  TestEnum.nine: 'nine',
};

const _$TestEnumEnumMapInverted = {
  'one': TestEnum.one,
  'two': TestEnum.two,
  'three': TestEnum.three,
  'four': TestEnum.four,
  'five': TestEnum.five,
  'six': TestEnum.six,
  'seven': TestEnum.seven,
  'eigth': TestEnum.eigth,
  'nine': TestEnum.nine,
};

K $enumDecodeInverted<K extends Enum, String>(
    Map<String, K> enumValues,
    String? source, {
      K? unknownValue,
    }) {
  if (source == null) {
    throw ArgumentError(
      'A value must be provided. Supported values: '
          '${enumValues.values.join(', ')}',
    );
  }

  // Will be null is source isn't in enumValues
  unknownValue = enumValues[source];

  if (unknownValue == null) {
    throw ArgumentError(
      '`$source` is not one of the supported values: '
          '${enumValues.values.join(', ')}',
    );
  }

  return unknownValue;
}

Yields :

I/flutter (20434): 2022-04-29 09:35:56.056985
I/flutter (20434): 2022-04-29 09:35:57.897399
I/flutter (20434): 2022-04-29 09:35:57.952474

I don't suppose this is "good-practice" benchmarking, but it does show a tremendous gap between the two implementations. I think it warrants looking into changing this implementation, although it might have been made as such for certain reasons I am not aware of.

mtc-jed avatar Apr 29 '22 07:04 mtc-jed