[자바스크립트] 시간 차이 측정하기
요약
기본 원리: 함수를 이용, 시각을 2번 측정한 후 뒷 시간에서 앞 시간을 뺀다.
함수명 | 반환 타입 | 지원 정밀도 | 지원 환경 |
Date.now( ) | number ( 정수 ) | ms | 노드, 웹 |
process.hrtime.bigint( ) | bigint | ns | 노드 |
performance.now( ) | number ( float ) |
ms~ns(유사)
|
노드, 웹
|
Date 객체
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Date
Date - JavaScript | MDN
JavaScript Date 객체는 시간의 한 점을 플랫폼에 종속되지 않는 형태로 나타냅니다. Date 객체는 1970년 1월 1일 UTC(협정 세계시) 자정과의 시간 차이를 밀리초로 나타내는 정수 값을 담습니다.
developer.mozilla.org
노드에서 현재 시각을 알고 싶은 경우 Date 내장 객체를 이용한다. 이때 Date 객체는 UTC 기준 1970년 1월 1일 자정부터 대상 시각까지 지난 밀리초를 정수 값으로 가진다.
const date = new Date();
console.log(date.toLocaleString('ko-kr'));
console.log(date.valueOf());
사용 방법
작업에 걸린 시간을 측정하는 경우 밀리초를 반환하는 성질을 이용한다. 작업 전후에 시각을 측정한 후 두 값을 빼면 작업 간 걸린 시간을 밀리초 단위로 알 수 있다. 이와 같이 단순 숫자 계산만 필요한 경우 Date.now( ) 메서드를 이용한다.
const start = Date.now();
console.log("before wait: ", start);
await new Promise((resolve) => {
setTimeout(() => {
const end = Date.now();
console.log("after wait: ", end);
console.log(`time gaps: ${end - start}ms`);
}, 500);
});
Date 객체로 시간 차이를 측정할 때의 문제점은 Date 객체는 시간을 ms 단위로밖에 표현하지 못한다는 점이다. Date 객체는 명세상 밀리초 단위로 시간을 표현하므로, 그보다 더 낮은 시간 단위(마이크로초, 나노초 등)의 정밀도가 요구되는 경우에는 사용할 수 없다.
process.hrtime
https://nodejs.org/dist/latest-v18.x/docs/api/process.html#processhrtimebigint
Process | Node.js v18.13.0 Documentation
Process# Source Code: lib/process.js The process object provides information about, and control over, the current Node.js process. import process from 'node:process';const process = require('node:process'); Process events# The process object is an instance
nodejs.org
노드 내장 모듈인 process에서는 hrtime이라는 시간 측정 목적 함수를 제공한다. hrtime.bigint() 함수는 과거 임의 시간에 대해 상대적인 경과 시간을 나노초 단위로 반환한다. 숫자의 타입은 bigint이므로 직접 연산이 가능한 장점이 있다.
사용 방법
import { hrtime } from 'process';
const start = hrtime.bigint();
console.log("before wait: ", start);
await new Promise((resolve) => {
setTimeout(() => {
const end = hrtime.bigint();
console.log("after wait: ", end);
console.log(`time gaps: ${end - start}ns
${(end - start) / BigInt(1e3)}μs
${(end - start) / BigInt(1e6)}ms`);
}, 500);
});
Date 객체보다 더 정밀한 표현이 가능하기 때문에 작업에 소요되는 시간을 측정할 때는 hrtime을 사용하는 편이 더 좋다. 단, process 모듈은 노드 환경에만 존재하므로 웹 환경에서는 사용할 수 없다.
performance.now
https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
performance.now() - Web APIs | MDN
The performance.now() method returns a DOMHighResTimeStamp, measured in milliseconds.
developer.mozilla.org
performance는 환경의 성능을 측정할 때 사용되는 객체로, 현재 노드 환경과 웹 환경(window) 모두에 구현되어 있다. 그중 now라는 함수는 특정 시점을 기준으로 경과한 시간을 부동 소수점의 밀리초 ( 소수점 단위를 통해 마이크로초 표현 ) 단위로 표현할 수 있다. 따라서 Date 객체보다 정밀한 숫자 표현이 가능해진다.
ms 이하 정밀도가 소수점 아래자리로 표현되는 이유
특징적인 부분은 마이크로초 부분을 소수점 자리로 제공하여 의도적으로 타이머의 정밀도를 줄인다는 점이다. MDN 문서에 따르면 해당 값을 이용하여 스펙터 버그를 통한 부채널 공격을 발생시킬 수 있어 의도적으로 숫자의 정밀도를 낮춘다고 한다. 실제로 스펙터 버그의 취약점을 이용한 부채널 공격을 시도하는 프로젝트인 spook.js가 존재한다.
Spook.js
eTLD+1 is an acronym for effective top-level domain plus one. A top-level domain (TLD) is the part of a domain name without dots, such as "com", "org", or "edu". Users can register domain names under a TLD, like "example.com". On the other hand, an effecti
www.spookjs.com
사용 방법
사용 방법은 위에서 설명한 방식들과 동일하게 두 시각을 측정한 후 숫자를 빼면 된다.
let start = performance.now();
setTimeout(() => {
const end = performance.now();
console.log("after wait: ", end);
console.log(`time gaps: ${(end-start).toFixed(0)}ms
${((end - start)*1e3).toFixed(0)}μs`);
},500);
browser-process-hrtime
특히 performance.now는 웹 환경에서 process.hrtime에 대한 폴리필을 제공하는 데 사용되기도 한다.
https://github.com/kumavis/browser-process-hrtime
GitHub - kumavis/browser-process-hrtime: Browser shim for Node.js process.hrtime()
:watch: Browser shim for Node.js process.hrtime(). Contribute to kumavis/browser-process-hrtime development by creating an account on GitHub.
github.com
browser-process-hrtime 라이브러리는 performance.now을 이용하여 현재 deprecated 상태에 있는 hrtime에 대한 폴리필을 제공한다. 원리 자체는 기존에 설명한 방식과 동일하다.
- #L4: 현재 웹 환경의 performance.now 함수를 찾아 설정한다.
- #L15: hrtime에 대한 폴리필 함수로, performance.now 호출 결과에 값을 곱하거나 빼는 등 연산을 수행하여 시간을 계산한다. hrtime의 명세처럼 [초, 나노초] 형식의 타임스탬프를 반환한다.
"정확한" 작업 소요 시간 측정하기
https://blaxsior-repository.tistory.com/169
[자바스크립트] setTimeout이 정확한 시간을 보장하지 않는 이유
setTimeout() - Web API | MDN 전역 setTimeout() 메서드는 만료된 후 함수나 지정한 코드 조각을 실행하는 타이머를 설정합니다. developer.mozilla.org 자바스크립트는 특정 시간 이후, 또는 시간 간격으로 작업
blaxsior-repository.tistory.com
글은 안읽어도 되는데, 글 마지막 부분에서 제시하는 링크들은 이벤트 루프를 맛깔나게 설명하므로 읽어보자. 작업에 소요되는 시간을 정확히 측정하기 위해 필요한 부분이다.
대부분의 자바스크립트 환경은 이벤트 루프 기반으로 동작한다. 이벤트 루프는 현재 스택에 적재된 작업을 모두 마친 이후 프로미스, 콜백 및 이벤트 핸들러 작업을 큐에서 꺼내 처리한다. 다음 코드를 실행해 보면 이게 사실임을 알 수 있다.
console.log("before");
setTimeout(() => {
console.log("timeout!");
});
setImmediate(() => {
console.log("immediate!");
});
process.nextTick(() => {
console.log("nextTick!");
});
// setTimeout, setImmediate보다 먼저 실행됨.
console.log("after? no...");
let i = 0;
while(i++ < 3)
{
console.log("hello, world!");
}
console.log("real after? no...");
앞서 언급한 프로미스, 이벤트 핸들러 및 콜백에 의한 연산은 자바스크립트 환경 동작 상 스택이 아닌 별도 큐에 저장되어 있다가, 현재 수행하고 있는 작업이 모두 끝난 시점에 수행된다. 이러한 이유로 인해 콜백을 등록한 작업 자체는 완료되더라도 블록킹을 유발하는 스택 상의 작업이나 큐 내의 다른 작업들에 의해 콜백의 실행이 연기될 수 있다.
따라서 콜백 등을 이용하여 작업 시간을 측정하는 경우, 전체 프로그램 내에서 해당 작업만 실행하여 타 작업의 영향을 받지 않도록 만들 필요가 있다.