Flutter Analysis and Practice: Cross-End Solution Performance Comparison

Image for post
Image for post

After rewriting the product details page using Flutter, we compared the performance of the Flutter and native details pages. As a result, Flutter and native are comparable on mid-range and high-end models, while Flutter is smoother than native on low-end models. When using Flutter to make the details page, the Xianyu team did not pay more attention to performance optimization but gave priority to function implementation for fast launch. However, the test results showed that the Flutter implementation was unexpectedly better than the native implementation.

To this end, developers used Flutter not as a replacement for native but as part of an enhanced cross-end solution. The following analyzes whether Flutter or React Native is the better cross-end development solution from the perspective of performance.

4.4.1 Cross-End Solution Comparison

4.4.1.1 Comparison

4.4.1.2 Scenarios

4.4.1.3 Test Tools

  • ADB-based Shell script (Android): CPU, memory, and FPS

4.4.1.4 Testing Models

  • Android: Xiaomi Mi 2S (Android 5.0.2) and Samsung Galaxy S8 (Android 7.0)

4.4.2 Data Analysis and Comparison

4.4.2.1 iOS

Image for post
Image for post
Figure 4–14

2) Figure 4–15 shows the test result on the iPhone 6S (iOS 10.3.2.)

Image for post
Image for post
Figure 4–15

3) Conclusion

The test results indicate that Flutter’s FPS on low-end and mid-range iOS models is better than React Native. In terms of CPU utilization, Flutter’s performance is slightly lower than React Native on low-end models but slightly higher on mid-range models. Flutter’s initial memory is approximately the same as React Native on low-end models but exceeds React Native by about 30 MB on mid-range models. This is a result of Flutter’s different memory policies for low-end and mid-range models. The extra 30 MB memory is used by Dart virtual machines (VMs.) In models with less available memory, a Dart VM has small initial memory and allocates memory resources at runtime, which explains why low-end models show a greater CPU consumption. On mid-range models, more VM memory is pre-allocated for processing, reducing CPU intervention, and delivering a smoother experience. In short, the Flutter team has taken more targeted approaches for different models to deliver a better user experience.

4.4.2.2 Android

Image for post
Image for post
Figure 4–16

2) Figure 4–17 shows the test result on the Samsung Galaxy S8 (Android 7.0.)

Image for post
Image for post
Figure 4–17

3) Conclusion

1) In terms of CPU utilization, Flutter outperformed React Native on both the high-end and low-end models, especially on the low-end Xiaomi Mi 2S.
2) For Android models, smoothness indicators were added. Flutter’s FPS and smoothness performance significantly exceeded the React Native.
3) Notably, in terms of memory usage, Flutter’s initial memory on the Xiaomi Mi 2S exceeded React Native by 40 MB. React Native’s memory usage soared during tests, while Flutter’s remained stable. The React Native code needs to be optimized. For a given set of code, Flutter’s memory usage is similar on Android and iOS, while React Native’s code needs to be adjusted on one of these two systems. Therefore, Flutter outcompeted React Native in memory usage. Curiously, Flutter and React Native had the same initial memory on the Samsung Galaxy S8. The Xianyu team speculates that React Native also pre-allocates some memory resources on high-end Android models, but further research is needed to confirm this.

4.4.3 Summary

4.4.4 More Information

  • Run the top command through busybox to obtain the CPU utilization on Android
  • Accumulate the CPU utilization of each thread to obtain the total CPU utilization on iOS
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
  • To obtain the memory usage on iOS, use physfootprint during the test, which is the most accurate physical memory. However, residentsize (the resident memory usage, which cannot show the real memory changes) is used for many open-source apps.
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;

Original Source:

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store