CPU 친화적 관리

8276 단어 cpu
쉽게 말하면 CPU 친화성(affinity)은 프로세스가 주어진 CPU에서 다른 프로세서로 옮겨지지 않고 최대한 오래 실행되어야 하는 경향이다.리눅스 커널 프로세스 스케줄러는 천성적으로 소프트 CPU 친화성(affinity)이라고 불리는 특성을 가지고 있는데 이것은 프로세스가 보통 프로세서 사이에서 빈번하게 이동하지 않는다는 것을 의미한다.이러한 상태는 바로 우리가 바라는 것이다. 왜냐하면 프로세스가 이동하는 빈도가 적으면 발생하는 부하가 적다는 것을 의미하기 때문이다.
2.6 버전의 리눅스 핵에는 개발자가 하드 CPU 친화성(affinity)을 프로그래밍할 수 있는 메커니즘도 포함되어 있다.이것은 프로그램이 프로세스가 어느 프로세서에서 실행되는지 명확하게 지정할 수 있다는 것을 의미한다.
Linux 내부 하드 친화성(affinity)이란 무엇입니까?
Linux 내부 핵에서 모든 프로세스는 관련 데이터 구조를 가지고 있다task_struct.이 구조는 매우 중요하고 원인은 매우 많다.이 중 친화성(affinity)과 연관성이 가장 높은 것은 cpus_allowed비트 마스크였다.이 비트 마스크는 n비트로 구성되어 있으며 시스템의 n개의 논리 프로세서와 일일이 대응합니다.4개의 물리적 CPU가 있는 시스템은 4개가 될 수 있습니다.만약 이 CPU들이 모두 하이퍼스레딩을 사용한다면, 이 시스템에는 8비트 마스크가 있다.
주어진 프로세스에 주어진 위치를 설정하면, 이 프로세스는 관련 CPU에서 실행될 수 있습니다.따라서 프로세스가 모든 CPU에서 실행되고 필요에 따라 프로세서 사이를 이동할 수 있다면 비트 마스크는 모두 1입니다.실제로 이것이 Linux 프로세스의 기본 상태입니다.
Linux 커널 API는 비트 마스크를 수정하거나 현재 비트 마스크를 볼 수 있는 몇 가지 방법을 제공합니다.
  • sched_set_affinity()(비트 마스크 수정용)
  • sched_get_affinity()(현재 비트 마스크 보기용)
  • 주의, cpu_affinity는 서브라인에 전달되기 때문에 적절하게 호출해야 한다sched_set_affinity.
    맨 위로
    왜 경친화성(affinity)을 사용해야 합니까?
    보통 리눅스 핵은 프로세스를 잘 스케줄링해서 실행해야 할 곳에서 프로세스를 실행할 수 있다. (이것은 사용 가능한 프로세서에서 실행되고 전체적인 성능을 얻는 것이다.)내부 핵에는 CPU 간 작업 부하 이동을 감지하는 알고리즘이 포함되어 있으며, 프로세스 이동을 사용해서 바쁜 프로세서의 압력을 낮출 수 있다.
    일반적인 경우, 응용 프로그램에서는 부족한 스케줄러 행동만 사용할 수 있다.그러나 성능 최적화를 위해 이러한 부족한 행동을 수정하기를 원할 수도 있습니다.경친화성(affinity)을 사용하는 세 가지 이유를 살펴보자.
    원인 1.대량의 계산을 해야 한다
    대량의 계산을 바탕으로 하는 상황은 일반적으로 과학과 이론 계산에 나타나지만 통용 분야의 계산에도 이런 상황이 나타날 수 있다.흔히 볼 수 있는 표지는 응용 프로그램이 멀티프로세서의 기계에 많은 계산 시간을 들여야 한다는 것을 발견하는 것이다.
    원인복잡한 어플리케이션 테스트
    복잡한 소프트웨어를 테스트하는 것은 우리가 내부 핵의 친화성(affinity) 기술에 흥미를 느끼는 또 다른 원인이다.선형 신축성 테스트가 필요한 프로그램을 고려합니다.몇몇 제품 성명은 더 많은 하드웨어를 사용할 때 더욱 잘 실행할 수 있다.
    각 프로세서 구성에 대해 한 대의 시스템을 구입하는 대신 다음과 같은 작업을 수행할 수 있습니다.
  • 멀티프로세서 구입 시스템
  • 할당 프로세서 증가
  • 초당 사무수 측정
  • 평가 결과의 신축성
  • 만약 응용 프로그램이 CPU가 증가함에 따라 선형적으로 신축할 수 있다면, 초당 사무수와 CPU 개수 사이에는 선형적인 관계가 있을 것이다. (예를 들어 사선도 - 다음 절의 내용을 참조하십시오.)이렇게 모델링을 하면 응용 프로그램이 하위 하드웨어를 효과적으로 사용할 수 있는지 확인할 수 있다.
    Amdahl의 법칙
    Amdahl 법칙은 하나의 직렬 프로세서로만 문제를 해결하는 것보다 병렬 프로세서로 문제를 해결하는 가속비에 관한 법칙이다.가속도(Speedup)는 직렬 실행(한 프로세서만 사용)과 같은 시간으로 프로그램의 병렬 실행(여러 프로세서 사용)을 나눈 시간입니다.
          T(1)
    
    S = ------
    
          T(j)
    
          

    이 중 T(j)j 개의 프로세서 실행 프로그램을 사용하는 데 걸린 시간이다.
    Amdahl 법칙은 이런 가속비는 현실에서 일어나지 않을 수도 있지만 그 값에 매우 가깝다는 것을 설명한다.일반적인 상황에 대해 말하자면, 우리는 모든 프로그램에 직렬 구성 요소가 있다는 것을 추론할 수 있다.문제 집합이 커지면서 직렬 구성 요소는 최종적으로 해결 방안을 최적화하는 데 한계에 도달할 것이다.
    Amdahl 법칙은 높은 CPU 캐시 적중률을 유지하기를 원할 때 특히 중요하다.주어진 프로세스가 다른 곳으로 옮겨지면, CPU 캐시를 사용하는 장점을 잃게 된다.실제로 사용 중인 CPU가 자신을 위해 특수한 데이터를 캐시해야 한다면, 모든 다른 CPU는 이 데이터를 자신의 캐시에서 효력을 잃게 할 것이다.
    따라서 여러 개의 스레드가 같은 데이터를 필요로 한다면, 이 스레드를 특정한 CPU에 연결하는 것은 매우 의미가 있다. 이렇게 하면 같은 캐시 데이터에 접근할 수 있거나 최소한 캐시의 명중률을 높일 수 있다.그렇지 않으면, 이 스레드들은 서로 다른 CPU에서 실행될 수 있으며, 이렇게 하면 다른 캐시 항목이 빈번하게 효력을 상실할 수 있다.
    원인시간에 민감하고 결정적인 프로세스를 실행하고 있습니다
    CPU 친화성(affinity)에 관심을 갖는 마지막 이유는 실시간 프로세스입니다.예를 들어, 8번 호스트의 어떤 프로세서를 지정하고, 다른 7개의 프로세서가 모든 일반적인 시스템 스케줄링을 처리할 수 있도록 하기 위해 하드 친화성 (affinity) 을 사용할 수도 있습니다.이런 방법은 장시간 운행하고 시간에 민감한 응용 프로그램이 실행될 수 있을 뿐만 아니라 다른 응용 프로그램이 나머지 계산 자원을 독점할 수 있도록 허용할 수 있다.
    아래의 예시 프로그램은 이것이 어떻게 작동하는지 보여 준다.
    맨 위로
    어떻게 경친화성(affinity)을 이용합니까
    리눅스 시스템을 매우 바쁘게 할 수 있는 프로그램을 설계해 보겠습니다.앞에서 설명한 시스템 호출과 시스템에 몇 개의 프로세서가 있는지 설명하는 API를 사용하여 이 프로그램을 구축할 수 있습니다.실제로, 우리의 목표는 이러한 프로그램을 작성하는 것이다. 시스템의 모든 프로세서를 몇 초 동안 바쁘게 할 수 있다.다음 섹션샘플 프로그램 다운로드에서 다운로드할 수 있습니다.
    명세서프로세서 사용
                    
    
    /* This method will create threads, then bind each to its own cpu. */
    
    bool do_cpu_stress(int numthreads)
    
    {
    
       int ret = TRUE;
    
       int created_thread = 0;
    
       /* We need a thread for each cpu we have... */
    
       while ( created_thread < numthreads - 1 )
    
       {
    
          int mypid = fork();
    
          if (mypid == 0) /* Child process */
    
           {
    
              printf("\tCreating Child Thread: #%i
    ", created_thread); break; } else /* Only parent executes this */ { /* Continue looping until we spawned enough threads! */ ; created_thread++; } } /* NOTE: All threads execute code from here down! */

     
    보시다시피 이 코드는 fork 호출을 통해 간단하게 라인을 만들 뿐입니다.모든 라인은 이 방법의 뒤에 있는 코드를 실행한다.이제 각 스레드마다 친화성(affinity)을 자신의 CPU로 설정합니다.
    명세서스레드당 CPU 친화성(affinity) 설정
                    
    
       cpu_set_t mask;
    
       /* CPU_ZERO initializes all the bits in the mask to zero. */
    
            CPU_ZERO( &mask );
    
       /* CPU_SET sets only the bit corresponding to cpu. */
    
            CPU_SET( created_thread, &mask );
    
       /* sched_setaffinity returns 0 in success */
    
            if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )
    
       {
    
          printf("WARNING: Could not set CPU Affinity, continuing...
    "); }

     
    만약 프로그램이 여기까지 실행될 수 있다면, 우리의 루트는 이미 자신의 친화성 (affinity) 을 설정했다.호출sched_setaffinitypid에서 인용한 프로세스의 CPU 친화성(affinity) 마스크를 설정합니다.pid가 0이면 현재 프로세스를 사용합니다.
    친화성(affinity) 마스크는 mask에 저장된 비트 마스크로 표시된다.가장 낮은 비트는 시스템의 첫 번째 논리 프로세서에 대응하고, 가장 높은 비트는 시스템의 마지막 논리 프로세서에 대응한다.
    모든 설정의 비트는 합법적으로 스케줄링할 수 있는 CPU에 대응하고, 설정하지 않은 비트는 스케줄링할 수 없는 CPU에 대응한다.다시 말하면 프로세스가 귀속되어 대응하는 위치가 설정된 프로세서에서만 실행될 수 있다.일반적으로 마스크의 모든 비트가 배치됩니다.이 라인들의 친화성(affinity)은 파생된 하위 프로세스에 전달된다.
    비트 마스크를 직접 수정해서는 안 됩니다.아래의 매크로를 사용해야 합니다.우리의 예에서 이 매크로를 모두 사용하지 않았지만, 본문에는 이 매크로들이 상세하게 열거되어 있습니다. 프로그램에서 이 매크로가 필요할 수도 있습니다.
    명세서비트 마스크를 간접적으로 수정하는 매크로
                    
    
    void CPU_ZERO (cpu_set_t *set)
    
         CPU   set      ,       。
    
    void CPU_SET (int cpu, cpu_set_t *set)
    
         cpu    CPU   set  。
    
    void CPU_CLR (int cpu, cpu_set_t *set)
    
         cpu   CPU   set    。
    
    int CPU_ISSET (int cpu, const cpu_set_t *set)
    
       cpu   CPU   set    ,           (true),      (false)。
    
    

     
    본고의 경우 샘플 코드는 모든 라인이 계산량이 비교적 큰 조작을 계속 수행하도록 할 것이다.
    명세서모든 스레드는 계산에 민감한 조작을 실행한다
                    
    
        /* Now we have a single thread bound to each cpu on the system */
    
        int computation_res = do_cpu_expensive_op(41);
    
        cpu_set_t mycpuid;
    
        sched_getaffinity(0, sizeof(mycpuid), &mycpuid);
    
        if ( check_cpu_expensive_op(computation_res) )
    
        {
    
          printf("SUCCESS: Thread completed, and PASSED integrity check!
    ", mycpuid); ret = TRUE; } else { printf("FAILURE: Thread failed integrity check!
    ", mycpuid); ret = FALSE; } return ret; }

     
    이제 Linux 2.6 버전의 커널에 CPU 친화성(affinity)을 설정하는 기본 지식을 습득할 수 있습니다.다음에, 우리는 main 프로그램을 사용하여 이 방법을 봉인한다. 이것은 사용자가 지정한 파라미터를 사용하여 몇 개의 CPU를 바쁘게 해야 하는지를 설명한다.시스템에는 몇 개의 프로세서가 있는지 결정하는 다른 방법이 있습니다.int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);
    이 방법은 프로그램이 얼마나 많은 프로세서를 바쁘게 해야 하는지 스스로 결정할 수 있게 한다. 예를 들어 모든 프로세서를 바쁘게 하고, 사용자가 시스템의 실제 프로세서 범위의 하위 집합을 지정할 수 있도록 한다.
    맨 위로
    샘플 프로그램 실행
    앞에서 설명한 샘플 프로그램 을 실행할 때, 많은 도구를 사용하여 CPU가 바쁜지 확인할 수 있습니다.단순히 테스트하는 경우에는 Linux 명령top을 사용할 수 있습니다.top 명령을 실행할 때 "1"키를 누르면 각 CPU 실행 프로세스가 차지하는 비율을 볼 수 있습니다.
    맨 위로
    끝말
    이 예시 프로그램은 매우 간단하지만 리눅스 핵을 이용하여 이루어진 경친화성(affinity)의 기본 지식을 보여준다.(이 코드를 사용하는 모든 프로그램은 의심할 여지없이 더 의미 있는 일을 할 것이다.)CPU 친화성(affinity) 커널 API의 기본 지식을 이해하면 복잡한 응용 프로그램에서 마지막 성능을 추출할 수 있습니다.

    좋은 웹페이지 즐겨찾기