Linuxパフォーマンスの最適化(15)-CPUバインディング

1つの分離されたCPU

1.分離CPUの概要

CPU負荷が高く、CPUを集中的に使用するタスクの場合は、CPUアフィニティを設定して、タスクの実行効率を向上させ、CPUコンテキストの切り替えを回避し、CPUキャッシュのヒット率を上げることをお勧めします。
デフォルトでは、Linuxカーネルスケジューラは任意のCPUコアを使用できます。特定のタスク(プロセス/スレッド)がCPUコアを独占する必要があり、他のタスク(プロセス/スレッド)にそれを使用させたくない場合は、他のタスクに許可せずに、指定したCPUを分離できます。プロセスの使用法。

2.絶縁型CPUの特性

分離CPUは、分離CPUで実行されるタスクのリアルタイムパフォーマンスを効果的に向上させることができます。分離CPUでのタスクの実行を保証しながら、他のタスクが実行できるCPUリソースを削減します。したがって、コンピューターのCPUリソースを計画する必要があります。

3.CPU設定を分離します

Linuxカーネルのisolcpus起動パラメーターは、SMPバランススケジューリングアルゴリズムで1つ以上の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

Linuxカーネルがisolcpusパラメーターで開始されると、Linuxカーネルタスクバランススケジューラーは、指定されたCPUコアにプロセスをスケジュールしなくなります。ユーザーは通常、tasksetまたはcsetコマンドを使用してプロセスをCPUコアにバインドする必要があります。

2.CPUバインディングの概要

1.CPUコアの概要

ハイパースレッディングテクノロジー(ハイパースレッディング)は、特別なハードウェア命令を使用して2つの論理コア(CPUコア)を2つの物理チップにシミュレートします。これにより、1つのプロセッサでスレッドレベルの並列コンピューティングを使用でき、マルチスレッドオペレーティングシステムと互換性があります。このソフトウェアは、CPUのアイドル時間を短縮し、CPUの動作効率を向上させます。
物理CPUは、コンピューターのマザーボードにインストールされているCPUです。
論理CPUは、物理CPU上の物理CPUコアです。通常、物理CPUには複数の物理コアがあります。つまり、複数の論理CPUがあります。Intel Hyper-Threading Technology(HT)がサポートされている場合、論理CPUはCPUコアの2倍の数に分割できます。
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アフィニティは、プロセスを1つまたはグループのCPUにバインドできるスケジューリングプロパティ(schedulerプロパティ)です。
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アーキテクチャでは、この動作は比較的小さい可能性があります。これは主に、2つの間でキャッシュリソースとバスリソースの割り当てと使用方法が異なるためです。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 prints 0および1に対応し、プロセスがCPUの0番目と1番目のコアでのみ実行できることを示します。
taskset -c -p pid
指定されたプロセスのCPUアフィニティを表示します

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

指定されたプロセスのCPUアフィニティを設定します。分離されたCPUの場合、最初のCPUのみが有効です。
CPU No. 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セットに移動します

3、プロセスバインディング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アフィニティを確認します。

4、スレッドバインディング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
おすすめ