Ananicy rewritten in C++ for much lower CPU and memory usage.
Beta status
- Rule and configuration loading
- Process detection and scanning
- Renicing
- Changing CPU scheduler
- Changing IO class and IO nice
- Setting OOM score
- Change default configuration file and rules directory using environment variables (
ANANICY_CPP_{CONF,CONFDIR}) - Cgroups
- V1
- V2 (Limited support, see #21)
- Autogroup
- Systemd integration
- Unit file
- Hardening unit file
- Working sd_notify
- CLI compatibility with Ananicy
- Something else... ?
- CMake 3.17+
- fmtlib 8.0+ (can be automatically downloaded)
- spdlog 1.9+ (can be automatically downloaded)
- nlohmann_json 3.9+ (can be automatically downloaded)
- C++ compiler compatible with C++20 (
g++ >= 10orclang++ >= 10). Notably,<concepts>andstd::jthreadare required. Work is in progress to add support tog++ ~= 9, see https://gitlab.com/ananicy-cpp/ananicy-cpp/-/issues/28 for more info. - Optional:
Install the prerequisites: sudo apt install git cmake build-essential g++ libsystemd-dev
It should be a fully drop-in replacement from Ananicy.
If you detect a difference in behavior (except for bugs in Ananicy that are fixed in ananicy-cpp),
please create an issue.
If you want pre-made community rules, you can use the rules from the original Ananicy project.
Simply copy them to your rules directory (by default, /etc/ananicy.d).
CMAKE_BUILD_TYPE:STRING: EitherNone(default),Release, orDebug. The effects are mainly on the default compiler flagsananicy-cppis built with.USE_EXTERNAL_JSON:BOOL: Use system nlohmann_json library,USE_EXTERNAL_SPDLOG:BOOL: Use system spdlog library,USE_EXTERNAL_FMTLIB:BOOL: Use system fmtlib,ENABLE_SYSTEMD:BOOL: Add systemd support,USE_BPF_PROC_IMPL:BOOL: Use BPF for processing,BPF_BUILD_LIBBPF:BOOL: Build libbpf instead of using system libary,STATIC:BOOL: Build a static binary,
In order to actually build ananicy-cpp, open a terminal,
clone the repository (or download a release archive), and do the following commands:
- If using the
gitrepo (development version):git clone https://gitlab.com/ananicy-cpp/ananicy-cpp.gitcd ananicy-cpp
- If using release archive:
wget https://gitlab.com/ananicy-cpp/ananicy-cpp/-/archive/v1.1.1/ananicy-cpp-v1.1.1.tar.gztar -xvf ananicy-cpp-v1.1.1.tar.gzcd ananicy-cpp-v1.1.1
cmake -B build \
-DCMAKE_RELEASE_TYPE=Release \
-D[Your Option Here] -D[Another Option] \
-S .
cmake --build build --target ananicy-cpp
sudo cmake --install build --component Runtime
- If using install-deps.sh and configure.sh (example using BPF_PROC_IMPL)
git clone https://gitlab.com/ananicy-cpp/ananicy-cpp.gitcd ananicy-cppenv USE_BPF=ON ./install-deps.sh && ./configure.sh --use_bpf_proc && ./build.shsudo cmake --install build/RelWithDebInfo --component Runtime
To enable the systemd service,
enable Systemd support with -DENABLE_SYSTEMD=ON on cmake invocation, and run this command after installing ananicy-cpp:
systemctl enable --now ananicy-cpp.service
If you want to stop or disable it later, you can use:
systemctl (stop|disable) ananicy-cpp.service
You can have more information in systemd's man page (man systemd on Linux)
There is an AUR package named ananicy-cpp, which is maintained by me.
You can simply use your preferred AUR-helper to install it.
There is also binaries in cachyos and chaotic-aur, although ananicy-cpp is relatively fast to build.
Add rules in /etc/ananicy.d. This path can be overridden with ANANICY_CPP_CONFDIR
environment variable.
Rules are defined in files ending with .rules.
For instance, to add a rule for GCC, you could do the following:
- Create the
/etc/ananicy.d/10-compilersfolder. - Create the
/etc/ananicy.d/10-compilers/gcc.rulesfile - Add
{"name": "gcc", "nice": 19, "latency_nice": 19, "sched": "batch", "ioclass": "idle"}to the file.
You can then (re)start ananicy-cpp.service.
nice: [-20-19]: Set the nice value of the process. A process with a higher nice value will be more "polite", and will get less cpu time than processes with a lower nice value.latency_nice: [-20-19]: Set the latency_nice value of the process. A process with a lower latency_nice value indicates the task to have the least latency as compared to the task having a higher latency_nice. A additonal kernel patch is needed see latency_nicesched: {"fifo", "rr", "normal", "batch", "idle"}: Set the scheduling policy.fifoandrr(for round-robin) are realtime scheduling policies, and must only be used for latency critical programs, likeXorgorpulseaudiofor instance. Nice values are ignored,rtprioshould be used instead.deadline: Special realtime scheduling policy which can't be set byananicy-cpp, but can be reported by it.normalis well... normal, the default behavior for the current OS. Specifying this option can be useful if you want to force the child of a realtime process to have a normal scheduling policy.batch: Very useful for compilers or other CPU-hungry, non-interactive programs, like compilers for instance. It can actually improve their performance with almost no cost on the rest of the system.idle: Very, very low priority, even lower than a nice value of19. Useful for background, low priority stuff, like file indexer for instance.
rtprio: [0, 99]: Sets the static priority of a process. Only relevant if the actual scheduling policy of a process is a realtime one, i.e.fifo,rrordeadline. A higher value means a higher priority.ioclass: {"best-effort", "realtime", "idle", "none"}: Define the IO scheduling policy. By default, it isbest-effort. Only the CFQ I/O scheduler supportsioclassandionice, see ioprio_set.realtimeis to be used cautiously, as the process will have the absolute priority above allbest-effortprocesses, and can starve them. This could prevent you from starting a shell, for instance.ionice: [0, 7]. Lower value is higher priority.
idle: Process gets I/O resources after all other processes. This could starve the process.ioniceis completely ignored.
none: Reset I/O policy to system default,ionicemust be0.best-effort: Try to fairly share I/O resources between processes.ionice: [0, 7]. Lower value is higher priority.
oom_score_adj: [-999, 999]: Adjust the Out Of Memory killer score of a process. Negative value decrease the score of the process, making it less likely to be killed if available memory gets very low. It is recommended to use it on critical programs which must be killed last if you lack memory.cgroup: Put the process in the specified cgroup. This can be any cgroup, including those created outsideananicy-cpp.type: Set the type of the rule. All options defined in the type will be used as if written explicitly in the rule, although you can override each option if needed.
To avoid repeating yourself, you can add types.
It must be defined in a .types file.
The syntax is the following:
{"type": "my_type", "nice": 19, "other_parameter": "value"}It can then be used in any rule by simply adding the type property to the rule.
For instance, {"name": "gcc", "type": "compiler"}
Parameters can be overridden, for instance:
{"type": "compiler", "nice": 19, "sched": "batch", "ioclass": "idle"}{"name": "gcc", "type": "compiler", "ioclass": "none", "ionice": 0}They are defined in .cgroups files.
cgroup_v1 are recommended for proper functionality (see issue #21), they can be forced by
passing systemd.unified_cgroup_hierarchy=0 to your kernel cmdline (by editing /etc/default/grub for instance).
Only one attribute is supported, CPUQuota:
{"cgroup": "cpu80", "CPUQuota": 80}One major limitations of cgroups right now is that they can only be created by the root user.
You can not use a CAP_SYS_CGROUP capability thing like for other things, because it simply does not exist yet.
If it is ever implemented in the Linux kernel, please create an issue.
You can use the original Ananicy's rules by copying the 00-default, 00-types.types and 00-cgroups.cgroups to your ananicy.d directory (by default, in /etc).
I mostly used Ananicy on older computers to improve interactivity. However, having Ananicy use megabytes of RAM and a decent amount of CPU time troubled me. Thus I decided to rewrite it in C++, using an event based approach. RAM usage is much lower (only a few thousands of bytes !), and CPU usage is almost always zero thanks to its event-based implementation.