Flutter Analysis and Practice: Cross-End Solution Performance Comparison
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
Flutter or React Native is selected as a cross-end solution.
4.4.1.1 Comparison
The Xianyu team reached out to cross-end development expert Car Guo on GitHub, who had developed two practical apps in Flutter and React Native.
4.4.1.2 Scenarios
1) The log-on is successful.
2) On the “Dynamic” page, tap the search button, and search for the keyword “Java.” Browse three pages at a normal speed and return to the third page after the fourth page is loaded.
3) Tap the “Trend” tab, browse “Feeds” until the bottom of the page, and then tap “Item” at the bottom. Browse details and three pages of information, and then return to the “My” tab.
4) View Feeds on the “My” tab to the bottom. Tap the search button in the upper-right corner and search for the keyword “C.” After browsing three pages, wait for the fourth page to load.
4.4.1.3 Test Tools
- Testing on mobile phones (iOS): CPU and memory Instruments: FPS
- ADB-based Shell script (Android): CPU, memory, and FPS
4.4.1.4 Testing Models
- iOS: iPhone 5C (iOS 9.0.1) and iPhone 6S (iOS 10.3.2)
- 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
1) Figure 4–14 shows the test result on the iPhone 5C (iOS 9.0.1.)
2) Figure 4–15 shows the test result on the iPhone 6S (iOS 10.3.2.)
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
1) Figure 4–16 shows the test result on the Xiaomi Mi 2S (Android 5.0.2.)
2) Figure 4–17 shows the test result on the Samsung Galaxy S8 (Android 7.0.)
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
Based on the previous test results, Flutter outperforms React Native in terms of both experience and test data. Xianyu is developed by an Android developer, and both Flutter and React Native are brand-new technology stacks. Car Guo tends to provide a consistent experience for users. He has not invested much in performance tuning. Therefore, Flutter can offer better performance and better user experience with the same investment.
4.4.4 More Information
- Run
dumpsys meminfo packageName
to obtain the memory usage on Android - 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;