Flutter之Isolate耗时

280 阅读2分钟

Flutter中使用Isolate主要分为3种方式:

  1. UI主线程

  2. compute

  3. LoadBalancer

下面我们通过下面的几种任务来计算整个耗时

3种任务:

  1. String => Map

  2. Map => Person Model

  3. Person Model => Map

在分别计算在4种数据量下的情况:

LoadBalancer 中多出3个打印:jsonDecode,PersonDemo.fromJson,person.toJson,是纯计算耗时的打印,不考虑数据拷贝

方式UI 主线程computeLoadBalancer
100条
10000条
100000条
1000000条

总结:

  1. 在非UI的Isolate中执行耗时任务不会卡顿UI

  2. 但是数据写入Isolate或者写出Isolate会导致何其交互的UI Isolate卡顿

  3. compute只会存在数据隔离读入一次的情况

  4. LoadBalancer存在数据隔离的读入和写入,需要double的内存

所以:

  1. 如果是计算为主的任务,建议使用非UI的Isolate中计算

  2. 如果是高频率的计算任务,建议使用LoadBalancer

  3. 如果是比较占内存的计算任务,建议使用compute

  4. 如果内存耗时远高于计算,建议还是UI的Isolate中计算,可以考虑任务队列

PS:耗时排序

jsonEncode > jsonDecode > Map => Model > Model => Map


class PersonDemo {
 late String name;
 late int age;
 late int height;
 late String desc;
 late List<String> otherStringList;
 late List<int> otherIntList;
 late Map<String,int> otherIntMap;
 late Map<String,String> otherStringMap;

 PersonDemo.fromJson(Map<String, dynamic> json) {
    name = json['name'];
    age = json['age'];
    height = json['height'];
    desc = json['desc'];
    otherStringList = json['otherStringList'].cast<String>();
    otherIntList = json['otherIntList'].cast<int>();
    otherIntMap = json['otherIntMap'].cast<String,int>();
    otherStringMap = json['otherStringMap'].cast<String,String>();
  }

 Map<String, dynamic> toJson() {
   return {
     'name': name,
     'age': age,
     'height': height,
     'desc': desc,
     'otherStringList': otherStringList,
     'otherIntList': otherIntList,
     'otherIntMap': otherIntMap,
     'otherStringMap': otherStringMap,
   };
 }

}

///提供一个大量耗时的对比demo,用于比较字符串转JSON Map和Map转模型的耗时差异
///type 0:主线程 Map 1:compute Map 2:loadBalance
Future<void> testSpeedTime(int type) async {
  // 生成100...个待转换的JSON字符串
  List<Map<String, dynamic>> jsonStringMap = [];
    for (int i = 0; i < 100000; i++) {
    int age = Random().nextInt(100);
    int height = Random().nextInt(200);
    Map<String, dynamic> jsonMap = {
      'name': 'Person $i',
      'age': age,
      'height': height,
      'desc': 'This is Person $i',
      'otherStringList': ['String 1', 'String 2'],
      'otherIntList': [1, 2],
      'otherIntMap': {'1': 2, '3': 4},
      'otherStringMap': {'key1': 'value1', 'key2': 'value2'},
    };

    jsonStringMap.add(jsonMap);
  }

  String jsonString = jsonEncode(jsonStringMap);



  // 字符串转JSON Map
  Stopwatch watch = Stopwatch()..start();

  List<dynamic> jsonList;
  if (type == 1) {
    jsonList = await compute(jsonDecode, jsonString) as List<dynamic>;
  } else if (type == 2) {
    jsonList = (await AsyncQueueManager().runTask(
      AsyncQueueType.jsonSerialize,
      (argument) {
        Stopwatch watch2 = Stopwatch()..start();
        final temp = jsonDecode(jsonString);
        print('jsonDecode: ${watch2.elapsedMilliseconds}ms');
        return temp;
      },
      null,
    )) as List;
  } else {
    jsonList = jsonDecode(jsonString);
  }

  print('String to JSON Map: ${watch.elapsedMilliseconds}ms');

  // JSON Map转Person列表
  watch.reset();

  List<PersonDemo> persons;
  if (type == 1) {
     persons =
        await compute((List<dynamic> jsonList) => jsonList.map((json) => PersonDemo.fromJson(json)).toList(), jsonList);
  } else if (type == 2) {
    persons = (await AsyncQueueManager().runTask(
      AsyncQueueType.jsonSerialize,
          (argument) {
        Stopwatch watch2 = Stopwatch()..start();
        final temp = jsonList.map((json) => PersonDemo.fromJson(json)).toList();
        print('PersonDemo.fromJson: ${watch2.elapsedMilliseconds}ms');
        return temp;
      },
      null,
    ));
  }  else {
     persons = jsonList.map((json) => PersonDemo.fromJson(json)).toList();
  }





  print('JSON Map to Person List: ${watch.elapsedMilliseconds}ms length:${persons.length}');

  watch.reset();

  List<Map<String, dynamic>> jsonStringList;
  if (type == 1) {
    jsonStringList =
        await compute((List<PersonDemo> persons) => persons.map((person) => person.toJson()).toList(), persons);
  } else if (type == 2) {
    jsonStringList = (await AsyncQueueManager().runTask(
      AsyncQueueType.jsonSerialize,
      (argument) {
        Stopwatch watch2 = Stopwatch()..start();
        final temp = persons.map((person) => person.toJson()).toList();
        print('person.toJson: ${watch2.elapsedMilliseconds}ms');
        return temp;
      },
      null,
    ));
  } else {
    jsonStringList = persons.map((person) => person.toJson()).toList();
  }


  print('Person List to JSON Map: ${watch.elapsedMilliseconds}ms length:${jsonStringList.length}');

  watch.reset();

  String jsonPersonString;
  if (type == 1) {
    jsonPersonString = await compute(jsonEncode, jsonStringList);
  } else if (type == 2) {
    jsonPersonString = (await AsyncQueueManager().runTask(
      AsyncQueueType.jsonSerialize,
          (argument) {
        Stopwatch watch2 = Stopwatch()..start();
        final temp = jsonEncode(jsonStringList);
        print('person.jsonEncode: ${watch2.elapsedMilliseconds}ms');
        return temp;
      },
      null,
    ));
  } else {
    jsonPersonString = jsonEncode(jsonStringList);
  }
  print('Person List to JSON String: ${watch.elapsedMilliseconds}ms');
}

参考文章:

Flutter/Dart中的异步编程之Isolate | 码客 (psvmc.cn)

flutter入门之理解Isolate及compute - 简书 (jianshu.com)

Dart 2.15 更新后 isolate 应该这么用 - 掘金 (juejin.cn)

Dart asynchronous programming: Isolates and event loops | by Kathy Walrath | Dart | Medium --- Dart 异步编程:Isolates 和事件循环 |通过凯西沃尔拉斯 |飞镖 |中等的