단일 스레드는 여러 코어에서 어떻게 실행됩니까?


61

높은 수준에서 단일 스레드가 여러 코어에서 어떻게 실행되는지 이해하려고합니다. 아래는 나의 최선의 이해입니다. 나는 그것이 옳다고 생각하지 않습니다.

내가 읽은 하이퍼 스레딩 에 따르면 OS는 모든 스레드의 명령을 서로 기다리지 않는 방식으로 구성합니다. 그런 다음 CPU의 프론트 엔드는 하나의 스레드를 각 코어에 분배하여 해당 명령어를 추가로 구성하고 열린주기 중 각 스레드에서 독립적 인 명령어를 분배합니다.

따라서 단일 스레드 만있는 경우 OS는 최적화를 수행하지 않습니다. 그러나 CPU의 프론트 엔드는 각 코어에 독립적 인 명령어 세트를 분배합니다.

https://stackoverflow.com/a/15936270 에 따르면 특정 프로그래밍 언어는 더 많거나 적은 스레드를 만들 수 있지만 해당 스레드로 수행 할 작업을 결정할 때는 관련이 없습니다. OS 및 CPU가이를 처리하므로 사용 된 프로그래밍 언어에 관계없이 발생합니다.

여기에 이미지 설명을 입력하십시오

명확히하기 위해 단일 코어에서 여러 스레드를 실행하는 것이 아니라 여러 코어에서 단일 스레드 실행을 묻습니다.

내 요약에 어떤 문제가 있습니까? 스레드의 명령어는 여러 코어로 어디서 어떻게 분할됩니까? 프로그래밍 언어가 중요합니까? 나는 이것이 넓은 주제라는 것을 안다. 나는 그것에 대한 높은 수준의 이해를 기대하고 있습니다.


6
단일 소프트웨어 스레드에 대한 일련의 명령어는 여러 코어에서 실행될 수 있지만 한 번에 실행할 수는 없습니다.
Kroltan

1
소프트웨어 스레드 (OS 스케줄러 포함)와 하드웨어 스레드 또는 하이퍼 스레딩 (한 코어가 두 개처럼 동작하도록하는 CPU 기능)을 혼합하고 있습니다.
ugoren 2016 년

2
나는 20 명의 운전자와 4 대의 트럭을 가지고있다. 한 명의 운전자가 두 대의 트럭으로 패키지를 배송 할 수있는 방법은 무엇입니까? 한 트럭에 여러 명의 운전자가있을 수있는 방법은 무엇입니까? 두 질문에 대한 답은 같습니다. 차례를 바꾸십시오.
Eric Lippert

답변:


84

운영 체제는 실행 가능한 스레드에 CPU의 시간 조각을 제공 합니다.

코어가 하나 뿐인 경우 운영 체제는 타임 슬라이스에 대해 해당 코어에서 가장 적합한 스레드가 실행되도록 예약합니다. 타임 슬라이스가 완료된 후 또는 실행중인 스레드가 IO에서 차단되거나 프로세서가 외부 이벤트에 의해 중단 된 경우 운영 체제는 다음에 실행할 스레드를 재평가합니다 (그리고 동일한 스레드를 다시 선택하거나 다른 스레드를 선택할 수 있음).

실행 자격은 공정성과 우선 순위 및 준비 상태에 대한 변형으로 구성되며이 방법으로 다양한 스레드가 다른 스레드보다 시간 조각을 얻습니다.

코어 N이 여러 개인 경우 운영 체제는 코어에서 가장 적합한 N 스레드가 실행되도록 예약합니다.

프로세서 선호도 는 효율성을 고려합니다. CPU가 이전과 다른 스레드를 실행할 때마다 캐시가 이전 스레드에서는 따뜻하지만 새 스레드에서는 차갑기 때문에 비트 속도가 느려지는 경향이 있습니다. 따라서 여러 시간 조각에 걸쳐 동일한 프로세서에서 동일한 스레드를 실행하면 효율성이 향상됩니다.

그러나 운영 체제는 다른 CPU에서 하나의 스레드 시간 슬라이스를 자유롭게 제공 할 수 있으며 다른 시간 조각에서 모든 CPU를 통해 회전 할 수 있습니다. 그러나 @ gnasher729가 말한 것처럼 여러 CPU에서 동시에 하나의 스레드를 실행할 수는 없습니다 .

하이퍼 스레딩은 하나의 향상된 CPU 코어가 둘 이상의 서로 다른 스레드의 실행을 동시에 지원할 수 있는 하드웨어 방법입니다 . (이러한 CPU는 추가 풀 코어보다 실리콘 실체에서 더 낮은 비용으로 추가 스레드를 제공 할 수 있습니다.)이 향상된 CPU 코어는 CPU 레지스터 값과 같은 다른 스레드에 대한 추가 상태를 지원해야하며 조정 상태 및 동작도 있습니다. 스레드를 확장하지 않고 해당 CPU 내에서 기능 단위를 공유 할 수 있습니다.

하이퍼 스레딩은 하드웨어 관점에서, 프로그래머 관점에서 기술적으로 어려운 반면, 실행 모델은 더 복잡한 것이 아니라 추가 CPU 코어의 모델입니다. 따라서 여러 하이퍼 스레드 스레드가 하나의 CPU 코어의 캐시 아키텍처를 공유함에 따라 새로운 프로세서 선호도 문제가 있지만 운영 체제에는 추가 CPU 코어가 표시됩니다.


하이퍼 스레드 코어에서 실행되는 두 개의 스레드가 각각 자체 코어를 사용하는 속도의 절반만큼 빠르다고 생각할 수도 있습니다. 그러나 단일 스레드의 실행이 슬랙 사이클로 가득 차 있고 다른 하이퍼 스레드 스레드에서 일부를 사용할 수 있기 때문에 반드시 그런 것은 아닙니다. 또한 비 슬랙 사이클에서도 하나의 스레드가 다른 스레드와 다른 기능 단위를 사용하므로 동시 실행이 발생할 수 있습니다. 하이퍼 스레딩을위한 향상된 CPU에는 특별히이를 지원하기 위해 많이 사용되는 특정 기능 장치가 몇 개 더있을 수 있습니다.


3
"따라서 여러 시간 조각에 걸쳐 동일한 프로세서에서 동일한 스레드를 실행하면 효율성이 향상됩니다." 그럴 필요하지 않을까요 연속 시간 조각? 그렇지 않으면 캐시가 다른 스레드에 의해 지워집니다. 좋은 설명은 +1입니다.
jpmc26 2016 년

2
@Luaan : HT는 종종 좋지만 상황을 설명하는 것처럼 간단하지는 않습니다. 프론트 엔드 문제 대역폭 (인텔에서 클럭 당 4 uops, Ryzen에서 6 개)은 스레드간에 균등하게 공유됩니다 (중단되지 않은 경우). 그것이 병목 현상이라면, HT가 전혀 도움이되지 않는다고 말한 것처럼. 부하, ALU 및 저장소가 혼합되어있는 경우 Skylake가 잘 조정 된 루프에서 그에 근접하는 것은 드문 일이 아닙니다. 트랜지스터는 저렴합니다 (트랜지스터가 모두 저렴하거나 CPU가 녹을 수는 없습니다) 최신 x86 CPU는 프론트 엔드가 공급할 수있는 것보다 더 많은 실행 포트를 가지고 있습니다 (많은 실행 장치가 복제 됨).
Peter Cordes

2
... 여러 포트에서) ... 이것은 낭비처럼 보일 수 있지만 종종 루프는 한 번에 한 종류의 ALU 실행 장치 만 사용하므로 모든 항목이 중복되면 어떤 종류의 코드가 실행 되든지 여러 가지가 있음을 의미합니다 해당 명령어 포트. 따라서 대부분의 코드에는 프런트 엔드 대역폭을 차지하는 일부로드 및 / 또는 저장소가 있고 남은 것은 실행 단위를 포화시키기에 충분하지 않기 때문에 HT의 이점을 인용 한 이유는 그리 일반적이지 않습니다.
Peter Cordes

2
@Luaan : 또한 Intel CPU에서 정수 및 FP / 벡터 실행 단위는 동일한 실행 포트를 공유합니다 . 예를 들어, FP FMA / mul / add 장치는 포트 0/1에 있습니다. 그러나 정수 승수도 포트 1에 있으며 간단한 정수 연산은 4 개의 실행 포트 중 하나에서 실행할 수 있습니다 (응답의 다이어그램). 문제 발생 대역폭을 사용하는 두 번째 스레드는 실행 단위와 경쟁하지 않더라도 속도가 느려지지만 캐시와 경쟁이 심하지 않으면 순 처리량 증가가 종종 발생합니다. x264 / x265 (비디오 인코더)와 같이 잘 조정 된 고 처리량 코드조차도 HT의 Skylake에서 약 15 %의 이점을 얻습니다.
Peter Cordes

3
@luaan Peter가 말한 것 외에도 "그것은 HT의 원래 추론이었다"고 주장하는 것이 잘못되었습니다. HT의 근본적인 이유는 NetBurst 마이크로 아키텍처가 분기 클럭의 ​​오작동 및 기타 파이프 라인 버블 성능을 완전히 떨어 뜨릴 정도로 (클럭 속도를 높이기 위해) 파이프 라인을 극도로 연장했다는 것 입니다. HT는 파이프 라인의 버블로 인해이 고가의 칩 실행 장치가 유휴 상태로 유지되는 시간을 최소화하기위한 인텔 솔루션 중 하나였습니다. 다른 스레드의 코드를 삽입하여 실행할 수 있습니다.
코디 그레이

24

여러 개의 코어에서 동시에 실행되는 단일 스레드와 같은 것은 없습니다.

그러나 한 스레드의 명령어를 병렬로 실행할 수는 없습니다. 이를 허용하는 명령 파이프 라이닝순서가 잘못된 실행 이라는 메커니즘이 있습니다 . 각 코어에는 간단한 명령어로 사용되지 않는 많은 중복 리소스가 있으므로 다음 명령어가 이전 결과에 의존하지 않는 한 여러 명령어를 함께 실행할 수 있습니다. 그러나 이것은 여전히 ​​단일 코어 내부에서 발생합니다.

하이퍼 스레딩은 하나의 코어가 한 스레드의 명령을 병렬로 실행할뿐만 아니라 두 개의 다른 스레드의 명령을 혼합하여 리소스 사용을 더욱 최적화하는이 아이디어의 극단적 인 변형입니다.

관련 Wikipedia 항목 : 명령 파이프 라인 , 순서가 잘못된 실행 .


3
동시에 실행할 수는 없지만 동시에 실행할 수 있습니까? 이것들은 같은 것이 아닌가?
Evorlor 2016 년

10
@Evorlor 여기서 핵심은 코어와 실행 유닛의 차이점입니다. 단일 스레드는 하나의 코어에서만 실행될 수 있지만 프로세서는 동적 분석을 사용하여 코어에 의해 실행되는 명령이 서로 의존하지 않고이를 서로 다른 실행 단위에서 동시에 실행할 수 있습니다. 하나의 코어에는 여러 개의 실행 단위가있을 수 있습니다.
user1937198 2016 년

3
@Evorlor : 비 순차적 CPU는 단일 스레드의 명령 스트림 내에서 명령 레벨 병렬 처리를 찾아 이용할 수 있습니다 . 예를 들어, 루프 카운터를 업데이트하는 명령어는 루프가 수행하는 다른 작업과 독립적입니다. 또는 a[i] = b[i] + c[i]루프에서 각 반복은 독립적이므로 다른 반복에서로드, 추가 및 저장을 한 번에 수행 할 수 있습니다. 명령이 프로그램 순서대로 실행되었다는 환상을 유지해야하지만, 예를 들어 캐시에서 누락 된 저장소는 스레드를 지연시키지 않습니다 (저장소 버퍼의 공간이 부족해질 때까지).
Peter Cordes 2016 년

3
@ user1937198 : "동적 분석"이라는 문구가 JIT 컴파일러에 더 적합합니다. 고장난 CPU는 실제로 분석 하지 않습니다 . 그것은 해독되고 발행 된 모든 명령을 실행하고 입력을 준비하는 탐욕스러운 알고리즘과 같습니다. 비 순차적 재정렬 창은 몇 가지 마이크로 아키텍처 리소스에 의해 제한됩니다. 예를 들어 Intel Sandybridge의 재주문 버퍼 크기는 168 uops입니다 ( 실험적으로 ROB 크기 측정 참조 ). 모두 하드웨어 상태 머신으로 구현되어 클럭 당 4 개의 UOP를 처리합니다.
Peter Cordes

3
@Luaan 네, 흥미로운 아이디어 였지만 AOT 컴파일러는 여전히 그것을 충분히 활용하기에는 영리하지 않습니다. 또한 Linus Torvalds (및 기타)는 파이프 라인 내부의 많은 부분을 미래 설계에 큰 제약이 있다고 주장했습니다. 예를 들어 ISA를 변경하지 않고 파이프 라인 너비를 실제로 늘릴 수는 없습니다. 또는 일반적인 방법으로 종속성을 추적하는 CPU를 빌드하고 두 개의 VLIW 그룹을 병렬로 발행 할 수 있지만 EPIC의 CPU 복잡성 이점을 잃었지만 여전히 단점이 있습니다 (컴파일러가 채울 수없는 경우 대역폭 손실) 단어).
Peter Cordes

22

요약 : 단일 스레드 프로그램에서 (명령 수준) 병렬 처리찾아서 이용하는 것은 하드웨어에서 실행중인 CPU 코어에 의해 순수하게 수행됩니다. 그리고 대규모 재정렬이 아닌 수백 가지 명령의 창 이상.

단일 스레드 프로그램 은 단일 스레드 작업에서 시간을 허비하지 않고 다른 코어에서 다른 작업을 실행할 수 있다는 점을 제외하고 멀티 코어 CPU의 이점을 얻지 못합니다 .


OS는 모든 스레드의 명령을 서로 기다리지 않는 방식으로 구성합니다.

OS는 스레드의 명령 스트림 내부를 찾지 않습니다. 스레드 만 코어에 예약합니다.

실제로 각 코어는 다음에 수행 할 작업을 파악해야 할 때 OS의 스케줄러 기능을 실행합니다. 스케줄링은 분산 알고리즘입니다. 멀티 코어 머신을 더 잘 이해하려면 각 코어가 커널을 개별적으로 실행하는 것으로 생각하십시오. 멀티 스레드 프로그램과 마찬가지로 커널은 한 코어의 코드가 다른 코어의 코드와 안전하게 상호 작용하여 실행할 준비가 된 스레드 목록과 같은 공유 데이터 구조를 업데이트 할 수 있도록 작성되었습니다.

어쨌든 OS는 멀티 스레드 프로세스 가 멀티 스레드 프로그램을 수동으로 작성하여 명시 적으로 노출되어야하는 스레드 수준 병렬 처리를 이용하는 데 도움이 됩니다 . (또는 OpenMP 등 을 사용하는 자동 병렬 컴파일러로 ).

그런 다음 CPU의 프론트 엔드는 하나의 스레드를 각 코어에 분배하여 해당 명령어를 추가로 구성하고 열려있는주기마다 각 스레드에서 독립적 인 명령어를 분배합니다.

CPU 코어는 중지되지 않은 경우 하나의 명령 스트림 만 실행합니다 (예 : 다음 인터럽트까지 잠자기 (예 : 타이머 인터럽트)). 종종 스레드이지만, 커널 인터럽트 처리기 또는 커널이 처리 및 인터럽트 또는 시스템 호출 후 이전 스레드로 돌아 가기 이외의 작업을하기로 결정한 경우 기타 커널 코드 일 수도 있습니다.

HyperThreading 또는 기타 SMT 설계에서 물리적 CPU 코어는 여러 "논리적"코어처럼 작동합니다. 쿼드 코어와 하이퍼 스레딩 (4c8t) CPU 및 일반 8 코어 머신 (8c8t)의 OS 관점과의 유일한 차이점은 HT 인식 OS가 스레드를 물리적 코어를 분리하도록 스케줄링하려고 시도한다는 것입니다. 서로 경쟁하지 마십시오. 하이퍼 스레딩에 대해 알지 못한 OS는 8 개의 코어 만 표시합니다 (BIOS에서 HT를 비활성화하지 않으면 4 개만 감지).


" 프론트 엔드" 라는 용어 는 기계어 코드를 가져 와서 명령어를 해독하여 코어의 비 순차적 부분으로 발행하는 CPU 코어 부분을 의미합니다 . 각 코어에는 자체 프론트 엔드가 있으며 코어 전체의 일부입니다. 그것이 가져 오는 지침 입니다 CPU가 현재 실행중인 것.

코어의 비 순차적 부분 내에서 명령 (또는 uops)은 입력 피연산자가 준비되고 사용 가능한 실행 포트가있을 때 실행 포트로 전달됩니다. 이것은 프로그램 순서로 발생하지 않아도되므로 OOO CPU 가 단일 스레드 내 에서 명령 수준 병렬 처리를 활용할 수있는 방법 입니다.

아이디어에서 "핵심"을 "실행 단위"로 바꾸면 정확합니다. 그렇습니다. CPU는 독립적 인 명령어 / UOP를 실행 유닛에 병렬로 배포합니다. (하지만 실제로는 CPU의 명령 스케줄러 (예약 스테이션이라고도 함)가 실행 준비가 된 명령을 선택하는 경우 "프론트 엔드"라고 말했기 때문에 용어 혼합이 있습니다.)

비 순차적 실행은 두 개의 독립 루프 사이가 아니라 매우 짧은 로컬 수준에서만 최대 200 개의 명령어 만 ILP를 찾을 수 있습니다.


예를 들어, 이것과 동등한 asm

int i=0,j=0;
do {
    i++;
    j++;
} while(42);

Intel Haswell에서 카운터 하나만 증가시키는 동일한 루프만큼 빠르게 실행됩니다. i++의 이전 값 에만 의존 i하는 반면, 의 이전 값 j++에만 의존 j하므로 두 개의 종속 체인 은 프로그램 순서로 실행되는 모든 것을 환영하지 않고 병렬로 실행될 수 있습니다.

x86에서 루프는 다음과 같습니다.

top_of_loop:
    inc eax
    inc edx
    jmp .loop

Haswell에는 4 개의 정수 실행 포트가 있으며 모든 포트에는 가산기 장치가 있으므로 inc모두 독립적 인 경우 클럭 당 최대 4 개의 명령 처리량을 유지할 수 있습니다 . (지연 시간이 1 인 경우 4 개의 inc명령을 비행 상태로 유지하여 처리량을 최대화하려면 4 개의 레지스터 만 필요합니다 . 벡터 -FP MUL 또는 FMA와 대조 : 지연 시간 = 5 처리량 = 0.5는 10 개의 FMA를 비행하기 위해 10 개의 벡터 누산기가 필요합니다. 각 벡터는 256b 일 수 있으며 8 개의 단 정밀도 부동 소수점을 보유합니다.

가져온 분기 처리량도 병목 현상입니다. 분기당 처리량은 클럭 당 1로 제한되므로 루프는 항상 반복 당 하나 이상의 전체 클럭을 사용합니다. 또한 읽기 / 쓰기 eax또는 edx종속성 체인을 연장 하지 않는 한 성능 저하없이 루프 안에 명령을 하나 더 넣을 수 있습니다. 루프에 2 개의 명령어를 추가하면 (또는 하나의 복잡한 다중 Uop 명령어) 프런트 엔드에 병목 현상이 발생합니다. 비 순차적 코어에는 클럭 당 4 개의 UOP 만 발행 할 수 있기 때문입니다. ( 4 uops의 배수가 아닌 루프에서 발생하는 일에 대한 자세한 내용 은 이 SO Q & A 를 참조하십시오 : 루프 버퍼 및 uop 캐시는 흥미로운 일입니다.)


더 복잡한 경우, 병렬 처리를 찾으려면 더 큰 명령 창을 살펴 봐야 합니다. (예를 들어 10 개의 명령이 모두 서로 의존하는 명령이있을 수 있으며, 일부는 독립적 인 명령이있을 수 있습니다).

재주문 버퍼 용량은 비 순차적 창 크기를 제한하는 요소 중 하나입니다. Intel Haswell에서는 192 개입니다. ( 레지스터 이름 바꾸기 용량 (레지스터 파일 크기)과 함께 실험적으로 측정 할 수도 있습니다 .) ARM과 같은 저전력 CPU 코어는 비 순차적 실행을 수행 할 경우 ROB 크기가 훨씬 작습니다.

또한 CPU는 비 순차적 일뿐만 아니라 파이프 라인되어야합니다. 따라서 실행중인 명령보다 먼저 명령을 페치 및 디코딩해야합니다. 페치주기가 누락 된 후 버퍼를 리필하기에 충분한 처리량으로 처리하는 것이 좋습니다. 지점이 어떤 방식으로 진행되는지 모르는 경우 어디에서 가져와야할지 모르기 때문에 지점이 까다로워집니다. 이것이 분기 예측이 중요한 이유입니다. (그리고 현대 CPU가 추론 적 실행을 사용하는 이유 : 브랜치가 어느 방향으로 가고 명령 스트림을 페치 / 디코딩 / 실행하기 시작하는지 추측합니다. 잘못된 예측이 감지되면 마지막으로 성공한 상태로 롤백하고 거기서부터 실행됩니다.)

CPU 내부에 대한 자세한 내용을 보려면 Agner Fog의 마이크로 아치 안내서 및 Intel 및 AMD CPU 다이어그램이있는 David Kanter의 자세한 글을 포함 하여 Stackoverflow x86 태그 위키에 링크가 있습니다. 그의 Intel Haswell 마이크로 아키텍처 작성에서 , 이것은 칩 전체가 아닌 Haswell 코어의 전체 파이프 라인의 최종 다이어그램입니다.

이것은 단일 CPU 코어 의 블록 다이어그램입니다 . 쿼드 코어 CPU에는 칩에 4 개가 있으며 각각 L1 / L2 캐시 (L3 캐시, 메모리 컨트롤러 및 시스템 장치에 대한 PCIe 연결 공유)가 있습니다.

Haswell 전체 파이프 라인

나는 이것이 엄청나게 복잡하다는 것을 알고 있습니다. Kanter의 기사는 또한 예를 들어 실행 유닛 또는 캐시와 별도로 프론트 엔드에 대해 설명하기 위해이 부분을 보여줍니다.


2
"단일 스레드 프로그램에서 (명령 수준) 병렬 처리를 찾아서 이용하는 것은 하드웨어에서 순전히 수행됩니다."이것은 컴파일러 나 프로그래머가 ILP를 완전히 결정하거나 하드웨어간에 협력 적으로 ILP를 결정하는 VLIW가 아닌 기존 ISA에만 적용됩니다. 그리고 소프트웨어.
Hadi Brais

1
@ user7813604 : 예. 하이퍼 스레딩은 단일 스레드를 병렬화 할 수 없습니다. 반대로 하나의 코어에서 여러 스레드를 실행하여 스레드 당 성능을 줄이지 만 전체 처리량은 증가시킵니다.
Peter Cordes

1
@ user7813604 : ILP의 요점은 각 명령이 순서대로 실행되었다는 환상 을 유지하면서 각 명령이 다음 명령이 시작되기 전에 마무리 되는 동안 병렬로 실행할 수있는 명령을 찾는 것입니다 . 대기 시간이 1보다 큰 경우 스칼라 파이프 라인 된 CPU는 때때로 종속성 때문에 정지해야 할 수도 있지만 수퍼 스칼라 CPU에서는 훨씬 더 큰 문제입니다.
Peter Cordes

1
@ user7813604 : 예, 내 대답은 문자 그대로 예제로 사용합니다. 예를 들어 Haswell inc은 동일한 클록 사이클에서 4 개의 정수 ALU 실행 장치 까지 최대 4 개의 명령을 실행할 수 있습니다 .
Peter Cordes

1
@ user7813604 : 예, ILP는 병렬로 실행할 있는 양 입니다. 실제 CPU는 실제로 단일 코어 내에서 ILP를 병렬로 실행하여 ILP를 찾고 악용 할 수있는 능력이 제한적입니다 (예 : Intel의 최대 4 와이드 수퍼 스칼라). 이 답변은 예제를 통해 설명하려고합니다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.