I need to externally limit a process/session to a certain number of cores.
Are there any other possibilities than CPU affinity (I don't like the need to specify the actual cores) and cgroups (hard to integrate into our project)?
Well, for the CPU affinity bit, that's usually intended to solve a different set of problems that pertain to the physical CPU's that are executing the program. That's why you have to specify which particular CPU's you're talking about with CPU affinity.
From the fact that you don't care which CPU's get used, I'm guessing you're just trying to get time slices re-shaped. cgroups actually accomplish what you're wanting there and are the mechanism for adjusting CPU time irrespective of executing CPU(s).
The determining difference basically boils down to this: either you care how which physical cores your process runs on or you don't. If you don't have any performance requirements that require the processes run on particular CPU's (certain CPU caches remain warm, ensure related processes don't execute on foreign NUMA nodes, that particular interrupts get handled by a particular set of CPU's, etc) then all you're probably just trying to control CPU time allocation. In that case it's a mistake to think in terms of "CPU cores" when what you really mean is "CPU Time."
What parts of cgroups are you having a hard time integrating? I can update my answer once I know what specific problem you're running into.
We went with cgroups in the end, since there really doesn't seem to be any other approach that would accomplish this.
Cgroups allow CPU utilization limiting through the kernel scheduler, using
cpu.cfs_quota_us. This avoids the explicit specification of CPU cores.
It might be too much overhead for you, but if I need to test something on less cores than my machine has available, I setup a VirtualBox VM and specify the number of CPUs in the configuration.