最近遇到一個問題,來來回回花了好幾天才解決,在此記錄一下。
需求是這樣的,項目中要用到圖片播放,每秒鐘大概有十幾張圖片展示,而圖片的傳輸是通過原始的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很好,路還很長,讓我們一起奮鬥前行!