10

I'm attempting to limit a process to a given number of CPU cores. According to the taskset man page and this documentation, the following should work:

[fedora@dfarrell-opendaylight-cbench-devel ~]$ taskset -pc 0 <PID>
pid 24395's current affinity list: 0-3
pid 24395's new affinity list: 0

To put it simply - this doesn't work. Putting the process under load and watching top, it sits around 350% CPU usage (same as without taskset). It should max out at 100%.

I can properly set affinity via taskset -c 0 <cmd to start process> at process spawn time. Using cpulimit -p <PID> -l 99 also kinda-works. In both cases, putting the process under the same load results in it maxing out at 100% CPU usage.

What's going wrong here?

  • Should I have posted this on StackExchange#Unix & Linux? If so, can someone move it or tell me how to move it? – dfarrell07 Jul 2 '14 at 19:06
  • Per this question, a mod needs to move it. I'm going to flag the question for mod attention. Mod - please move this question to the Unix and Linux stack exchange site. Sorry for the confusion! – dfarrell07 Jul 2 '14 at 19:20
  • 3
    As dfarrell07 pointed out, taskset does not affect all threads (LWPs) of a process by default. Use the "-a" option to taskset to affect all threads in the process. – user89643 Oct 29 '14 at 19:31
  • I checked with a Java program and yes, the -a option is needed (then it works reliably). Another note: It doesn't matter how many new Java threads are created in Java after you have run taskset, the CPU limit is still applied. Apparently the word "thread" means something else than a Java thread here. – Stefan Reich Jan 3 '19 at 16:01
6

Update: Newer versions of taskset have a -a/--all-tasks option that "operates on all the tasks (threads) for a given pid" and should solve the behavior I show below.

I wrote a Python script that simply spins up some threads and burns CPU cycles. The idea is to test taskset against it, as it's quite simple.

#!/usr/bin/env python

import threading

def cycle_burner():
    while True:
        meh = 84908230489 % 323422

for i in range(3):
    thread = threading.Thread(target=cycle_burner)
    print "Starting a thread"
    thread.start()

Just running the Python script eats up about 150% CPU usage.

[~/cbench]$ ./burn_cycles.py
Starting a thread
Starting a thread
Starting a thread

Launching my Python script with taskset works as expected. Watching top shows the Python process pegged at 100% usage.

[~/cbench]$ taskset -c 0 ./burn_cycles.py
Starting a thread
Starting a thread
Starting a thread

Interestingly, launching the Python script and then immediately using taskset to set the just-started process' affinity caps the process at 100%. Note from the output that the Linux scheduler finished executing the Bash commands before spawning the Python threads. So, the Python process was started, then it was set to run on CPU 0, then it spawned its threads, which inherited the proper affinity.

[~/cbench]$ ./burn_cycles.py &; taskset -pc 0 `pgrep python`
[1] 8561
pid 8561's current affinity list: 0-3
pid 8561's new affinity list: 0
Starting a thread
[~/cbench]$ Starting a thread
Starting a thread

That result contrasts with this method, which is exactly the same but allows the Python threads to spawn before setting the affinity of the Python process. This replicates the "taskset does nothing" results I described above.

[~/cbench]$ ./burn_cycles.py &
[1] 8996
[~/cbench]$ Starting a thread
Starting a thread
Starting a thread
[~/cbench]$ taskset -pc 0 `pgrep python`
pid 8996's current affinity list: 0-3
pid 8996's new affinity list: 0

What's going wrong here?

Apparently threads spawned before the parent process' affinity is changed don't inherit the affinity of their parent. If someone could edit in a link to documentation that explains this, that would be helpful.

| improve this answer | |
  • Please update your answer to include the parameter -a as suggested in the comments of the question. I tried and it worked for me. – Nagabhushan S N Mar 10 at 3:31
  • @NagabhushanSN Done, thanks! – dfarrell07 Jul 30 at 9:27
4

I think you'll need to call taskset once per thread, i.e. use ps -eL instead of pgrep and pipe that to taskset -cp 0

ps -eLo cmd,tid | grep python | perl -pe 's/.* (\d+)$/\1/' | xargs -n 1 taskset -cp 0

This calls task set for all thread ids.

| improve this answer | |
4

try numactl with --physcpubind (or -C) instead. The man page says:

... The policy is set for command and inherited by all of its children.

(in recent versions of taskset there is also an -a option which Sets or retrieves the CPU affinity of all the tasks (threads) for a given PID. but it's not clear whether this also works for daughter processes of a task launched with taskset as opposed to modifying an already running process)

| improve this answer | |
  • On openSUSE 42.2, -a does work with already running processes. – ederag Feb 2 '18 at 15:11
3

I use taskset's -a option successfully. I have a server named videoconverterd which consumes a lot of CPU; top shows

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 8991 root      20   0 1299472 346724  47380 S 385.7  4.3  42:41.37 videoconverterd

After running taskset -apc 0 8991, the CPU load drops to

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 8991 root      20   0 1221832 293344  47380 S  99.7  3.7  49:13.28 videoconverterd

I'm running CentOS 7 with taskset version 2.23.2.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.