성능업(ScaleUP) VS 확장(ScaleOUT)
ScaleUP
노드1의 장비 성능을 높이기위해 사용되는 여러가지 행위임
최종적으로 하나의 장비에서 많이 처리하고, 전체 장비의 개수를 줄이는 데 목적이 있으며,
장비의 고효율 IO처리, 효율적메모리관리및 CPU사용 최적화등의 튜닝 기술등이 이용될수 있으며
외적으로는 고성능 장비 도입 및 효율적 IO사용 분리 역시 같은 의미가 될수 있으며
개선및 도입에 따른 측정도 중요한 사안입니다.
ScaleOut
측정된 노드1의 성능을 베이스로, 대용량처리를 위해 노드를 수평적으로 확장해 나갈수 있는 행위이며
다른 서비스 의존성으로인한 병목현상으로 실제는 확장수만큼 성능이 올라가지는 않습니다.
이러한 손실을 줄이고 확장을 통한 성능향상을 위해 여러가지 클러스터및 분산저장 기술등이 이용됩니다.
외적으로는 장비를 병렬적으로 늘리고 효율적으로 관리하는 방법도 포함될수 있습니다.
병렬적으로 늘린 장비에 대한 장애대비,관리,확장대비 성능측정도 중요한 사안입니다.
동시성(Concurrency) VS 병렬처리(Parallelism)
동일시간(성능적차이를 제거하고)에 여러가지 작업을 동시에 처리한다란 점은 동일하지만
작동방식에는 약간의 차이가 있습니다.
쉬운 이해를 위해, 네트워크 모델을 제거하고 단일 PC에서의 차이를 살펴보겠습니다.
concurrency
동시성의 경우, 본질적으로 작업이 ,동일시간(나노세컨)에 동시에 실행되지는 않습니다. 작업이 작은 단위로 조각이 난상태로
task 스위칭이 일어나면서 일시정지, 실행을 반복하게 됩니다.
일반적으로 Task관리자에의해 상태를 복구를 반복 하면서 동시에 처리되는것처럼 보일려고 합니다.
하나의 Task에대한 성능을 높이기보다, 전체적인 총량에 대한 처리의 효율을 높이는데 의미가 있습니다.
Thread에 비해 Task는 충분히 많이 만들수 있으며 멀티 스레드에서 Thread Context 스위칭보다 효율적이다라고 하고있습니다.
Task task1; Task task2; Task task3; //작업 3개가 동시에 시작합니다. task1.Start(); task2.Start(); task3.Start();
물론 이 코드는, 실행 최적화에의해 다중코어 병렬처리가 될수도 있습니다. Task VS Thread : Task는 Thread를 효율적으로 사용하는 좀더 추상적 개체로
OS에따라 스케쥴 방식이 틀리고 또한 프레임워크따라 비동기처리를 위한
스레드 관리방식이 틀리기때문에
이 부분은 깊게 들어가지 않고, Actor가 어떻게 스레드수를 고려하여 튜닝가능한지?
actor-dispatcher 편에서 에서 더 자세하게 설명예정입니다.
Parallelism
병렬처리의 경우 작업을 명시적으로 여러개의 물리(코어)적 연산 장치에 각각 Join시켜 동시(병렬)에 처리하는 방식입니다.
가용한(풍부한) 자원을 바탕으로, 여러가지 작업을 한꺼번에 수행할수가 있습니다. 물론 단순하게 스레드를 N개로 생성하여
각 Task를 하나씩 작동시키는 방법도 의미적으로 병렬처리라고 할수있습니다.
자원적으로 생성할수 있는 스레드수는 제한적입니다.
//병렬처리를 위한 멀티 코어 프로그래밍 예 Thread[] threads = new Thread[NumOfThread]; Process currentProcess = Process.GetCurrentProcess(); foreach (ProcessThread processThread in currentProcess.Threads) { processThread.ProcessorAffinity = currentProcess.ProcessorAffinity; } //물리적단위인 Core(process) 에 Task를 명시적으로 걸고 동시에 시작해버립니다. for (int i = 0; i < NumOfThread; i++) { threads[i].Join(); } }
주어진 시간에 어떠한 작업을 스케쥴링에의해 블락킹없이 동시에 처리하느냐(동시성)?
자원을 바탕으로 한꺼번에 처리하느냐(병렬)? 의 문제는 CPU에만 국한된 문제가 아니며
어느방식을 사용하던, 실제로는 꼭 한가지 방식 한정적으로 작동 되지 않습니다.
추가정보
분산처리모델을 적용 시키면 ( 동시성과 병렬처리의 조합)
비용이 큰 연산단위를 더 작은단위로 잘게(Slice)나눠 빠른 분산처리를 통해 작업총량에 걸리는 시간을 어떻게 줄일까?
Akka에서는 StrameSinkAndFlows+Cluster 조합으로 이러한 문제를 풀려고 하고있습니다.
1시간마다 배치를 돌려 밀린일을 한꺼번에한다던지? 이러한 기능은 AKKA와 맞지 않는 Job의 형태입니다.
Synchronous(동기)
메서드 호출후 값이 반환되거나, 예외가 throw될때까지 호출자가 진행할수 없는 경우
Asynchronous(비동기)
메서드 호출후 호출자는 몇가지 메카니즘(완료및 진행 등록된 콜백)을 통해 알수있으며
진행이가능 합니다. 동기구현을 목적으로 블로킹(Result)기능을 사용할수 있지만
일반적으로 비동기 API로 설계하는것은 시스템이 진보할수 있음을 보장함으로 선호됩니다.
액터는 본질적으로 비동기입니다.
Non-blocking vs. Blocking
한 스레드의 지연이 다른 스레드를 무기한 지연 시킬수 있을때 블록킹되었다라고 표현함
예를 들면 상호배제(mutual exclusion)을 사용하여 하나의 스레드가 독점할시 자원을 공유하고있는
다른 스레드가 진행될수 없는경우
논블록킹은 이와 반대되는 개념으로, 어떠한 스레드도 다른것들을 무기한 연기할수 없다란것을 의미합니다.
블록킹 작업포함시 전반적인 작업진행이 쉽게 보장이 되지 않기때문에, 논블록킹 작업방식으로의 설계가 추천됩니다.
Deadlock vs. Starvation vs. Live-lock
DeadLock
DeadLock(교착상태)는 여러 Task가 어떠한 특정 상태를 기다린후 진행할수 있는 구조에서
어떠한 Task가 특정상태에 도달하지 못할때 하위의 모든 시스템이 멈추는 경우입니다.
Starvation
Starvation(빈곤)은 계속 진행이되며 , 우선순위가 높은 Task가 어렵게 진행이되지만
항상 높은 우선순위의 Task를 선택하는 순진한 스케쥴링 알고리즘때문에 낮은 순위의 Task가
끝나지 않거나 진입을 못하는 경우입니다.
LiveLock
LiveLock은 Task중 누구도 진행되지 않는 경우이며, 교착상태와 유사합니다. 서로 진행되기를
기다리면서 Task의 상태가 지속적으로 변화는 상황으로 불행한 경우 획득하지 않고 항상 상대방에게
양보하는 경우가 발생하여 누구도 실행되지 않는 케이스입니다.
Race Condition
우리가 정한 이벤트 세트의 순서에대한 가정이 , 외부의 결정적이지 않은 효과임에도 불구하고
위반되어 오동작인 상태가 되는것을 레이스(경쟁) 조건이라고 합니다.
발생하는 케이스는 스레드가 공유 가능상태를 가지고 경쟁상태가 자주발생하면서 스레드의 작업순서가
바뀌면서 예기치 않는 동작이 발생하는것입니다. 일반적으로 공유상태는 경쟁조건을 가질필요가 없으며
순서를 보정하지 않습니다. 패킷으로 예를 들면 P1,P2를 차례로 서버에 보냈지만 패킷이 다른 네트워크의
경로 를 통해 P2를 먼저받고 P1을 나중에 수신받을수 있으며 순서에대한 정보처리없이 서버가 이를 처리하려한다면
패킷의 의미에 따라 Race Condion(경쟁조건)이 발생할수가 있습니다. 이것은 멀티스레드에서도 발생할수 있는
문제이며 동시에 공유자원을 접근해서 발생하는 문제도 포함됩니다.
이러한 문제는 여러가지 액터가 메시지를 동시에 보내고
어떠한 한가지 액터가 그 메시지를 받아서 처리할때 메시지 순서에대한 보증을
어떠한 근거로 할것인가? 의 고민에 빠지게됩니다.
메시지 전달 보증(Message Delivery Reliability)에 추가적으로 언급하겠습니다.
Non-blocking Guarantees (Progress Conditions)
이전 섹션에서 설명된것처럼 블로킹은 여러가지 이유로 바람직하지 않습니다.
이것은 교착상태의 위험과 시스템 처리량 감소를 야기 시킬수 있습니다.
다음은 다양한 강도(차단이 강하거나 제한) 를 지닌 차단 특성에대해 논의를 합니다.
Wait-freedom -기다림이 없으나 호출수 한정
모든 호출이 한정된 수의 단계로 끝나도록 보장되는 경우 메소드는 대기하지 않아도됩니다.
wait-free 메소드는 결코 블로킹되지 않으며 교착상태가 발생하지 않으며
참여자는 제한된 수의 호출후에 진행할수 있으므로 궁핍(기아)현상이 발생하지 않습니다.
Lock-freedom -Lock이 없음
Lock이 없기때문에 교착상태가 발생하지 않지만
호출이 결국 완료되는것을 보장하지 않기때문에 궁핍(기아)현상을 보장하기에는 충분하지 않습니다.
Obstruction-freedom -기다림/Lock등 방해요소가없음
메소드가 격리되어 작동하다가, 특정 시점을 (Write중)
obstacle-free(방해가 없는상태)라고 하며 ,다른 스레드가 접근시
아무런 조취를 하지 않기때문에 진행작업이 중단될수가 있습니다. ( access violation at address )
낙관성 동시성 제어(OCC-Optimistic concurrency control)는
발생가능성이 거의 없을것으로 예상하고(충돌로 인해 문제 발생시,스케쥴을 조정해버려서 해결)
일정에따라 다시 시도하여 작업 성공으로 간주합니다.
빠른성능을 위해 이러한 문제를 인지하고 작동되는 케이스보다,
멀티스레드 프로그래밍시 유의할점에대해 인식없이 작동 시키고
문제가 발생하면 후 조취로 무분별한 Lock / 전역 예외등 주로 개발코드의 품질이 나빠지는 케이스로 연결됩니다.
Actor는 속성공유를 기본으로 하지 않는 개발패턴이여서 동시성 프로그래밍 흐름에 부적절한 키워드인 Lock을 사용하지 않아도됩니다.
스레드 처리 모델중 하나인 세마포어를 통해 여러개의 태스크를 안정적이고 빠른 코드로 실행하는 코드를 작성해보고 Actor가 왜 복잡한 스레드 모델이 필요없는지?
실습코드 작성예정입니다.