cpu-affinity
Upcoming SlideShare
Loading in...5
×
 

cpu-affinity

on

  • 1,676 views

CPU affinity doc

CPU affinity doc

Statistics

Views

Total Views
1,676
Views on SlideShare
1,676
Embed Views
0

Actions

Likes
1
Downloads
17
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

cpu-affinity cpu-affinity Document Transcript

  • CPU AffinityBind specific processes to specific processors with a new system call.The ability in Linux to bind one or more processes to one or more processors, called CPUaffinity, is a long-requested feature. The idea is to say ³always run this process on processor one´or ³run these processes on all processors but processor zero´. The scheduler then obeys theorder, and the process runs only on the allowed processors.Other operating systems, such as Windows NT, have long provided a system call to set the CPUaffinity for a process. Consequently, demand for such a system call in Linux has been high.Finally, the 2.5 kernel introduced a set of system calls for setting and retrieving the CPU affinityof a process.In this article, I look at the reasons for introducing a CPU affinity interface to Linux. I then coverhow to use the interface in your programs. If you are not a programmer or if you have an existingprogram you are unable to modify, I cover a simple utility for changing the affinity of a givenprocess using its PID. Finally, we look at the actual implementation of the system call.Soft vs. Hard CPU AffinityThere are two types of CPU affinity. The first, soft affinity, also called natural affinity, is thetendency of a scheduler to try to keep processes on the same CPU as long as possible. It ismerely an attempt; if it is ever infeasible, the processes certainly will migrate to anotherprocessor. The new O(1) scheduler in 2.5 exhibits excellent natural affinity. On the opposite end,however, is the 2.4 scheduler, which has poor CPU affinity. This behavior results in the ping-pong effect. The scheduler bounces processes between multiple processors each time they arescheduled and rescheduled. Table 1 is an example of poor natural affinity; Table 2 shows whatgood natural affinity looks like.Table 1. The Ping-Pong Effect time 1 time 2 time 3 time 4Process A CPU 0 CPU 1 CPU 0 CPU 1Table 2. Good Affinity time 1 time 2 time 3 time 4Process A CPU 0 CPU 0 CPU 0 CPU 0Hard affinity, on the other hand, is what a CPU affinity system call provides. It is a requirement,and processes must adhere to a specified hard affinity. If a processor is bound to CPU zero, forexample, then it can run only on CPU zero.
  • Why One Needs CPU AffinityBefore we cover the new system calls, lets discuss why anyone would need such a feature. Thefirst benefit of CPU affinity is optimizing cache performance. I said the O(1) scheduler tries hardto keep tasks on the same processor, and it does. But in some performance-critical situations²perhaps a large database or a highly threaded Java server²it makes sense to enforce the affinityas a hard requirement. Multiprocessing computers go through a lot of trouble to keep theprocessor caches valid. Data can be kept in only one processors cache at a time. Otherwise, theprocessors cache may grow out of sync, leading to the question, who has the data that is themost up-to-date copy of the main memory? Consequently, whenever a processor adds a line ofdata to its local cache, all the other processors in the system also caching it must invalidate thatdata. This invalidation is costly and unpleasant. But the real problem comes into play whenprocesses bounce between processors: they constantly cause cache invalidations, and the datathey want is never in the cache when they need it. Thus, cache miss rates grow very large. CPUaffinity protects against this and improves cache performance.A second benefit of CPU affinity is a corollary to the first. If multiple threads are accessing thesame data, it might make sense to bind them all to the same processor. Doing so guarantees thatthe threads do not contend over data and cause cache misses. This does diminish the performancegained from multithreading on SMP. If the threads are inherently serialized, however, theimproved cache hit rate may be worth it.The third and final benefit is found in real-time or otherwise time-sensitive applications. In thisapproach, all the system processes are bound to a subset of the processors on the system. Thespecialized application then is bound to the remaining processors. Commonly, in a dual-processor system, the specialized application is bound to one processor, and all other processesare bound to the other. This ensures that the specialized application receives the full attention ofthe processor.Affinity MasksOn most systems, Linux included, the interface for setting CPU affinity uses a bitmask. Abitmask is a series of n bits, where each bit individually corresponds to the status of some otherobject. For example, CPU affinity (on 32-bit machines) is represented by a 32-bit bitmask. Eachbit represents whether the given task is bound to the corresponding processor. Count the bitsfrom right to left, bit 0 to bit 31 and, thus, processor zero to processor 31. For example:11111111111111111111111111111111 = 4,294,967,295is the default CPU affinity mask for all processes. Because all bits are set, the process can run onany processor. Conversely:00000000000000000000000000000001 = 1is much more restrictive. Only bit 0 is set, so the process may run only on processor zero. Thatis, this affinity mask binds a process to processor zero.
  • Get it? What do the next two masks equal in decimal? What is the result of using them as theaffinity mask of a process?1000000000000000000000000000000000000000000000000000000000000011The first is equal to 2,147,483,648 and, because bit 31 is set, binds the process to processornumber 31. The second is equal to 3, and it binds the process in question to processor zero andprocessor one.The Linux CPU affinity interface uses a bitmask like that shown above. Unfortunately, C doesnot support binary constants, so you always have to use the decimal or hexadecimal equivalent.You may get a compiler warning for very large decimal constants that set bit 31, but they willwork.Using the New System CallsWith the correct kernel and glibc in hand, using the system calls is easy:#define _GNU_SOURCE#include <sched.h>longsched_setaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr);longsched_getaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr);The first system call is used to set the affinity of a process, and the second system call retrievesit.In either system call, the PID argument is the PID of the process whose mask you wish to set orretrieve. If the PID is set to zero, the PID of the current task is used.The second argument is the length in bytes of the CPU affinity bitmask, currently four bytes (32bits). This number is included in case the kernel ever changes the size of the CPU affinity maskand allows the system calls to be forward-compatible with any changes; breaking syscalls is badform, after all. The third argument is a pointer to the bitmask itself.Let us look at retrieving the CPU affinity of a task:unsigned long mask;unsigned int len = sizeof(mask);if (sched_getaffinity(0, len, &mask) < 0) { perror("sched_getaffinity"); return -1; }printf("my affinity mask is: %08lxn", mask);
  • As a convenience, the returned mask is binary ANDed against the mask of all processors in thesystem. Thus, processors in your system that are not on-line have corresponding bits that are notset. For example, a uniprocessor system always returns 1 for the above call (bit 0 is set and noothers).Setting the mask is equally easy:unsigned long mask = 7; /* processors 0, 1, and 2 */unsigned int len = sizeof(mask);if (sched_setaffinity(0, len, &mask) < 0) { perror("sched_setaffinity");}This example binds the current process to the first three processors in the system.You then can call sched_getaffinity() to ensure the change took effect. What doessched_getaffinity() return for the above setup if you have only two processors? What if you haveonly one? The system call fails unless at least one processor in the bitmask exists. Using a maskof zero always fails. Likewise, binding to processor seven if you do not have a processor sevenwill fail.It is possible to retrieve the CPU affinity mask of any process on the system. You can set theaffinity of only the processes you own, however. Of course, root can set any process affinity.I Want a Tool!If you are not a programmer, or if you cannot modify the source for whatever reason, you stillcan bind processes. Listing 1 is the source code for a simple command-line utility to set the CPUaffinity mask of any process, given its PID. As we discussed above, you must own the process orbe root to do this.Listing 1. bind (The whole program is given in the end of the article)Usage is simple; once you learn the decimal equivalent of the CPU mask, you need:usage: bind pid cpu_maskAs an example, assume we have a dual computer and want to bind our Quake process (with PID1600) to processor two. We would enter the following:bind 1600 2Getting Really CraftyIn the previous example, we bound Quake to one of the two processors in our system. To ensuretop-notch frame rates, we need to bind all the other processes on the system to the otherprocessor. You can do this by hand or by writing a crafty script, but neither is efficient. Instead,
  • make use of the fact that CPU affinity is inherited across a fork(). All of a process childrenreceive the same CPU affinity mask as their parent.Then, all we need to do is have init bind itself to one processor. All other processes, by nature ofinit being the root of the process tree and thus the superparent of all processes, are then likewisebound to the one processor.The cleanest way to do this type of bind is to hack this feature into init itself and pass in thedesired CPU affinity mask using the kernel command line. We can accomplish our goal with asimpler solution, though, without having to modify and recompile init. Instead, we can edit thesystem startup script. On most systems this is /etc/rc.d/rc.sysinit or /etc/rc.sysinit, the first scriptrun by init. Place the sample bind program in /bin, and add these lines to the start of rc.sysinit:/bin/bind 1 1/bin/bind $$ 1These lines bind init (whose PID is one) and the current process to processor zero. All futureprocesses will fork from one of these two processes and thus inherit the CPU affinity mask. Youthen can bind your process (whether it be a real-time nuclear control system or Quake) toprocessor one. All processes will run on processor zero except our special process (and anychildren), which will run on processor one. This ensures that the entire processor is available forour special process.Kernel Implementation of CPU AffinityLong before Linus merged the CPU affinity system calls, the kernel supported and respected aCPU affinity mask. There was no interface by which user space could set the mask.Each process mask is stored in its task_struct as an unsigned long, cpus_allowed. Thetask_struct structure is called the process descriptor. It stores all the information about a process.The CPU affinity interface merely reads and writes cpus_allowed.Whenever the kernel attempts to migrate a process from one processor to another, it first checksto see if the destination processors bit is set in cpus_allowed. If the bit is not set, the kernel doesnot migrate the process. Further, whenever the CPU affinity mask is changed, if the process is nolonger on an allowed processor it is migrated to one that is allowed. This ensures the processbegins on a legal processor and can migrate only to a legal processor. Of course, if it is bound toonly a single processor, it does not migrate anywhere.ConclusionThe CPU affinity interface introduced in 2.5 and back-ported elsewhere provides a simple yetpowerful mechanism for controlling which processes are scheduled onto which processors. Userswith more than one processor may find the system calls useful in squeezing another drop ofperformance out of their systems or for ensuring that processor time is available for even themost demanding real-time task. Of course, users with only one processor need not feel left out.They also can use the system calls, but they arent going to be too useful.
  • Listing 1. bind/* bind - simple command-line tool to set CPU * affinity of a given task */#define _GNU_SOURCE#include <stdlib.h>#include <stdio.h>#include <sched.h>int main(int argc, char *argv[]){ unsigned long new_mask; unsigned long cur_mask; unsigned int len = sizeof(new_mask); pid_t pid; if (argc != 3) { fprintf(stderr, "usage: %s [pid] [cpu_mask]n", argv[0]); return -1; } pid = atol(argv[1]); sscanf(argv[2], "%08lx", &new_mask); if (sched_getaffinity(pid, len, &cur_mask) < 0) { perror("sched_getaffinity"); return -1; } printf("pid %ds old affinity: %08lxn", pid, cur_mask); if (sched_setaffinity(pid, len, &new_mask)) { perror("sched_setaffinity"); return -1; }
  • if (sched_getaffinity(pid, len, &cur_mask) < 0) { perror("sched_getaffinity"); return -1; } printf(" pid %ds new affinity: %08lxn", pid, cur_mask); return 0;}