Node.js 앱 런타임 측정 문제
Node.js 애플리케이션의 런타임 측정은 성능 최적화의 중요한 측면입니다. 애플리케이션의 여러 부분을 실행하는 데 걸리는 시간을 측정하여 병목 현상을 식별하고 코드를 최적화하여 성능을 향상시킬 수 있습니다.
함수 실행 시간을 어떻게 측정할 수 있습니까?
node.js에서 시간을 측정하는 방법에는 여러 가지가 있습니다. 이를 수행하는 가장 높은 해상도의 방법은 process.hrtime() 또는 Perf_hooks 를 사용하는 것입니다 . 둘 다 시스템 시계에 의존하지 않기 때문에 Date 클래스를 사용하는 것보다 낫습니다. 그러나 여전히 그들 사이에는 몇 가지 차이점이 있습니다.
process.hrtime()은 나노초 수준의 해상도를 제공하고 perf_hooks는 마이크로초 수준의 해상도를 제공합니다. 이는 process.hrtime() 이 일반적으로 더 정확하지만 호출 비용이 더 많이 든다는 것을 의미합니다.
이제 다음 논리의 실행 시간을 측정하는 예를 들어 보겠습니다. 1000ms 후에 해결되는 비동기 논리(이것이 일부 HTTP 호출이라고 가정할 수 있음)가 있습니다. 이 약속을 이행하는 데 걸리는 시간을 측정하기 위해 perf_hooks를 사용할 것입니다.
const {performance} = require('perf_hooks');
const SOME_BIG_NUMBER = 4000000000;
async function asyncLogic(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const start = performance.now();
asyncLogic(1000).then(() => {
const time = performance.now() - start;
console.log(`Time taken: ${time}ms`);
});
let x = 0;
for (let i = 0; i < SOME_BIG_NUMBER; i++) {
x += Math.sqrt(SOME_BIG_NUMBER);
}
Time taken: 3948ms
이벤트 루프 및 이벤트 큐
JavaScript 런타임 모델은 이벤트 루프를 기반으로 하며, 코드 실행을 담당하고 비동기 로직을 사용할 수 있게 해주는 부분입니다.
JavaScript 코드를 실행하면 다음과 같은 일이 발생합니다.
- JavaScript는 프로그램에 나타나는 순서대로 모든 동기 코드를 실행합니다.
- 비동기 코드(예: asyncLogic 함수)가 있는 경우 JavaScript는 코드를 즉시 실행하는 대신 이벤트 대기열 에 추가합니다.
- 이벤트 루프는 실행 대기 중인 이벤트가 있는지 확인하기 위해 이벤트 큐를 지속적으로 확인합니다 .
- 이벤트를 실행할 준비가 되면 이벤트 루프는 이벤트 큐에서 실행할 수 있는 호출 스택 으로 이벤트를 이동합니다.
- 이벤트가 실행되면 이벤트 루프가 더 많은 이벤트에 대한 이벤트 대기열을 확인하기 위해 돌아가기 전에 추가 동기 코드가 실행됩니다.
그래서 우리 예제에서 시간 측정이 4000ms인 이유입니다.
- 이벤트 큐에 추가된 asyncLogic 함수를 실행합니다.
- 그런 다음 실행하는 데 약 4000ms가 걸리는 동기식 논리를 실행합니다.
- 이벤트 루프는 asyncLogic() 콜백을 이벤트 큐에서 호출 스택으로 이동합니다.
- 그런 다음 콜백 함수를 실행하고 종료 시간을 계산합니다.
이벤트 루프 지연
따라서 애플리케이션 성능을 모니터링할 때 이벤트 루프의 지연, 이 콜백이 실행될 때까지 이벤트 큐에 새 콜백을 추가하는 사이에 이벤트 루프에 걸리는 시간과 같이 주의해야 하는 중요한 메트릭이 있습니다.
다음 코드를 사용하여 이벤트 루프 지연을 측정할 수 있는 루프를 쉽게 만들 수 있습니다.
function measureEventLoopLag() {
let lastLoopTime = performance.now();
setTimeout(() => {
const delay = performance.now() - lastLoopTime - 1000;
lastLoopTime = performance.now();
console.log(`Event Loop lag is: ${delay} ms`)
measureEventLoopLag();
}, 1000)
}
런타임 개선
코드에 4000ms가 걸리는 HTTP 호출이 있고 실행하는 데 약 4000ms가 걸리는 느린 루프도 있다고 가정해 보겠습니다. 이전 예제에서 본 것처럼 이 런타임을 측정하면 전체 코드를 실행하는 데 4000ms가 걸린다는 것을 알 수 있습니다.
코드를 리팩토링한 후 느린 루프 실행 시간을 50% 줄입니다. 코드를 다시 실행할 때 측정에 따른 전체 런타임은 실제로 변경되지 않았습니다. 비동기 흐름이 완료될 때까지 기다렸기 때문에 전체 흐름을 완료하는 데 여전히 약 4000ms를 기다렸습니다. 리팩토링이 실제로 도움이 되지 않은 것 같습니다.
실제로 CPU 측면에서 살펴보면 큰 차이를 만들었습니다. HTTP 호출은 CPU를 많이 사용하지 않으며 대부분 응답을 기다립니다. 따라서 대부분의 경우 CPU는 느린 루프를 실행하는 데 사용됩니다. 코드를 리팩토링하고 50%로 줄였을 때 전체 시간은 변경되지 않았지만 CPU에 상당한 영향을 미쳤습니다.
결론
우리의 예에서는 모니터링할 로직이 매우 작았습니다. 실행할 로직이 많은 복잡한 시스템이 있으면 상황이 정말 복잡해집니다. 이 경우 로직의 작은 부분을 측정하여 성능 저하를 더 잘 모니터링해야 합니다.
애플리케이션 실행 시간을 모니터링할 때 유의해야 할 두 가지 중요한 사항이 있습니다.
- 비동기 함수 시간 측정은 동기 코드(이벤트 루프 지연)의 영향을 받을 수 있습니다.
- 비동기 및 동기 로직이 모두 있는 측정 함수는 런타임 개선(또는 감소)을 숨길 수 있습니다.
- ~30ms보다 높은 이벤트 루프 지연은 애플리케이션에 정말 좋지 않습니다.

![연결된 목록이란 무엇입니까? [1 부]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































