It is widely known that Android is an Operating System with much room for custom configuration. This is in part due to the usage of the Linux kernel at its core. Today, we are going to discuss the configuration of one of the lowest-level parts of Android’s kernel: its Task Scheduler. With the right Task Scheduler configuration, you can boost performance on Android. Keep reading to find out how you can tweak your device for highest performance.
But What Exactly is a Task Scheduler?
A system’s Task Scheduler is responsible for distributing CPUs among a large number of running tasks. It does that by giving each task some run time on the CPU and swapping it out with another one on specific time intervals. In general, there are two types of task scheduling: preemptive scheduling and cooperative scheduling.
In preemptive scheduling, the kernel decides when to preempt (=swap) a running task. It can do that at almost any time, based on complex scheduling algorithms. In contrast, in cooperative scheduling, each task is responsible for notifying the kernel when it does not need the CPU anymore, and thus it can be swapped out. In this case, the kernel is only responsible for identifying the best candidate for scheduling and putting it on the CPU.
Task Scheduling on Android
Android runs on top of Linux, which is a preemptive kernel. Task scheduler is triggered at a hard-coded, constant interval, the scheduling interval. This is usually every 10 milliseconds on Android devices. When the scheduler runs, it decides whether the running task should be preempted or not, and which task from those waiting to run should take its place on the CPU. Since version 2.6.23, Linux kernel uses a highly sophisticated task scheduler, the Completely Fair Scheduler (CFS). CFS tries to give every task a fair amount of run time on the CPU based on several factors, including task weight and priority. This scheduler’s algorithm is quite complex to understand. That is why there is a relatively small number of kernel developers that contribute to its development.
Tweaking the CFS Scheduler
- Root Access
- A Terminal Emulator App
Despite its complexity, CFS scheduler offers some configuration options. Tweaking these options could highly affect system performance. Traditionally, the sysctl command line tool is used for examining and changing kernel parameters at runtime, including scheduler parameters. We are going to use this tool in our guide since it comes pre-installed on most Androids out there. But first, we need to do a small introduction to it:
How to Use Sysctl
- Listing Parameters and their current values
To list all available kernel parameters with their assigned values, give the following command inside a Terminal App:
To get only the task scheduler-related parameters, we could give the following command:
sysctl -a | grep "sched"
This gives the following output on our example device:
The parameters list is quite big. We will only discuss some of these parameters below, which are the most important ones.
- Changing a Parameter
We can change a parameter using a command in the form sysctl -w [parameter]=[value] where the parameter is the kernel parameter name and value is the new value. For example:
sysctl -w kernel.sched_scaling_enable=1
Read below for more sysctl usage examples. Keep in mind that changes do not survive a reboot. You can use an init.d script to apply any needed changes at boot time. Lastly, make sure that you run sysctl commands with root privileges. You can get root privileges in a Terminal App by giving the following command:
Basic Task Scheduler Parameters
Processor-bound tasks are guaranteed to run for this minimum time before they are preempted. A task is considered as processor-bound when the time it takes to complete depends only on the processor speed. For example, a task waiting for a package to arrive through the net is not a processor-bound task. Generally, increasing this value will increase a system’s throughput. On the other hand, values too high might reduce device responsiveness. This parameter takes values in nanoseconds. For example, to set it to 4000000 nanoseconds (4 milliseconds), we can give the following command:
sysctl -w kernel.sched_min_granularity_ns=4000000
As per official Linux kernel documentation, this is the central CFS scheduler tunable.
All of the tasks on the CFS’s run queue are guaranteed to be scheduled at least once within this period. However, as the number of tasks increases, the scheduler cannot assure that both this and sched_min_granularity_ns are satisfied. So, when the number of running tasks exceeds sched_latency_ns/sched_min_granularity_ns , this period equals to [ (number of runnable tasks) * sched_min_granularity_ns ]. Values are expressed in nanoseconds.
This controls whether the scheduler can automatically adjust sched_latency_ns, based on the number of online CPUs. Possible values are 0 (do not adjust) , 1 (logarithmic adjustment), and 2 (linear adjustment).
This parameter defines whether a freshly forked child runs before the parent continues execution
(for example a messaging App that starts a background message sync service). This might be beneficial in processes that the child needs to execute some code as soon as it runs. Whether changing this parameter will improve a device’s performance or not, depends on the usage scenario. To enable this feature, enter the following command in the Terminal App:
sysctl -w kernel.sched_child_runs_first=1
A sleeping task that wakes up after an event will run for at least the amount of time defined by this parameter. An example is a process waiting for data to arrive from a sensor. That process will usually sleep until the sensor sends an interrupt. Then, it will wake up and try to execute some code as soon as possible. This parameter must hold a value higher than half of sched_latency_ns. This ensures that waking tasks can compete with CPU hogs (tasks that make big use of the device’s processor).
Kernel Scheduler Configuration Examples
Following examples are provided as a reference for more experimentation. They follow some simple rules depending to the workload they are designed for, but they are not guaranteed to have the same results on all Android devices.
Games are demanding applications that make heavy utilization of the CPU. Giving them higher run time on the CPU might help reduce hangs and sound artifacts. Most games typically run several code execution threats in the background. Letting these threats run as soon as possible might be beneficial. Tunable scaling is set to off, to make sure kernel.scheduler_latency_ns remains constant.
Most multicore Android devices use a CPU hotplug daemon which turns CPUs on or off depending on the system load. The kernel scheduler does a great job adjusting its latency depending on the number of available CPUs. As mentioned above, this feature is provided through the kernel.sched_tunable_scaling parameter. Furthermore, high device responsiveness generally needs low scheduling latencies. We can help keep latencies low by reducing the tasks minimum run time.
- kernel.sched_tunable_scaling=1 or kernel.sched_tunable_scaling=2 (results differ between devices and workloads)
Low Battery Consumption
Task Scheduler can greatly affect battery consumption. A high rate of task swapping on the CPU will introduce some scheduler-related workload on the system. This might increase battery consumption. However, a very low rate of task swapping (very high minimum granularity and/or latency values) will severely decrease responsiveness. The magic recipe lies somewhere in the middle. To minimize scheduler-related workload, tunable scaling is disabled.
We have covered the most important configuration parameters of the Linux Task Scheduler. Stay tuned for the second part of this guide, in which we will discuss how to enable or disable some special Task Scheduler features. If you decide to tweak your scheduler’s parameters and get some good results, share your results with us in the comments section below!