성능 향상을 위해 멀티 스레딩이 선호되는 이유는 무엇입니까?


23

질문이 있는데, 프로그래머가 왜 동시성과 멀티 스레드 프로그램을 좋아하는 것 같습니다.

나는 여기에 두 가지 주요 접근법을 고려하고 있습니다 :

  • 기본적으로 신호를 기반으로하는 비동기 방식 또는 새로운 C # 5.0과 같은 많은 논문 및 언어에서 호출하는 비동기 방식 및 파이프 라인 정책을 관리하는 "컴패니언 스레드"
  • 동시 접근 또는 다중 스레딩 접근

나는 여기서 하드웨어와 최악의 시나리오에 대해 생각하고 있으며이 2 가지 패러다임을 직접 테스트했으며 비동기 패러다임은 사람들이 왜 90 %의 시간을 얻지 못하는지에 대한 승자입니다 작업 속도를 높이거나 리소스를 제대로 활용하려는 경우 멀티 스레딩에 대해 이야기하십시오.

CPU 내에서 메모리 컨트롤러를 제공하지 않는 Intel 쿼드 코어가있는 오래된 컴퓨터에서 멀티 스레드 프로그램과 비동기 프로그램을 테스트했으며 메모리는 완전히 마더 보드로 관리 되며이 경우 성능은 끔찍합니다. 멀티 스레드 응용 프로그램, 3-4-5와 같이 비교적 적은 수의 스레드라도 문제가 될 수 있으며 응용 프로그램이 응답하지 않고 느리고 불쾌합니다.

반면에 좋은 비동기 방식은 아마도 더 빠르지는 않지만 최악도 아닙니다. 응용 프로그램이 결과를 기다렸다가 멈추지 않고 응답 성이 있으며 훨씬 더 나은 스케일링이 진행됩니다.

또한 스레딩 세계의 컨텍스트 변경이 실제 시나리오에서는 그렇게 저렴하지 않다는 사실을 발견했습니다. 실제로 계산하고 서로 교환 해야하는 스레드가 2 개 이상인 경우 특히 비용이 많이 듭니다.

현대 CPU에서는 상황이 그렇게 다르지 않습니다. 통합 된 메모리 컨트롤러이지만 제 요점은 x86 CPU는 기본적으로 직렬 시스템이며 메모리 컨트롤러는 마더 보드에 외부 메모리 컨트롤러가있는 오래된 시스템과 동일한 방식으로 작동한다는 것입니다 . 컨텍스트 스위치는 여전히 내 응용 프로그램에서 관련 비용이며 통합 된 메모리 컨트롤러 또는 최신 CPU에 코어가 2 개 이상 있다는 것이 사실입니다.

내가 경험 한 것은 이론적으로는 좋지만 실제로는 그렇지는 않지만 하드웨어가 부과하는 메모리 모델에서는이 패러다임을 잘 활용하기가 어렵고 사용에서 다양한 문제가 발생합니다. 여러 스레드의 조인에 대한 내 데이터 구조.

또한 두 패러다임은 작업 또는 작업이 특정 시점에 수행 될 때 보안에 대한 접견을 제공하지 않으므로 기능 측면에서 실제로 유사합니다.

X86 메모리 모델에 따르면, 대다수의 사람들이 비동기 접근 방식뿐만 아니라 C ++와 동시성을 사용하도록 제안하는 이유는 무엇입니까? 또한 컨텍스트 스위치가 계산 자체보다 비싼 컴퓨터의 최악의 시나리오를 고려하지 않는 이유는 무엇입니까?


2
비교하는 한 가지 방법은 자바 스크립트 세계를 보는 것입니다. 스레딩이없고 콜백을 사용하여 모든 것이 적극적으로 비동기 적이었습니다. 작동하지만 자체 문제가 있습니다.
로봇

2
@StevenBurnap 당신은 무엇을 웹 워커라고 부릅니까?
user16764

2
"3-4-5와 같이 비교적 적은 수의 스레드도 문제가 될 수 있습니다. 응용 프로그램이 응답하지 않고 느리고 불쾌합니다." => 잘못된 디자인 / 부적절한 스레드 사용으로 인한 것일 수 있습니다. 일반적으로 스레드가 데이터를 교환 할 때 이런 상황이 발생하는 경우가 있습니다.이 경우 멀티 스레딩이 정답이 아니거나 데이터를 다시 파티션해야 할 수도 있습니다.
assylias

1
@assylias UI 스레드가 크게 느려지는 것은 스레드 전체에 과도한 잠금이 있음을 나타냅니다. 구현이 잘못되었거나 사각형 구멍을 둥근 구멍에 넣으려고합니다.
Evan Plaice

5
"프로그래머는 일반적으로 동시성 및 다중 스레드 프로그램을 좋아하는 것 같습니다." 나는 "프로그래머들이 그것을 싫어한다"고 말할 것입니다 ... 그러나 종종 유일하게 유용한 일입니다 ...
johannes

답변:


34

당신은 멀티 코어 / procesors이 사용

비동기 대량 IO 바인딩 처리를 수행하는 데 가장 좋지만 CPU 바인딩 처리는 어떻습니까?

장기 실행 프로세스에서 단일 스레드 코드 블록 (즉, 멈춤)이 발생하면 문제가 발생합니다. 예를 들어, 워드 프로세서 문서를 인쇄 할 때 작업이 전송 될 때까지 전체 응용 프로그램이 정지되는 것을 기억하십니까? 응용 프로그램 정지는 CPU를 많이 사용하는 작업 중 단일 스레드 응용 프로그램 차단의 부작용입니다.

다중 스레드 응용 프로그램에서 CPU를 많이 사용하는 작업 (예 : 인쇄 작업)을 백그라운드 작업자 스레드로 보내 UI 스레드를 비울 수 있습니다.

마찬가지로, 다중 프로세스 응용 프로그램에서 작업 (예 : IPC, 소켓 등)을 통해 작업을 처리하도록 특별히 설계된 하위 프로세스로 작업을 보낼 수 있습니다.

실제로 비동기 다중 스레드 / 프로세스 코드에는 각각 장점과 단점이 있습니다.

주요 클라우드 플랫폼에서는 CPU 바운드 처리에 특화된 인스턴스와 IO 바운드 처리에 특화된 인스턴스를 제공하므로 트렌드를 확인할 수 있습니다.

예 :

  • 스토리지 (예 : Amazon S3, Google Cloud Drive)는 CPU 바인딩
  • 웹 서버는 IO 바인딩 (Amazon EC2, Google App Engine)
  • 데이터베이스는 쓰기 / 인덱싱을위한 CPU 바운드와 읽기를위한 IO 바운드입니다

그것을 원근법으로 넣으려면 ...

웹 서버는 IO에 강한 플랫폼의 완벽한 예입니다. 연결 당 하나의 스레드를 할당하는 다중 스레드 웹 서버는 확장 된 컨텍스트 전환 및 공유 리소스의 스레드 잠금으로 인해 모든 스레드에 더 많은 오버 헤드가 발생하기 때문에 확장 성이 떨어집니다. 비동기 웹 서버는 단일 주소 공간을 사용합니다.

마찬가지로, 비디오 인코딩을 전문으로하는 응용 프로그램은 멀티 스레드 환경에서 훨씬 더 잘 작동합니다. 관련된 많은 처리로 인해 작업이 완료 될 때까지 주 스레드를 잠글 수 있기 때문입니다. 이를 완화 할 수있는 방법이 있지만 큐를 관리하는 단일 스레드, 정리를 관리하는 두 번째 스레드 및 많은 처리를 관리하는 스레드 풀을 갖는 것이 훨씬 쉽습니다. 스레드 간 통신은 작업이 할당 / 완료된 경우에만 발생하므로 스레드 잠금 오버 헤드가 최소한으로 유지됩니다.

가장 좋은 응용 프로그램은 종종 두 가지의 조합을 사용합니다. 예를 들어, 웹앱은 nginx (예 : 비동기 단일 스레드)를로드 밸런서로 사용하여 수신 요청의 토런트를 관리하고 유사한 비동기 웹 서버 (예 : Node.js)를 사용하여 http 요청을 처리하고 다중 스레드 서버 세트를 사용할 수 있습니다. 콘텐츠 업로드 / 스트리밍 / 인코딩 등 처리

수년 동안 멀티 스레드, 멀티 프로세스 및 비동기 모델 사이에서 많은 종교적 전쟁이있었습니다. 대부분의 경우와 마찬가지로 가장 좋은 대답은 "의존"입니다.

GPU와 CPU 아키텍처를 동시에 사용하는 것을 정당화하는 동일한 사고 방식을 따릅니다. 함께 작동하는 두 개의 특수 시스템은 단일 모 놀리 식 접근 방식보다 훨씬 더 향상 될 수 있습니다.

둘 다 사용하기 때문에 더 좋지 않습니다. 작업에 가장 적합한 도구를 사용하십시오.

최신 정보:

Apache에 대한 참조를 제거하고 사소한 수정을했습니다. Apache는 멀티 프로세스 모델을 사용하여 모든 요청에 ​​대해 프로세스를 분기하여 커널 수준에서 컨텍스트 전환의 양을 증가시킵니다. 또한 프로세스간에 메모리를 공유 할 수 없으므로 각 요청마다 추가 메모리 비용이 발생합니다.

멀티 스레딩은 스레드 간의 공유 메모리에 의존하기 때문에 추가 메모리가 필요합니다. 공유 메모리는 추가 메모리 오버 헤드를 제거하지만 컨텍스트 전환 증가로 인한 불이익을 초래합니다. 또한 경쟁 조건이 발생하지 않도록하기 위해 스레드간에 공유되는 모든 리소스에는 스레드 잠금 (한 번에 하나의 스레드에만 배타적 액세스를 보장)이 필요합니다.

"프로그래머는 일반적으로 동시성 및 다중 스레드 프로그램을 좋아하는 것 같습니다." 멀티 스레드 프로그래밍은 상당량의 시간을 투자 한 사람이라면 누구나 두려워 할 것입니다. 교착 상태 (자원이 실수로 마무리되지 않도록 차단하는 두 개의 다른 소스에 의해 실수로 잠길 때 발생하는 버그) 및 경쟁 조건 (프로그램이 잘못된 순서로 인해 무작위로 잘못된 결과를 임의로 출력하는 경우)은 추적하기 가장 어려운 것 중 일부입니다 아래로 고치십시오.

업데이트 2 :

IPC가 네트워크 (즉, 소켓) 통신보다 빠르다는 담요 설명과는 대조적입니다. 항상 그런 것은 아닙니다 . 이것들은 일반화이며 구현 별 세부 사항은 결과에 큰 영향을 줄 수 있습니다.


프로그래머가 멀티 프로세스해야하는 이유 나는 1 개 이상의 프로세스를 사용하면 상당한 오버 헤드를 추가 할 수있는 일종의 프로세스 간 통신이 필요하다고 가정합니다. 이것은 오래된 Windows 프로그래머와 같은 방식입니까? 언제 멀티 프로세스해야합니까? 그런데 답장을 보내 주셔서 감사합니다. 비동기 및 멀티 스레드가 무엇인지에 대한 좋은 그림.
user1849534

1
프로세스 간 통신이 전체 오버 헤드를 증가 시킨다고 가정합니다. 그러나 처리 상태가 변경 불가능하거나 시작 / 완료시 동기화 만 처리하면됩니다. 더 많은 병렬 작업을 팬 아웃하는 것이 훨씬 더 효율적일 수 있습니다. 액터 패턴은 좋은 예이며, 그것에 대해 읽지 않았다면 실제로 읽을 가치가 있습니다. akka.io
sylvanaar

1
@ user1849534 여러 스레드가 공유 메모리 + 잠금 또는 IPC 를 통해 서로 통신 할 수 있습니다 . 실수하면 잠금이 더 쉽지만 디버그하기가 더 어렵습니다 (예 : 잠금, 교착 상태 누락). 잠금이 확장되지 않기 때문에 작업자 스레드 가 많은 경우 IPC가 가장 좋습니다 . 어느 쪽이든 다중 스레드 방식을 사용하는 경우 스레드 간 통신 / 동기화를 최소로 유지하는 것이 중요합니다 (예 : 오버 헤드 최소화).
Evan Plaice

1
@ akka.io 당신은 완전히 옳습니다. 불변성은 잠금 오버 헤드를 최소화 / 제거하는 한 가지 방법이지만 컨텍스트 전환에 여전히 시간이 걸립니다. 불변성이 스레드 동기화 문제를 해결하는 방법에 대한 세부 정보를 포함하도록 답변을 확장하려면 자유롭게 느끼십시오. 필자가 설명하려는 주요 요점은 비동기 통신이 멀티 스레드 / 프로세스에 비해 뚜렷한 이점이 있고 그 반대의 경우도 있다는 것입니다.
Evan Plaice

(계속) 그러나 솔직히 많은 CPU 바인딩 처리 기능이 필요하다면 액터 모델을 건너 뛰고 여러 네트워킹 노드로 확장 할 수 있도록 빌드했습니다. 내가 본 가장 좋은 솔루션은 소켓 레벨 통신을 통해 0MQ의 작업 인공 호흡기 모델을 사용하는 것입니다. 그림 5 @ zguide.zeromq.org/page:all을 참조하십시오 .
Evan Plaice

13

Microsoft의 비동기식 접근 방식 은 멀티 스레드 프로그래밍을위한 가장 일반적인 목적, 즉 IO 작업에 대한 응답 성을 향상시키는 좋은 방법 입니다.

그러나 비동기 방식은 성능을 전혀 향상 시키거나 CPU를 많이 사용하지 않는 작업에 대한 응답 성을 향상시킬 수 없다는 것을 인식하는 것이 중요합니다.

응답 성을위한 멀티 스레딩

응답 성을위한 멀티 스레딩은 과도한 IO 작업 또는 많은 계산 작업 중에 프로그램을 응답 적으로 유지하는 전통적인 방법입니다. 백그라운드 스레드에 파일을 저장하면 하드 드라이브가 작업을 완료 할 때까지 기다리지 않고도 작업을 계속할 수 있습니다. IO 스레드는 종종 쓰기의 일부가 완료되기를 기다리는 것을 차단하므로 컨텍스트 전환이 자주 발생합니다.

마찬가지로 복잡한 계산을 수행 할 때 정기적 인 컨텍스트 전환을 허용하여 UI가 응답 상태를 유지하고 사용자가 프로그램이 중단되었다고 생각하지 않도록합니다.

여기서의 목표는 일반적으로 여러 스레드가 다른 CPU에서 실행되도록하는 것이 아닙니다. 대신, 백그라운드 백그라운드 작업이 실행되는 동안 UI를 업데이트하고 사용자에게 응답 할 수 있도록 장기 실행 백그라운드 작업과 UI간에 컨텍스트 전환이 발생하도록하는 데 관심이 있습니다. 일반적으로 UI는 CPU를 많이 사용하지 않으며 스레딩 프레임 워크 또는 OS는 일반적으로 동일한 CPU에서 실행하도록 결정합니다.

컨텍스트 전환 비용이 추가되어 실제로 전체 성능이 저하되지만 CPU 성능이 목표가 아니기 때문에 신경 쓰지 않습니다. 우리는 보통 우리가 필요로하는 것보다 더 많은 CPU 파워를 가지고 있다는 것을 알고 있으므로 멀티 스레딩에 대한 우리의 목표는 사용자의 시간을 낭비하지 않고 사용자를 위해 작업을 수행하는 것입니다.

"비동기"대안

"비동기 방식"은 단일 스레드 내에서 컨텍스트 전환을 활성화하여이 그림을 변경합니다. 이를 통해 모든 작업이 단일 CPU에서 실행될 수 있으며 스레드 생성 / 정리가 줄어들고 스레드 간의 실제 컨텍스트 전환이 줄어든 성능이 약간 향상 될 수 있습니다.

네트워크 리소스의 수신을 기다리는 새 스레드를 만드는 대신 (예 : 이미지 다운로드) 이미지를 사용할 수있게 하는 async메소드가 사용 await되며 그 동안 호출 메소드를 생성합니다.

여기서 주요 장점은 잠금 및 동기화를 전혀 사용하지 않기 때문에 교착 상태 방지와 같은 스레딩 문제에 대해 걱정할 필요가 없으며 프로그래머가 백그라운드 스레드를 설정하고 다시 가져 오는 작업이 약간 적다는 것입니다 UI를 안전하게 업데이트하기 위해 결과가 다시 표시되면 UI 스레드에서

기술적 인 세부 사항을 너무 깊이 조사하지는 않았지만 가끔 가벼운 CPU 활동으로 다운로드를 관리하면 별도의 스레드가 아닌 UI 이벤트 대기열의 작업과 같은 작업이됩니다. 다운로드가 완료되면 비동기 메소드가 해당 이벤트 큐에서 재개됩니다. 다시 말해, await"필요한 결과가 있는지 확인하고, 그렇지 않은 경우이 스레드의 작업 대기열에 다시 넣습니다"와 비슷한 것을 의미합니다.

이 방법은 CPU를 많이 사용하는 작업의 문제를 해결하지 못합니다. 대기 할 데이터가 없으므로 실제 백그라운드 작업자 스레드를 만들지 않고 상황 컨텍스트 전환을 수행 할 수 없습니다. 물론 비동기 방식을 널리 사용하는 프로그램에서 비동기 메소드를 사용하여 백그라운드 스레드를 시작하고 결과를 리턴하는 것이 여전히 편리 할 수 ​​있습니다.

성능을위한 멀티 스레딩

"성능"에 대해 이야기하기 때문에 멀티 스레딩을 성능 향상에 사용하는 방법에 대해서도 논의하고 싶습니다. 단일 스레드 비동기 방식으로는 불가능합니다.

실제로 단일 CPU에 충분한 CPU 전원이없고 성능에 멀티 스레딩을 사용하려는 경우 실제로 수행하기가 어려운 경우가 많습니다. 반면에 하나의 CPU가 충분한 처리 성능을 갖추지 못하면 합리적인 시간 내에 프로그램이 수행하려는 작업을 수행 할 수있는 유일한 솔루션이기도합니다.

사소한 병렬 처리

물론 멀티 스레딩을 통해 실제 속도를 높이는 것이 쉬운 경우도 있습니다 .

많은 독립적 인 계산 집약적 작업 (즉, 결과를 결정하기 위해 수행해야하는 계산과 관련하여 입력 및 출력 데이터가 매우 작은 작업)이 발생하는 경우 종종 다음과 같이 상당한 속도 향상을 얻을 수 있습니다. 스레드 풀 (사용 가능한 CPU 수에 따라 적절하게 크기 조정)을 작성하고 마스터 스레드가 작업을 분배하고 결과를 수집합니다.

성능을위한 실용적인 멀티 스레딩

나는 너무 많은 전문가가되기를 원치 않지만, 일반적으로 요즘 발생하는 성능에 대한 가장 실용적인 멀티 스레딩은 사소한 병렬 처리가있는 응용 프로그램의 위치를 ​​찾고 여러 스레드를 사용한다는 것입니다 혜택을 얻을 수 있습니다.

최적화와 마찬가지로 일반적으로 프로그램 성능을 프로파일 링하고 핫스팟을 식별 한 후에 최적화하는 것이 좋습니다.이 부분은 스레드없이 다른 스레드에서 실행해야한다고 임의로 결정하여 프로그램 속도를 늦추기 쉽습니다. 먼저 두 부분이 CPU 시간의 상당 부분을 차지하는지 확인합니다.

추가 스레드는 더 많은 설정 / 삭제 비용과 더 많은 컨텍스트 스위치 또는 더 많은 CPU 간 통신 비용을 의미합니다. 별도의 CPU를 사용하는 경우 이러한 비용을 보충하기에 충분한 작업을 수행하지 않고 응답 성 이유로 별도의 스레드가 필요하지 않은 경우 이점이 없어 속도가 느려집니다.

상호 종속성이 거의없고 프로그램 런타임의 상당 부분을 차지하는 작업을 찾으십시오.

상호 의존성이 없다면 사소한 병렬 처리의 경우 스레드로 쉽게 설정하고 이점을 누릴 수 있습니다.

상호 의존성이 제한된 작업을 찾을 수있어 정보 교환을위한 잠금 및 동기화로 인해 속도가 크게 느려지지 않으면 멀티 스레딩으로 인해 속도가 향상 될 수 있습니다. 필요할 때 동기화되지 않아 잘못된 결과가 발생합니다.

또는 멀티 스레딩을위한 더 일반적인 응용 프로그램 중 일부는 (일부) 미리 정해진 알고리즘의 속도 향상을 찾는 것이 아니라, 더 많은 예산을 책정하려고합니다. 게임 엔진을 작성하는 경우 AI가 프레임 속도 내에서 결정을 내릴 수 있도록 AI에 자체 CPU를 제공 할 수 있으면 AI에 더 큰 CPU주기 예산을 제공 할 수 있습니다.

그러나 스레드를 프로파일 링하고 어느 시점에서 비용을 보충하기에 충분한 작업을 수행하고 있는지 확인하십시오.

병렬 알고리즘

또한 여러 프로세서를 사용하여 속도를 높일 수있는 문제가 많이 있지만 CPU간에 간단히 분할하기에는 너무 모 놀리 식입니다.

CPU 간 통신 비용으로 인해 여러 CPU를 사용할 때의 이점을 제거하기가 쉽기 때문에 병렬 알고리즘은 사용 가능한 최상의 비 병렬 알고리즘과 관련하여 big-O 런타임에 대해 신중하게 분석해야합니다. 일반적으로 각 CPU에서 계산을 사용하는 것보다 CPU 간 통신이 적어야합니다 (큰 O 용어로).

현재로서는 여전히 복잡한 분석이 필요하기 때문에 여전히 학술 연구를위한 공간입니다. 부분적으로 사소한 병렬 처리가 매우 일반적이기 때문입니다. 부분적으로 아직 컴퓨터에 CPU 코어가 많지 않아서 하나의 CPU에서 합리적인 시간 프레임으로 해결할 수없는 모든 CPU를 사용하여 합리적인 시간 프레임으로 해결할 수 있습니다.


잘 생각 된 답을 얻으려면 +1하십시오. 그래도 마이크로 소프트의 제안은 액면가에주의를 기울여야한다. .NET은 동기식 우선 플랫폼이므로 에코 시스템은 동기식 솔루션 구축을 지원하는 더 나은 시설 / 문서를 제공하도록 편향되어 있습니다. Node.js와 같은 비동기 우선 플랫폼의 경우에는 그 반대입니다.
Evan Plaice

3

응용 프로그램이 응답하지 않고 느리고 불쾌합니다.

그리고 당신의 문제가 있습니다. 반응 형 UI는 성능이 뛰어난 응용 프로그램을 만들지 않습니다. 종종 반대입니다. 작업자 스레드가 작업을 수행하는 대신 UI 입력을 확인하는 데 많은 시간이 소요됩니다.

비동기식 접근 방식을 가진 '단지'인 한, 그것은 대부분의 환경에서 특정 사용 사례 에 맞게 조정되었지만 멀티 스레딩 입니다 . 다른 경우, 그 비동기는 항상 동시 적이 지 않은 코 루틴을 통해 수행됩니다.

솔직히 말하면 비동기 작업은 추론하기가 더 어려워 실제로 수동 접근 방식과 비교할 때 실제로 이점 (성능, 견고성, 유지 관리 가능성)을 제공하는 방식으로 사용하는 것입니다.


왜 ? 예를 들어 boost signal2 라이브러리에서 바나나를 찾은 것은 무엇입니까?
user1849534
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.