Linux 성능 최적화 (15) -CPU 바인딩

하나의 분리 된 CPU

1. 분리 된 CPU 소개

CPU 부하가 높은 CPU 집약적 작업의 경우 CPU 선호도를 설정하여 작업 실행 효율성을 높이고, CPU 컨텍스트 전환을 방지하고, CPU 캐시 적중률을 높이는 것이 좋습니다.
기본적으로 Linux 커널 스케줄러는 모든 CPU 코어를 사용할 수 있습니다. 특정 작업 (프로세스 / 스레드)이 CPU 코어를 독점해야하고 다른 작업 (프로세스 / 스레드)이 CPU 코어를 사용하는 것을 원하지 않는 경우 다른 작업을 허용하지 않고 지정된 CPU를 격리 할 수 ​​있습니다. 프로세스 사용.

2. 분리 된 CPU의 특성

격리 된 CPU는 격리 된 CPU에서 실행되는 작업의 실시간 성능을 효과적으로 향상시킬 수 있습니다. 격리 된 CPU에서 작업을 실행하는 동안 다른 작업을 실행할 수있는 CPU 리소스를 줄일 수 있으므로 컴퓨터 CPU 리소스를 계획해야합니다.

3. CPU 설정 분리

Linux 커널의 isolcpus 시작 매개 변수는 SMP 균형 스케줄링 알고리즘에서 하나 이상의 CPU를 분리하는 데 사용되며 지정된 프로세스는 분리 된 CPU에 배치되어 CPU 선호도 설정을 통해 실행됩니다.
isolcpus= cpu_number [, cpu_number ,...]
(1) GRUB 구성 파일을 수정합니다.
기본 GRUB 구성은 / etc / default / grub이고 isolcpus = 11,12,13,14,15가 GRUB_CMDLINE_LINUX 값에 추가됩니다. 모든 CPU 코어는 쉼표로 구분해야하며 영역 범위는 지원되지 않습니다.
GRUB_CMDLINE_LINUX="isolcpus=1,2 crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"
(2) grub을 업데이트
하고 grub 부팅 파일 /boot/grub/grub.cfg를 다시 생성 한 다음 시스템을 다시 시작하여 적용합니다.

update-grub
update-grub2
grub-mkconfig -o /boot/grub/grub.cfg

isolcpus 매개 변수를 사용하여 Linux 커널이 시작되면 Linux 커널 작업 균형 스케줄러는 더 이상 지정된 CPU 코어로 프로세스를 예약하지 않습니다. 사용자는 일반적으로 taskset 또는 cset 명령을 사용하여 프로세스를 CPU 코어에 바인딩해야합니다.

2. CPU 바인딩 소개

1. CPU 코어 소개

하이퍼-스레딩 기술 (하이퍼-스레딩)은 특수 하드웨어 명령을 사용하여 두 개의 논리 코어 (CPU 코어)를 두 개의 물리적 칩으로 시뮬레이션하므로 단일 프로세서가 스레드 수준 병렬 컴퓨팅을 사용할 수 있으며 다중 스레드 운영 체제와 호환됩니다. 이 소프트웨어는 CPU의 유휴 시간을 줄이고 CPU의 작동 효율성을 향상시킵니다.
물리적 CPU는 컴퓨터 마더 보드에 설치된 CPU입니다.
논리적 CPU는 물리적 CPU의 물리적 CPU 코어입니다. 일반적으로 물리적 CPU에는 여러 개의 물리적 코어가 있습니다. 즉, 논리적 CPU가 여러 개 있습니다. Intel HT (Hyper-Threading Technology)가 지원되는 경우 논리 CPU는 CPU 코어 수의 두 배로 나눌 수 있습니다.
cat /proc/cpuinfo|grep "physical id"|sort -u|wc -l
물리적 CPU 수
cat /proc/cpuinfo|grep "cpu cores"|uniq
보기 각 물리적 CPU의 코어 수 (즉, 코어 수)
cat /proc/cpuinfo|grep "processor"|wc -l
보기 논리적 CPU 수
cat /proc/cpuinfo|grep "name"|cut -f2 -d:|uniq
보기 CPU 이름 및 모델 보기
ps -eo pid,args,psr
프로세스가 실행중인 논리적 CPU보기

2. CPU 바인딩 소개

CPU 바인딩은 프로세스 또는 스레드에 대한 해당 CPU 선호도를 설정하여 프로세스 또는 스레드가 해당 플래그 비트 세트가있는 CPU에서만 실행되도록하여 애플리케이션의 CPU 사용 효율성을 향상시킵니다. 응용 프로그램이 여러 CPU에서 실행될 수있는 경우 운영 체제는 CPU간에 응용 프로그램을 자주 전환하여 CPU 캐시가 무효화되고 캐시 적중률이 줄어들고 CPU 사용 효율성이 감소합니다. CPU 바인딩 기술을 사용하면 CPU 캐시 오류를 어느 정도 방지하고 시스템 성능을 향상시킬 수 있습니다.
CPU 선호도는 프로세스를 하나 또는 CPU 그룹에 바인딩 할 수있는 예약 속성 (스케줄러 속성)입니다.
SMP (Symmetric Multi-Processing) 아키텍처에서 Linux 스케줄러 (스케줄러)는 지정된 프로세스가 다른 CPU에서 실행되는 대신 CPU 선호도 설정에 따라 바인딩 된 CPU에서 실행되도록합니다.,
Linux 스케줄러 또한 자연스러운 CPU 선호도를 지원합니다. 스케줄러는 동일한 CPU에서 프로세스를 계속 실행하려고합니다. 즉, 프로세스가 일반적으로 프로세서간에 자주 마이그레이션되지 않으며 프로세스 마이그레이션 빈도는 다음을 의미합니다. 부하가 적습니다.
프로그램의 작성자가 스케줄러보다 프로그램을 더 잘 알고 있기 때문에 CPU0을 너무 많이 차지하지 않고 CPU 코어를 수동으로 할당하거나 주요 프로세스와 다른 프로세스를 합쳐서 모두 CPU를 설정할 수 있습니다. 선호도는 특정 프로그램의 성능을 향상시킬 수 있습니다.
Linux 커널 프로세스 스케줄러는 본질적으로 소프트 CPU 선호도 (선호도)이며 프로세스는 일반적으로 프로세서간에 자주 마이그레이션되지 않습니다.
모든 프로세스의 CPU 할당
ps -eo pid,cmd,psr
을 봅니다. 프로세스의 모든 스레드에 대한 CPU 할당 을 봅니다.
ps -To 'pid,lwp,psr,cmd' -p [PID]

3. CPU 바인딩의 특징

프로세스 / 스레드를 CPU에 바인딩하면 CPU 캐시 적중률이 크게 증가하여 메모리 액세스 손실을 줄이고 애플리케이션 성능을 향상시킬 수 있습니다. NUMA 아키텍처에서는이 작업이 시스템 운영 속도 향상에 더 큰 의미가있는 반면 SMP 아키텍처에서는 이러한 개선이 상대적으로 작을 수 있다고 생각합니다. 이는 주로 캐시와 버스 리소스의 할당 및 사용 방법이 둘 사이에 다르기 때문입니다. NUMA 아키텍처에서는 각 CPU에 자체 리소스 시스템 세트가 있으며 SMP 아키텍처에서는 각 코어가 여전히 이러한 리소스를 공유해야합니다.
각 CPU 코어가 프로세스를 실행할 때 각 프로세스의 리소스가 독립적이므로 CPU 코어간에 전환 할 때 컨텍스트를 고려할 필요가 없습니다. 각 CPU 코어가 스레드를 실행할 때 스레드가 리소스를 공유해야하는 경우도 있습니다. 공유 리소스는 CPU의 한 코어에서 다른 코어로 복사되어야하므로 추가 오버 헤드가 발생합니다.

4. 작업 집합 바인딩 프로세스

yum install util-linux
작업 세트 도구를 설치하여
taskset [options] [mask] -p pid
프로세스의 CPU 선호도 보고, -p 옵션을 사용하여 PID를 지정하고, -cp 옵션을 지정하여 CPU 코어 목록을 인쇄하는 경우 기본적으로 16 진수를 인쇄합니다. 3의 이진 형식은 0011이며 -cp는 0과 1을 인쇄하여 프로세스가 CPU의 0 번째 및 1 번째 코어에서만 실행될 수 있음을 나타냅니다.
taskset -c -p pid
지정된 프로세스의 CPU 선호도보기

taskset -p mask pid
taskset -c [CPU NUMBER] -p PID

지정된 프로세스의 CPU 선호도를 설정합니다. 격리 된 CPU의 경우 첫 번째 CPU 만 유효합니다.
CPU 번호 11, 12, 13, 14, 15를 사용하여 프로세스를 실행하십시오.

taskset -c 11,12,13,14,15 python xx.py
taskset -c 11-15 python xx.py

Docker 컨테이너에서 격리 된 CPU를 계속 사용할 수 있습니다 .Docker 컨테이너를 만들 때 --cpuset-cpus 매개 변수를 통해 컨테이너에서만 사용할 수있는 CPU를 지정하여 Docker 컨테이너에서 격리 된 CPU를 얻을 수 있습니다.

5. Cset 바인딩 프로세스

cset set --cpu CPU CPUSET NAME
CPU 코어 세트를 정의합니다. 독립 CPU의 경우 첫 번째 CPU 코어 만 유효합니다.
cset proc --move --pid=PID,...,PID --toset=CPUSET NAME
여러 프로세스를 지정된 CPU 세트로 이동

셋, 프로세스 바인딩 CPU

1. 시스템 호출 API

#define _GNU_SOURCE        
#include <sched.h>
int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);

매개 변수 :
pid : 프로세스 번호, pid 값이 0이면 현재 프로세스가 지정되었음을 의미합니다.
cpusetsize : 일반적으로 sizeof (cpu_set_t)로 설정되는 마스크 매개 변수로 지정된 숫자의 길이.
마스크 : CPU 마스크

2. 프로그래밍 실현

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/sysinfo.h>
#include<unistd.h>

#define __USE_GNU
#include<sched.h>
#include<ctype.h>
#include<string.h>
#include<pthread.h>

#define THREAD_MAX_NUM 10  //1个CPU内的最多进程数
int CPU_NUM = 0;  //cpu中核数
int CPU = 3; // CPU编号

void* threadFun(void* arg)
{
    cpu_set_t mask;  //CPU核的集合

    CPU_ZERO(&mask);
    // set CPU MASK
    CPU_SET(CPU, &mask);
    //设置当前进程的CPU Affinity
    if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
    {
        printf("warning: could not set CPU affinity, continuing...\n");
    }
    cpu_set_t affinity;   //获取在集合中的CPU
    CPU_ZERO(&affinity);
    // 获取当前进程的CPU Affinity
    if (sched_getaffinity(0, sizeof(affinity), &affinity) == -1)
    {
        printf("warning: cound not get Process affinity, continuing...\n");
    }
    int i = 0;
    for (i = 0; i < CPU_NUM; i++)
    {
        if (CPU_ISSET(i, &affinity))//判断线程与哪个CPU有亲和力
        {
            printf("this thread %d is running processor : %d\n", *((int*)arg), i);
        }
    }

    return NULL;
}

int main(int argc, char* argv[])
{
    int tid[THREAD_MAX_NUM];
    pthread_t thread[THREAD_MAX_NUM];
    // 获取核数
    CPU_NUM = sysconf(_SC_NPROCESSORS_CONF);
    printf("System has %i processor(s). \n", CPU_NUM);
    int i = 0;
    for(i=0;i<THREAD_MAX_NUM;i++)
    {
        tid[i] = i;
        pthread_create(&thread[i],NULL,threadFun, &tid[i]);
    }
    for(i=0; i< THREAD_MAX_NUM; i++)
    {
        pthread_join(thread[i],NULL);
    }
    return 0;
}

컴파일 :
gcc -o test test.c -pthread
실행 결과 :

System has 4 processor(s). 
this thread 1 is running processor : 3
this thread 0 is running processor : 3
this thread 4 is running processor : 3
this thread 9 is running processor : 3
this thread 7 is running processor : 3
this thread 5 is running processor : 3
this thread 6 is running processor : 3
this thread 8 is running processor : 3
this thread 3 is running processor : 3
this thread 2 is running processor : 3

3. 작업 세트는 프로세스를 CPU에 바인딩합니다.

(1) 프로세스를 지정된 CPU에 바인딩

taskset -pc CPU_NUMBER  PID
taskset -p PID

프로세스의 CPU 선호도 확인
(2) 프로세스가 시작되면 CPU에
taskset -c CPU_NUMBER PROGRAM&
바인딩하여 백그라운드에서 실행되는 PROGRAM 프로그램을 시작하고 프로세스를 CPU_NUMBER 코어에 바인딩하고 프로세스
taskset -p PID
의 CPU 선호도를 확인합니다.

네, 스레드 바인딩 CPU

1. 시스템 호출 API

#define _GNU_SOURCE            
#include <pthread.h>
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset)

매개 변수 :
pthead : 스레드 객체
cpusetsize : 일반적으로 sizeof (cpu_set_t)로 설정되는 마스크 매개 변수로 지정된 숫자의 길이.
마스크 : CPU 마스크

2. 프로그래밍 실현

#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/sysinfo.h>
#include<unistd.h>

#define __USE_GNU
#include<sched.h>
#include<ctype.h>
#include<string.h>
#include<pthread.h>

#define THREAD_MAX_NUM 10  //1个CPU内的最多进程数
int CPU_NUM = 0;  //cpu中核数
int CPU = 3; // CPU编号

void* threadFun(void* arg)
{
    cpu_set_t affinity;   //获取在集合中的CPU
    CPU_ZERO(&affinity);
    pthread_t thread = pthread_self();
    // 获取当前进程的CPU Affinity
    if (pthread_getaffinity_np(thread, sizeof(affinity), &affinity) == -1)
    {
        printf("warning: cound not get Process affinity, continuing...\n");
    }
    int i = 0;
    for (i = 0; i < CPU_NUM; i++)
    {
        if (CPU_ISSET(i, &affinity))//判断线程与哪个CPU有亲和力
        {
            printf("this thread %d is running processor : %d\n", *((int*)arg), i);
        }
    }

    return NULL;
}

int main(int argc, char* argv[])
{
    int tid[THREAD_MAX_NUM];
    pthread_t thread[THREAD_MAX_NUM];
    // 获取核数
    CPU_NUM = sysconf(_SC_NPROCESSORS_CONF);
    printf("System has %i processor(s). \n", CPU_NUM);
    cpu_set_t mask;  //CPU核的集合

    CPU_ZERO(&mask);
    // set CPU MASK
    CPU_SET(CPU, &mask);

    int i = 0;
    for(i=0;i<THREAD_MAX_NUM;i++)
    {
        tid[i] = i;
        pthread_create(&thread[i],NULL,threadFun, &tid[i]);
        //设置当前进程的CPU Affinity
        if (pthread_setaffinity_np(thread[i], sizeof(mask), &mask) != 0)
        {
            printf("warning: could not set CPU affinity, continuing...\n");
        }
    }
    for(i=0; i< THREAD_MAX_NUM; i++)
    {
        pthread_join(thread[i],NULL);
    }
    return 0;
}

컴파일 :
gcc -o test test.c -pthread
실행 결과 :

System has 4 processor(s). 
this thread 0 is running processor : 3
this thread 1 is running processor : 3
this thread 2 is running processor : 3
this thread 3 is running processor : 3
this thread 5 is running processor : 3
this thread 4 is running processor : 3
this thread 6 is running processor : 3
this thread 9 is running processor : 3
this thread 7 is running processor : 3
this thread 8 is running processor : 3

추천

출처blog.51cto.com/9291927/2594336