最近遇到一个问题,来来回回花了好几天才解决,在此记录一下。
需求是这样的,项目中要用到图片播放,每秒钟大概有十几张图片展示,而图片的传输是通过原始的socket tcp传输的。
由于这些tcp流是服务器推送的,涉及到拆包和拼包,具体来说服务器可能一个tcp流包含多张图片,所以要对每张图片进行拆包,而它每个流可能不完整,需要等到下一次的流进行拼接然后再处理。
bug是我在调试的时候debug模式没有任何问题,图片播放流畅,当我发布版本测试的时候release模式下,图片的播放就开始卡顿了。
一脸懵逼,为啥release模式下性能反而更低呢?
后来仔细分析加日志发现一个大秘密:
release模式下socket.listen的ondata回调List<int>数据是debug模式的三四倍,release模式下list的length平均为1800000,而debug模式下list的length最大不超过600000, 这样看起来release模式的性能还是会高很多。
但是问题来了,上文说过,业务涉及到拆包拼包,所以我对list进行了操作,sublist拆包,addAll拼包,在如此大的量级下,这两个api及其耗时,平均处理每张图片需要220多毫秒,因此会导致播放图片卡顿。而在debug模式下,由于报文比较少,这两个api相对没有那么耗时,所以处理的速度比较快,基本上包来了瞬间就拆分处理完了,因此才有一种debug版本性能反而比release版本性能高的假象。
既然问题的原因已经发现了,那么解决起来就比较简单了,不论什么模式,我只要将每个报文控制在一个合理的范围内,那么效率自然就高了。但是苦于没有找到像c语言那样设置tcp接收缓冲区大小的api,因此,我不得不对报文进行一个拆分处理,每个报文拆分成1024 * 256这么长的一小段(这个长度实验了好久,跟具体业务有关),用for循环去处理,运行发现图片在release模式下也能够流畅的运行了。。
为了更加直观的看到性能差距,我写了一个测试代码。
import 'package:flutter/material.dart';
class TestPage extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Container(child: FlatButton(onPressed: (){
handleList();
handleList2();
}, child: Center(child: Text("onPress"),)), color: Colors.green,);
}
void handleList(){
List<int> list = createList(2000000);
List<int> lastRemain = List();
int perLength = 100001;
int count = 0;
print("***************************************************");
int startTime = DateTime.now().millisecondsSinceEpoch;
lastRemain.addAll(list);
while(true){
if(lastRemain.length < perLength){
break;
}
List<int> cur = lastRemain.sublist(0, perLength);
lastRemain = lastRemain.sublist(cur.length);
count++;
}
print("count = $count cost = ${DateTime.now().millisecondsSinceEpoch - startTime}");
}
void handleList2(){
List<int> list = createList(2000000);
int length = list.length;
List<int> lastRemain = List();
int segLength = 1024 * 256;
int perLength = 100001;
int count = 0;
print("------------------------------------------");
int startTime = DateTime.now().millisecondsSinceEpoch;
for(int i = 0; i < length; i += segLength) {
List<int> seg = list.sublist(i, i+segLength>length?length:i+segLength);
lastRemain.addAll(seg);
while(true){
if(lastRemain.length < perLength){
break;
}
List<int> cur = lastRemain.sublist(0, perLength);
lastRemain = lastRemain.sublist(cur.length);
count++;
}
}
print("count = $count cost = ${DateTime.now().millisecondsSinceEpoch - startTime}");
print("***************************************************");
}
List<int> createList(int n){
List<int> list = List();
for(int i=0; i<n; i++){
list.add(i);
}
return list;
}
}
代码比较简单,但是也能说明问题。handleList是我们直接处理大包,handleList2是我们将大包拆分为中包然后再处理,perLength 假设是每张图片的大小,我们的任务是每次得到一张图片,如果不足一张,那么留给下次拼包处理。
运行代码:
I/flutter (10901): ***************************************************
I/flutter (10901): count = 19 cost = 770
I/flutter (10901): ------------------------------------------
I/flutter (10901): count = 19 cost = 225
I/flutter (10901): ***************************************************
I/flutter (10901): ***************************************************
I/flutter (10901): count = 19 cost = 653
I/flutter (10901): ------------------------------------------
I/flutter (10901): count = 19 cost = 227
I/flutter (10901): ***************************************************
I/flutter (10901): ***************************************************
I/flutter (10901): count = 19 cost = 720
I/flutter (10901): ------------------------------------------
I/flutter (10901): count = 19 cost = 220
I/flutter (10901): ***************************************************
结果能够说明问题。
如果我们将原始报文增加到6000000会怎样呢?来看看结果吧:
I/flutter (14229): ***************************************************
I/flutter (14229): count = 59 cost = 6449
I/flutter (14229): ------------------------------------------
I/flutter (14229): count = 59 cost = 943
I/flutter (14229): ***************************************************
I/flutter (14229): ***************************************************
I/flutter (14229): count = 59 cost = 6283
I/flutter (14229): ------------------------------------------
I/flutter (14229): count = 59 cost = 932
I/flutter (14229): ***************************************************
I/flutter (14229): ***************************************************
I/flutter (14229): count = 59 cost = 6147
I/flutter (14229): ------------------------------------------
I/flutter (14229): count = 59 cost = 924
I/flutter (14229): ***************************************************
虽然这次的bug这样解决了,但是感觉flutter在某些情况下性能上还是会成为一个瓶颈,因为我在原生的ios上面没有发现这个bug。
flutter很好,路还很长,让我们一起奋斗前行!