背景
论坛上很多小伙伴关心为什么闲鱼选择了 Flutter 而不选择其他跨端方案? 站在质量的角度, 高性能是一个很重的因素, 我们使用 Flutter 重写了宝贝详情页之后, 对比了 Flutter 和 Native 详情页的性能表现, 结论是中高端机型上 Flutter 和 Native 不相上下, 在低端机型上, Flutter 会比 Native 更加的流畅, 其实闲鱼团队在使用 Flutter 做详情页过程中, 没有更多地关注性能优化, 为了更快地上线, 也是优先功能的实现, 不过测试结果出来之后, 却出乎意料地优于原先的 Native 的实现 (具体的测试结果, 属于敏感数据, 要走披露流程, 伤不起...)
但是这样很显然不能敷衍过去, 仔细想了想, 确实 Flutter 的定位并不是要替代 Native, 他只想做一个极致的跨端解决方案, 所以还是要回到跨端解决方案的赛道, 给您从性能角度比一比, 谁才是更好的跨端开发方案?
参赛选手
- [Flutter]
- Flutter is Google's mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.
- [REACT NATIVE]
- We're working on a large-scale rearchitecture of React Native to make it more flexible and integrate better with native infrastructure in hybrid JavaScript/native apps.
鸣锣开赛
怎么比
怎么比较确实伤脑筋, 自己也写了一个 Flutter 和 一个 RN 的 App, 但是实在太丑陋, 担心大家关注点都到我的烂代码上了, 所以在 Github 上找到了一个跨端开发高手 Car Guo, 用 Flutter 和 RN 分别实现的一个实际可用的 App,Car Guo 谦虚表示其实也写的比较粗糙, 但是在我看来这个是具备真实使用场景的 App(Github 客户端 App, 提供丰富的功能, 旨在更好的日常管理和维护个人 Github), 还是有代表性的 [Flutter] https://github.com/CarGuo/GSYGithubAppFlutter [REACT NATIVE] https://github.com/CarGuo/GSYGithubApp
场景
1, 默认登录成功 2,"动态" 页, 点击搜索按钮, 搜索关键字 "Java", 正常速度浏览 3 页, 等第 4 页加载完成后回退 3, 点击 "趋势" 页 Tab, 浏览 Feeds 到页面底部, 点击最底部的 Item, 进入 Item 后, 浏览详情 + 浏览 3 页的动态后回退, 到 "我的"Tab 页 4, 查看 "我的"Feeds 到底部, 点击右上角搜索按钮, 搜索关键字 "C", 浏览 3 页后, 等第 4 页加载完成后场景结束
测试工具
iOS
掌中测 (iOS 端):CPU, 内存
- Instruments:FPS
- Android
基于 Adb 的 Shell 脚本: CPU, 内存, FPS
测试机型
- iOS:iPhone 5c 9.0.1 / iPhone 6s 10.3.2
- Android:Xiaomi 2s 5.0.2 / Sumsung S8 7.0
数据分析
- iOS
- iPhone 5c 9.0.1
iPhone 6s 10.3.2
测试结论
1,Flutter 在低端和中端的 iOS 机型上, FPS 的表现都优于 RN 2,CPU 的使用上 Flutter 在低端机上表现略差于 RN, 中端机型略优于 RN 3, 值得注意的是内存上的表现 (上图红色箭头区域),Flutter 在低端机型上的起始内存和 RN 几乎一致, 在中端机型上会多 30M 左右的内存 (分析为 Dart VM 的内存), 可以想到这应该是 Flutter 针对低端和中端机型上内存策略是不一样的, 可用内存少的机型, Dart VM 的初始内存少, 运行时进行分配 (这样也可以理解为什么在低端机上带来了更多的 CPU 损耗), 中端机器上预分配了更多的 VM 内存, 这样在处理时会更加的游刃有余, 减少 CPU 的介入, 带来更流畅的体验. 可以看出, Flutter 团队在针对不同机型上处理更加的细腻, 目的就是为了带来稳定流畅的体验.
- Android
- Xiaomi 2s 5.0.2
Sumsung S8 7.0
注: MFS - Max Frame Space: 指的是去掉 buffer 之后的两帧的时间差
测试结论
1,Flutter 在高低端机的 CPU 上的表现都优于 RN, 尤其在低端的小米 2s 上有着更优的表现 2,Android 端在原来 FPS 基础上增加了流畅度的指标, FPS 和流畅度的表现 Flutter 优于 RN(计算规则见附参考文章) 3,Android 端的内存也是值得关注的一点, 在小米 2s 上起始内存 Flutter 明显比 RN 多 40M,RN 在测试过程中内存飞涨, Flutter 相比之下会更稳定, 内存上 RN 侧的代码是需要调优的, 同一套代码 Flutter 在 Android 和 iOS 上并没有很大的差异, 但是 RN 的却要在单端调优, Flutter 在这项比拼上又更胜一筹. 比较奇怪的是三星 S8 上 Flutter 和 RN 的初始内存是一致的, 猜测是 RN 也 Android 高端机型上也会预分配一些内存, 具体细节还需要更进一步的研究.
升旗仪式
看了之前的数据, 做为裁判的我会把金牌颁给 Flutter, 在测试过程中的体验和数据上来看 Flutter 都优于 RN, 并且开发这个 App 的是一位 Android 的开发同学, Flutter 和 RN 对于他来说都是全新的技术栈, Car Guo 同学更倾向性地让大家得到一致性的使用体验, 性能方面并没有投入太多的时间进行调优, 由此看出 Flutter 在跨端开发上在同样投入的情况下, 可以获得更佳的性能, 更好的用户体验.
一些思考
拿到了这些数据, 也感受到 Flutter 带来福利, 那 Flutter 为什么可以做到这么流畅呢? Flutter 是如何优化了渲染, Dart VM 的 Runtime 是怎么玩的? 请大家继续关注后续解密文章, 感兴趣的同学欢迎加入闲鱼, 成为跨端解决方案的领军者.
参考
Android FPS & 流畅度: https://testerhome.com/topics/4775
Android 内存获取方式: dumpsys meminfo packageName
Android CPU 通过 busybox 执行 top 命令获取
iOS CPU 获取方式: 累计每个线程中的 CPU 利用率
- for (j = 0; j <thread_count; j++)
- {
- ATCPUDO *cpuDO = [[ATCPUDO alloc] init];
- char name[256];
- pthread_t pt = pthread_from_mach_thread_np(thread_list[j]);
- if (pt) {
- name[0] = '\0';
- __unused int rc = pthread_getname_np(pt, name, sizeof name);
- cpuDO.threadid = thread_list[j];
- cpuDO.identify = [NSString stringWithFormat:@"%s",name];
- }
- thread_info_count = THREAD_INFO_MAX;
- kr = thread_info(thread_list[j], THREAD_BASIC_INFO,(thread_info_t)thinfo, &thread_info_count);
- if (kr != KERN_SUCCESS) {
- return nil;
- }
- basic_info_th = (thread_basic_info_t)thinfo;
- if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
- tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;
- tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds;
- tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
- cpuDO.usage = basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
- if (container) {
- [container addObject:cpuDO];
- }
- }
- } // for each thread
复制代码
iOS 内存获取方式: 测试过程中使用的是 phys_footprint, 是最准确的物理内存, 很多开源软件用的是 resident_size(这个值代表的是常驻内存, 并不能很好地表现出真实内存变化, 这可以另开文章细谈)
- if ([[UIDevice currentDevice].systemVersion intValue] < 10) {
- kern_return_t kr;
- mach_msg_type_number_t info_count;
- task_vm_info_data_t vm_info;
- info_count = TASK_VM_INFO_COUNT;
- kr = task_info(mach_task_self(), TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info,&info_count);
- if (kr == KERN_SUCCESS) {
- return (vm_size_t)(vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap);
- }
- return 0;
- }
- task_vm_info_data_t vmInfo;
- mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
- kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
- if (result != KERN_SUCCESS)
- return 0;
- return (vm_size_t)vmInfo.phys_footprint;
复制代码
联系我们
如果对文本的内容有疑问或指正, 欢迎告知我们.
闲鱼技术团队是一只短小精悍的工程技术团队. 我们不仅关注于业务问题的有效解决, 同时我们在推动打破技术栈分工限制 (android/iOS/html5/Server 编程模型和语言的统一), 计算机视觉技术在移动终端上的前沿实践工作. 作为闲鱼技术团队的软件工程师, 您有机会去展示您所有的才能和勇气, 在整个产品的演进和用户问题解决中证明技术发展是改变生活方式的动力.
来源: https://juejin.im/post/5b9606055188255c7c6541c3