Dunno if I reinvented the wheel (transmission?), but I just made a simple little program that monitors CPU load, temperature and power supply status to automatically change gears as-needed.
In case anyone else is interested, the source is here.
This uses the manual gearbox in that thread. In that one you activate the gear you want, depending on what you’re going to do. Say you’re going to run some game that you know needs lots of power. You activate one of the higher gears then run the game. When you’re done playing, you switch back to a lower gear to preserve battery.
With this, the switch happens automatically using the same gear system, so this complements the manual script, making it easier to use.
For some situations, manually changing gears would be really annoying: going high during compilation then low when editing code, going high for loading webpages, etc. It is also pretty convenient that it switches to the lowest gear when the power supply is disconnected.
I’d recommend replicating the manual gear switching code in C++.
For one, constantly shelling out to python alone is slow (the reason I embedded the gearbox right into my keyboard daemon), and may oscillate the auto transmission (idle, gear down → python → hot, gear up!)
For two, you make modern C++ look like a better scripting candidate than python already
Also, if we can push this into millisecond level, we will be able to achieve something like dynamic hot-plugging in qualcomm android devices. I remember that from snapdragon 801/810 devices but not seen on later models. Strange, but considering that rk3399 has similar guts to msm8994…
Yeah, there’s room for improvement and doing the gear switching in C++ makes sense.
While Python is relatively slow, right now gears aren’t changed often enough for it to really matter. And after a switch it sleeps for a while, precisely to avoid skewing the measurements. In a way, using the existing script serves as a (convoluted, inefficient) configuration file.
If we were to get more information (how many threads need CPU time) then we’d be able to pick an ideal amount of cores and make better choices than simply picking a gear. Then doing everything (including fan control, maybe?) in C++ will make more sense.
Here’s an enhanced/updated version that I’ve been trying out.
Direct CPU control, no python scripts
Individual core throttling instead of predefined gears
Does not attempt to control GPU clock speed
Logs core status into /tmp/gears for displaying in status bar
Faster sampling and gear changing
Basically, it’ll try to keep cores between 20-90% busy. If a core is at less than 20%, it will get its clock speed decreased. If it can’t be decreased any further, it’ll power down. The opposite happens for cores at above 90%, a new core is powered up if the clock speed can’t be increased further.
If the CPU is hot or on battery power, the one of the big cores gets limited, keeping temperature below 80C.
On mains power core 0 no longer gears down. Less lag when the system suddenly needs power.
I am posting this from the devterm, running i3+picom with transparency effects, youtube streaming video to a picture-in-picture overlay. No stuttering, ~75C.
Enabling/disabling core that way is mostly useless, as:
unless you run lots of heavy tasks in parallel, apps are for most mono threaded so run on a single core
If it is multithreaded it will most likely count how many core are available when it start and not change dynamically
It would probably be more beneficial to take account of the type of cores, and have only the LITTLE core enabled on idle, then if one of the LITTLE is maxed out, enable a BIG one to replace the LITTLE.
CPU core scheduling is a complicated task, and it should be done by the kernel, I’m amazed that there has not been any proper implementation done by the kernel dev for that.
As for changing the speed of cores, the linux kernel have all it need for that, just enable the right governor, which is either ondemand or schedutil unless you want to force a specific mode. Both will be way more fine grained than any type of user space app especially because they know much more about the OS state than you can (CPU load, number of process and thread per process, etc…)
Changing the max frequency should not be needed until you want to do thing like what windows does in the power setting “more battery”, “middle” or “most powerful” which is basically more battery force a governor like powersqve, middle is something like conservative/schedutil and most powerful is the somewhat of the performance governor.
How windows does things is probably a bit more complete than these governors, but that’s the idea.
You can run multiple processes in parallel, not just threads. When you call make -j 4 you’ll have 4 compilers running and sharing time with whatever cores are available.
Even if the number of threads in an application is constant, it is very common for threads to be put to sleep as they run out of work to do. The number of cores can change dynamically in response to the load.
Instead of replacing, it’d probably be better to have both. The process that maxed out the core would then be able to hog a core to itself and wouldn’t be swapped out for the other processes than run in the background.
Agreed, ideally this’d all be done by the kernel. While it does change frequency up to the maximum in response to demand, it doesn’t enable/disable cores. It is possible that trying to set maximum frequency in user space is redundant, but then why do we do that in the manual gearbox?