[FP] - Coroutine과 Lazy Evaluation
함수형 프로그래밍을 접하다 보면 Lazy Evaluation(지연 평가)
를 자주 마주치게 됩니다.
지연평가는 함수형 프로그래밍을 지원하는 Lodash
,Rxjs
, Fxjs
와 같은 라이브러리에서도 지원하는 기능입니다.
지연 평가는 말 그대로 계산이 필요한 시점까지 계산을 미루는 것
입니다.
엄격한 평가
지연평가를 이해하기위해선 기본 동작인 엄격한 평가가 무엇인지 알고 비교하는 편이 좋습니다.
엄격한 평가는 각각 함수의 계산이 모두 종료되어야
다음 단계를 수행합니다.
즉, 예시에서 map 6번, filter 6번, slice 2번 총 14번 함수가 실행됩니다. 코드상으로 문제는 없지만 좀 더 효율적으로 생각해보자면 먼저 filter를 통해 필터링을 하고 나머지를 1씩 더하면 쓸데없는 연산없이 효율적인 작업이 가능해 보입니다.
지연 평가
지연평가는 엄격한 평가와는 다르게 흐름이 위에서 아래로 흐릅니다.
구현은 나중에!
지연 평가를 지원하는 코드는 내부 구현체를 코루틴을 이용해 구현합니다. 이는 다음 글에서 자세하게 다루겠습니다.
해당 코드에서 모든 원소에 대해 계산을 한 뒤 다음 함수로 넘기는 것이 아닌 원소 한개씩 계산합니다.
지연 평가를 이용하면 표와 같이 3까지 계산후 원하는 결과값을 얻었으므로 원소 4, 5 에 대한 계산을 하지 않는 것을 확인할 수 있습니다.
이를 예제코드에서 배열의 원소 개수를 1,000,000개로 수정한 뒤 적용한 성능을 비교해보겠습니다.
지연평가가 압도적인 성능을 나타냄을 알 수 있습니다.
지연평가를 구현하기 위에서는 제너레이터를 이해해야 하는데 이를 위해 코루틴이 무엇인지 알면 더 쉽게 이해할 수 있습니다.
Coroutine(코루틴)
코루틴은 프로그래밍에서 독립적으로 실행될 수 있는 서브루틴의 한 형태입니다.
서브루틴은 그럼 무엇일까요? 서브루틴은 프로그래밍에서 여러번 호출될 수 있는 독립적인 코드 블록을 말합니다. 예컨대 함수는 서브루틴이라고 말할 수 있죠.
그렇다면 코루틴은 어떤 서브루틴일까요? 코루틴은 일반적인 서브루틴과 다르게 실행 중간에 일시 중단하고 나중에 이어서 실행할 수 있는 능력을 가지고 있습니다. 이러한 특성으로 병행성(Concurrency, 동시성)을 구현하는 데 매우 유용하게 사용됩니다.
병행성과, 병렬성
- 병행성(Concurrency, 동시성) : 논리적으로 병렬 작업이 실행되는 것 처럼 보이는 것 (코루틴)
- 병렬성(Parallelism) : 물리적으로 병렬로 작업이 실행되는 것 (쓰레드)
그림으로 보면 다음과 같습니다.
서브루틴은 실행 후 반환문을 통해 원래 위치로 돌아옵니다.
반면 코루틴은 반환문이 없더라도 임의 지점(yield)에서 실행 중 동작을 중단하고 이후 해당 지점에서부터 실행을 재개합니다.
이를 통해 코루틴의 특징을 정리해보자면 다음과 같습니다.
GPT
- 일시 중단과 재개: 코루틴은 실행 중 언제든지 일시 중단하고, 이후에 그 지점부터 다시 실행을 재개할 수 있습니다. 이는 코루틴이 자신의 실행 상태를 저장하고 복원할 수 있기 때문에 가능합니다.
- 동시성 구현: 여러 코루틴들이 동시에 실행되면서, 하나의 스레드에서 시간을 적절히 분배하여 병행성을 구현할 수 있습니다. 이는 스레드를 생성하고 관리하는 비용을 줄이면서도 병행성을 달성할 수 있는 장점을 제공합니다.
- 비동기 프로그래밍: 코루틴은 비동기 코드를 보다 직관적이고 구조화된 방식으로 작성할 수 있도록 돕습니다. 특히 이벤트 기반 프로그래밍이나 네트워크 작업과 같은 경우에 매우 유용하게 쓰입니다.
- 메모리 사용: 스레드와 달리 코루틴은 상대적으로 작은 메모리를 사용합니다. 각 코루틴은 일시 중단된 상태에서도 자신의 상태를 보존하기 위한 메모리만 사용하므로, 많은 수의 코루틴을 생성해도 시스템에 부하를 거의 주지 않습니다.
자바스크립트를 통해 서버와 통신을 해보셨다면 여러분도 이미 코루틴을 사용해보신 경험이 있을 겁니다. 없다고요? 아래 코드를 보시죠
어떤가요? 사실 비동기 프로그래밍을 위해 사용하는 async/await
가 코루틴입니다.
async
함수는 await
키워드를 통해 Promise
가 처리될 때까지 함수의 실행을 일시 중단하고 처리 후 다시 실행합니다. 또한 await는 기본적으로 Promise를 기다리지만, 비동기 작업을 수행하는 다른 async함수나 Promise객체를 기다릴 수 있습니다.
자바스크립트에서는 제너레이터 함수를 이용해 코루틴을 구현할 수 있습니다. 제너레이터 함수를 통해 코루틴, 지연로딩을 구현하는 것은 다음 글에서 해보도록 하겠습니다.