-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Steps to reproduce
For instance, using a Pixel 8:
- have a continuously running animation
- have some isolates running with
compute(calculation, null) - run a perfetto trace with:
Perfetto command
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/trace \
<<EOF
buffers: {
size_kb: 63488
fill_policy: DISCARD
}
buffers: {
size_kb: 2048
fill_policy: DISCARD
}
data_sources: {
config {
name: "android.packages_list"
target_buffer: 1
}
}
data_sources: {
config {
name: "linux.process_stats"
target_buffer: 1
process_stats_config {
scan_all_processes_on_start: true
}
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "power/suspend_resume"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "sched/sched_wakeup_new"
ftrace_events: "sched/sched_waking"
ftrace_events: "sched/sched_process_exit"
ftrace_events: "sched/sched_process_free"
ftrace_events: "task/task_newtask"
ftrace_events: "task/task_rename"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/cpu_idle"
ftrace_events: "cpu/cpu_topology"
# atrace_apps: "com.example.flutter_threads"
}
}
}
data_sources {
config {
name: "track_event"
}
producer_name_filter: "com.example.flutter_threads"
}
duration_ms: 10000
EOFExpected results
Running a perfetto trace should reveal that UI thread, Raster thread and all Isolates should run on separate CPU cores.
Actual results
UI, Raster and DartWorker threads are all using the same CPU core
In Perfetto, hovering over a CPU sched slice reveals that all the slices for my app run on CPU 8

Zooming in, we can see that ui, raster and DartWorker are being scheduled on the same thread

Cause
flutter/engine#45673 is the PR introducing this change.
Of course, it's a great idea and implementation for most phones to improve the performance of Flutter! 🥳
However, I found 2 issues with this:
- some phones have only 1 core that gets recognized as performance core
- for instance the Pixel 8 has 9 cores
- 1 very fast
- 4 fast
- 4 quite slow
- for instance the Pixel 8 has 9 cores
- DartWorkers spawned from the UI thread can't set an affinity (Dart profiler thread can occupy fast core when running on Android. dart-lang/sdk#53748) so by default they will inherit the affinity of the main thread, and also run on the same core
- even for phones with multiple cores defined as "performance", Isolates will never use all the cores on the phone, only the ones marked as performance
- for instance, I have a Samsung A10s with 8 cores, including 4 marked as performance. If I spawn 4 isolates, then I might block the UI thread even though I have 4 more cores to use on the phone for background tasks
- even for phones with multiple cores defined as "performance", Isolates will never use all the cores on the phone, only the ones marked as performance
I think the Flutter team is aware of this already since @jonahwilliams tried a fix here flutter/engine#53136 but I think the impact is a bit more critical especially for apps running several isolates.
I'd be really happy to help with this in any way I can (submitting PRs for instance)! I think:
- Dart should indeed have a way to set the CPU affinity, and the Dart workers should be marked as "non performance cores"
- If a phone has only 1 big core, we also mark at least the 2nd fastest core as performance, so that UI and raster threads are guaranteed to run on separate cores
Code sample
Code sample
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() {
runApp(const MyApp());
}
int complexLoop(int? message) {
int startTime = DateTime.now().millisecondsSinceEpoch;
int sum = 0;
for (int i = 0; i < 1000000000; i++) {
sum += i;
}
int endTime = DateTime.now().millisecondsSinceEpoch;
print("TIME: ${endTime - startTime}");
return sum;
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
compute(complexLoop, null);
},
tooltip: 'Run calculation',
child: const Icon(Icons.play_arrow),
),
body: const Center(
child: FadeSquare(),
)));
}
}
class FadeSquare extends StatefulWidget {
const FadeSquare({super.key});
@override
State<FadeSquare> createState() => _FadeSquareState();
}
class _FadeSquareState extends State<FadeSquare>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)..repeat(reverse: true);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.ease,
);
}
@override
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
return FadeTransition(
opacity: _animation,
child: Container(
width: width,
height: width,
color: Colors.green,
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.24.0, on macOS 14.2.1 23C71 darwin-arm64, locale en-US)
• Flutter version 3.24.0 on channel stable at /Users/almouro/dev/tools/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 80c2e84975 (3 weeks ago), 2024-07-30 23:06:49 +0700
• Engine revision b8800d88be
• Dart version 3.5.0
• DevTools version 2.37.2
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/almouro/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• ANDROID_HOME = /Users/almouro/Library/Android/sdk
• ANDROID_SDK_ROOT = /Users/almouro/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15F31d
• CocoaPods version 1.15.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)
[✓] VS Code (version 1.92.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.94.0
[✓] Connected device (3 available)
• macOS (desktop) • macos • darwin-arm64 • macOS 14.2.1 23C71 darwin-arm64
• Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin • macOS 14.2.1 23C71 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 127.0.6533.120
! Error: Browsing on the local area network for iPhone de Inès Ryder. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
! Error: Browsing on the local area network for Salle de jeux. Ensure the device is unlocked and associated with the same local area network as this Mac. (code -27)
! Error: Browsing on the local area network for iPhone de BAM. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources
• All expected network resources are available.
• No issues found!