JavaScript의 런타임 모델은 코드의 실행, 이벤트의 수집과 처리, 큐에 대기중인 하위 작업을 처리하는 이벤트 루프에 기반하고 있습니다.
스택
함수의 호출들은 '프레임' 스택을 형성합니다. 인수와 지역 변수는 스택 바깥에 저장되므로 바깥 함수가 반환한 후에도 계속 존재할 수 있습니다. 클로저 환경이 중첩 함수에서 지역 변수에 접근할 수 있는 이유가 이것 입니다.
힙
객체는 힙에 할당됩니다. 힙은 단순히 메모리의 큰(그리고 대부분 구조화되지 않은) 영역을 지칭하는 용어입니다.
큐
JavaScript 런타임은 메시지 큐, 즉 처리할 메시지의 대기열을 사용합니다. 각각의 메시지에는 메시지를 처리하기 위한 함수가 연결돼있습니다.
런타임은 대기열에서 가장 오래된 메시지부터 큐에서 꺼내 처리하기 시작합니다. 이를 위해 꺼낸 메시지를 매개변수로, 메시지에 연결된 함수를 호출합니다. 다른 함수와 마찬가지로 호출로 인한 새로운 스택 프레임도 생성됩니다.
함수 처리는 스택이 다시 텅 빌 때까지 계속됩니다. 그 후, 큐에 메시지가 남아 있으면 같은 방법으로 처리를 계속 진행합니다.
이벤트 루프는 기능을 구현할 때 사용하는 방식에서 그 이름을 얻었습니다.
while(queue.waitForMessage()){
queue.processNextMessage();
}
queue.waitForMessage() 함수는 현재 처리할 수 있는 메시지가 존재하지 않으면 새로운 메시지가 도착할 때까지 동기적으로 대기합니다.
이벤트 루프는 Call Stack이 비어있을 때, Queue의 작업을 stack으로 옮겨 줍니다.
setTimeout 함수는 이벤트루프를 확인하기 좋은 예제입니다. 매개변수로 큐에 추가할 콜백함수(메시지)와 시간 값을 갖습니다. 아래 예제에서 두번째 매개 변수 값을 주지 않으면 0초가 기본 값이기 때문에 바로 실행될 것 같지만, 결과는 즉시 실행되지 않는 것을 볼 수 있습니다.
(function() {
console.log('시작');
setTimeout(function cb() {
console.log('콜백 1: 콜백 메시지');
}); // has a default time value of 0
console.log('평범한 메시지');
setTimeout(function cb1() {
console.log('콜백 2: 콜백 메시지');
}, 0);
console.log('종료');
})();
// "시작"
// "평범한 메시지"
// "종료"
// "콜백 1: 콜백 메시지"
// "콜백 2: 콜백 메시지"
콜백함수로 온 메시지는 큐에 들어가기 때문에, 호출된 함수들이 스택에서 모두 완료가 되면 이후에 큐에 있는 이벤트를 처리하게 되는 것입니다.
다른 언어와 달리 JavaScript는 blocking 연산을 하지 않습니다. Non-Blocking은 이벤트 루프 모델의 흥미로운 특징으로 대부분의 입출력 처리가 이벤트와 콜백을 통해 수행되므로 애플리케이션이 IndexedDB 질의나 XHR 요청의 반환을 대기 중이더라도 여전히 사용자 입력 등 다른 것들을 처리할 수 있는 것입니다.
이는 자바스크립트가 싱글쓰레드임에도 불구하고 다중 작업을 처리할 수 있는 방식을 설명할 수 있습니다.
그럼 왜 자바스크립트는 싱글쓰레드일까요?
전통적으로 JavaScript는 짧고 빠르게 실행되는 코드를 위한 것이었습니다. 복잡한 작업을 수행하면서 오랜 시간 브라우저에서 실행되는 구조가 아닌 서버에서 주로 계산하여 보여주기 위함이었습니다.
더군다나 멀티쓰레드를 지원하는 언어의 경우 동기화(synchronized) 문제에 대해 많은 신경을 쓰고 있는데, JavaScript와 같이 변수의 형태와 추가가 자유로운 언어에서 동기화를 지원하기 까다로울 뿐더러, 이미 웹 상에서 벌어지는 경우의 수는 런타임 모델로 다중 작업 처리를 해결하였기 때문에 필요성을 느끼지 못해 싱글쓰레드를 지원하는 것입니다.
물론, 완전 방법이 없는 것은 아닙니다.
- 동기적을 실행되는 복잡한 계산은 setImmediate(callback)을 사용하여, 각 작업을 chunk 하는 방식이 있고,
- fork를 사용한 멀티 프로세스 방식을 채용할 수도 있습니다.
하지만 위 두 방식 보다 WebAPI에서는 Web Workers API를 제공합니다.
주 실행 스레드와 분리된 별도의 백그라운드 스레드에서 실행할 수 있는 방식을 통해 웹 애플리케이션에서 암호화, 압축/암축해제, 이미지조작, 컴퓨터비전(얼굴인식) 등의 수행을 도울 수 있습니다.
[참조 : https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API]
ML 모델을 사용자들에게 서빙하는 방식에는 2가지 방식이 있다고 생각합니다.
물론 최근에는 Large-Model이 큰 성능을 내고 있기 때문에 서버와의 통신을 통해서 결과값을 받아 보여주는 방식을 주로 사용하고 있지만, 점점 Client기기의 성능이 향상되는 만큼 경량화된 ML모델을 client에서 직접 띄워서 사용하는 방식도 사용될 가능성이 높아진다고 생각합니다.
그럴 때 더욱이 JavaScript의 런타임 모델은 고민해 볼 수 있는 좋은 내용인 것 같습니다.
[출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop]
[참조 : https://chanyeong.com/blog/post/44]
[참조 : https://yceffort.kr/2021/04/nodejs-multithreading-worker-threads]
[참조 : https://stackoverflow.com/questions/39879/why-doesnt-javascript-support-multithreading]
'Programming Language > JavaScript' 카테고리의 다른 글
JavaScript - Promise (프로미스) (0) | 2022.07.11 |
---|---|
JavaScript - 가비지 컬렉션(Garbage Collection) (0) | 2022.07.08 |
JavaScript - 클로저(Closure) (0) | 2022.07.07 |
JavaScript - 변수(Variable) (0) | 2022.07.07 |
JavaScript - 콜백 함수(Callback) (0) | 2022.07.06 |