Skip to content

Chapter 08, Designing concurrent code

danny song edited this page Nov 13, 2015 · 3 revisions

Chapter 08, Designing concurrent code

도입

  • 이전 장에서는 C++11으로 동시성 코드를 구현하는 것에 집중했고 특히 6, 7장에서는 다수의 쓰레드에서 동시 접근 안전한 기본적인 데이타 구조 디자인을 살펴봤다. 이제 좀더 넓 문맥에서 동시성 디자인을 하는 방법을 알아보자. 동시성 코드는 다른 프로그래밍 처럼 디자인에 대해서 주의 깊은 관심이 필요하지만, 순차적 코드보다 더 많은 요소를 생각해 야 한다. 예들들어, 데이타 공유 그리고 다수 쓰레드에서 데이타 접근시 데이타 싱크로나이즈, 쓰레드간 싱크로나이즈 등등 이장에서는 다음을 알아본다.
    • 얼마나 많은 쓰레드를 사용하는지에 대한 high level관점(그리고 근본적인)에서의 고려
    • 어떤 코드가 어떤 쓰레드에서 실행되는지
    • 동시성 다지안이 어떻게 코드 명확화에 영향을 끼치는지
    • 최적의 성능을 위해 공유 데이타를 어떻게 구조화 하는지 low level관점에서의 고려

8.1 쓰레드 사이에 작업(Work)을 나누는 기술

  • 집을 짓는 다고 생각해보자
    • 무엇을 해야 할까?
      • 집 터 기초를 다져야 하고 벽을 쌓아야 하며 배관을 설치도 하고 선들도 연결하고 등등...
      • 방법
        1. 나 혼자 배워서 다 한다.
        • 시간도 오래 걸리고 이것 저것 할게 많아 문맥 전환이 빈번하다!
        1. 사람을 고용한다.
        2. 능숙하진 않지만 모든 기술을 조금씩 알고 있는 사람들 고용 - 여전히 문맥 전환은 존재하지만 혼자하는거보단 빠르다.
        3. 각 분야의 전문가를 고용한다. - 각 분야의 전문가가 모여 작업을 하기 때문에 집중도가 높아졌고 문맥 전환 비용이 낮다.
          그래서 효율 및 속도는 전체적으로 이전보다 높아졌다. 하지만 각 작업이 특정 전문가에만
          의존하기때문에 특정 작업이 없을 경우 그 전문가는 할수 있는게 없다.(go to idle)
          - 아마도 특정 분야에 따라 다른 수의 전문가를 고용할 수 있다. 예를들어, 집을 짓는데 전기기사보단 벽돌공이 더 많이 필요하다. 만약 한번에 많은 수의 집을 짓는다면 쉬고 있는 배관공에게 더 많은 일을 줄 수 있다. 만약 할 일이 없는 전문가에게 보수를 지불할 필요가 없다면 같은 숫자의 일할 수 있는 인력으로 더 큰 팀을 꾸릴 수 있다.
      • 결국엔 쓰레드를 다루는것도 같은 이치다!
        1. 얼마나 많은 쓰레드를 써야하고, 어떤 작업을 해야 하고, generalist써야 할지 specialist를 써야 할지..
        2. 이런 것을 생각하면서 동시성 프로그램 디자인을 디자인해야 하며 디자인에 따라 성능 뿐만아니라 코드 명료성에 지대한 영향을 끼친다.

8.1.1 처리를 시작하기 전에 쓰레드 사이에 데이터를 나누기

  • std::for_each로 간단한 병렬처리를 알아보자.
    • 처리 쓰레드에게 각 앨리먼트 연산을 맡길 수 있다. 최상의 성능을 위해 데이타 구조체를 어떻게 나누는가는 그 구조체 자체의 detail에 의존적이다.
    • 가장 쉽게는 첫번째 N개 앨리먼트를 첫번재 쓰레드에, 다음 N개를 두번째 이런식으로 처리 책임이 각각 쓰레드에서 맡겨지고 쓰레드 사이에 communcation없이 독립적으로 수행한다.
      • 이런 접근법은 Message Passing Interface ( MPI ), OpenMP와 비슷하다.
        • 작업이 작은 작업 단위로 나눠지고 각 worker 쓰레드에 assign된다. 결과는 최종 Reduction단계를 거쳐 완성된다.
          • secion 2.4에서 accumulate 예제의 접근법이며 for_each는 reduction 단계를 필요없다.
    • reduction 단계는 중요하다. listing 2.8같은 초보적인 구현은 reduction 단계를 마지막에 수행할 것이다. 그러나 이 단계 조차도 병렬화될 수 있다. 예들들어, 사실 accumulate 자체가 reduction 연산이여서, 쓰레드의 수가 하나의 쓰레드에서 처리 하는 최소 아이템 수보다 크면 재귀 호출로 구현할 수 있다.
    • 그리고 매번 새로운 쓰레드를 생성하는것 대신 각 쓰레드가 나눠진 작업을 완료할 때마다 worker 쓰레드가 reduction 작업을 수행할 수도 있다.
    • 하지만 이런 디자인도 데이타의 성격에 따라 적용될 수도 안될 수도 있음을 유의하자. 즉 데이타 그리고 작업의 성격에따라 다자인 되어야 한다.
      • 예를 들어 데이타가 작업 전에 독립적으로 나눠질 수 없고 데이타 처리 중에 또는 후에 가능하다면 어떻게 할 것인가?
      • Quick sort

8.1.2 데이터를 재귀적으로 나누기

8.1.3 테스크 타입에 따라 작업을 나누기