개발/Javascript

[You don't know JS] Part3 - 6장. 벤치마킹과 튜닝

lanace 2020. 10. 11. 22:44

a++와 ++a 사이에 어떤것이 성능이 좋을것인가?

학부생때도 뭐가 더 좋은지 많이 고민해보았고, 써져있는 글마다 달랐다.

결론적으로 의미가 없다는걸 알게되었다.

저 둘이 성능차이가 있으면 얼마나 있을꺼고, 내 서비스에 얼마나 큰 영향을 줄수 있을까...

책에서도 그러한 미시적 성능 튜닝에 초점을 두고있진 않았다.

6.1 벤치마킹


일반적으로 많이 사용하는 성능 측정 코드이다.

var start = (new Date()).getTime(); // TODO: 테스트할 작업 var end = (new Date()).getTime(); console.log(end - start);

위와같은 방법으론 정확한 성능을 알아낼 수 없다.

문제점은 다음과 같다.

  • getTime의 최소값인 밀리세컨 이하의 값은 측정 불가
  • 단일 실행 문제
  • 부가 작업 문제
  • 환경 의존적 문제

6.1.1 반복

단순히 반복실행후 평균값을 구한다 해도 문제는 여전히 있다.

  1. 이상점이 생기면 평균값이 외곡된다.
  2. 타이머의 정확도에 의존적이다.
  3. 샘플의 이상적인 갯수 설정
  4. 접근 방식간의 균형점을 찾아야함

결과적으로 벤치마킹은 하나의 분야로 다루어질 정도로 깊은 분야이다.

단순히 생각해서 측정할 문제가 아니다.

따라서 외부에 검증된 라이브러리나 서비스를 이용하는것이 좋다.

6.1.2 Benchmark.js

신뢰할 수 있는 벤치마킹은 반드시 통계적으로 검증된 지침에 근거한다.

Benchmark.js은 브라우저 환경의 스크립팅 이외에서도 사용 가능

애플리케이션 코드의 임계 경로 부분에 대한 자동화 성능 회기 테스트로 많이 사용됨

6.2 콘텍스트가 제일


위에서도 잠깐 얘기했지만 a++와 ++a 간의 성능차이는 의미가 없는경우가 대부분이다.

만약 a++와 ++a 사이에 성능차이가 100나노 초 정도의 짧은 시간이라면 아무리 많이 실행하더라도 서비스에 큰 지장을 주지 않기 때문이다.

따라서 이러한 미세한 성능에 신경쓰기보단 서비스에 지장을 주는지, 필요한 성능을 이끌어 낼 수 있는지 고민하는게 더 현실적이라는 의미이다.

컴퓨터 사이언스에선 중요하겠지만 엔지니어링 입장에선 중요하지 않다는 얘기이다.

6.2.1 엔진 최적화

독립 테스트 결과 x가 y보다 빠르다는 사실이 항상 적용 되는것은 아니기 떄문에 조심해야 한다.

var twelve = "12"; var foo = "foo"; var x1 = parseInt(twelve); var x2 = parseInt(foo); var y1 = Number(twelve); var y2 = Number(foo);

Number와 parseInt를 비교하는데, 어느쪽이 빠를까

뇌피셜로 생각해보자.

foo와 twleve모두 한곳에서 사용되므로 자바스크립트 엔진이 모두 인라인으로 결정할 것이다.

아니면 데드코르 제거 휴리스틱이 개입되어 x1, x2, y1, y2 모두 사용하지 않는 코드라 판단하여 그냥 삭제할 수도 있다.

현대 자바스크립트 엔진은 엄청 복잡하다.

실제로 어떻게 동작할지 알 수 없다.

따라서 엔진 내부에선 어떻게 동작할지 알 수 없기때문에 이러한 최적화는 의미가 없다.

실제 코드가 아닌 실제 결과를 테스트 해야한다.

6.3 jsPerf.com


jsPerf.com은 접속 가능한 공개 URL에 대해 Benchmeark.js 라이브러리를 이용하여 통계적으로 정확하고 믿음성 있는 테스트를 대행한다.

6.4 좋은 테스트를 작성하려면


테스트 케이스 사이의 차이점은 무엇인지, 그 차이점은 의도적인지 비 의도적적인지 분석하고 고민해야 한다.

정확한 테스트 의도가 무엇인지 기록하고 문서화하자

의도적 차이라면 확실하게 공표하여야 한다.

테스트와 크게 상관없는 부분은 페이지나 테스트 설정부에 미리 선언형태로 빼어내는것도좋다.

실행은 대개 더 느려지겠지만 콘텍스트와 더 밀접하게 연관된 차이점을 포착할 수 있다.

6.5 미시성능

6.5.1 똑같은 엔진은 없다.

자바스크립트가 명세한 성능 요건은 거의 없다.

작업에 따라 어느정도 성능 가감은 불가피하지만 어떤 작업을 최적화 대상으로 삼을지는 엔진이 주관적으로 판단한다.

따라서 모든 브라우저에 최적화는 힘들다.

그래도 그중 많이 언급되는걸로는 다음과 같은 것들이 있다.

  • 함수간 arguments변수를 전달하지 말자. 메모리 누수때문에 함수 구현체가 느려진다.
  • try-catch 구문은 함수에서 떼어내라. 브라우저는try-catch 구문이 포함된 함수를 최적화 하려고, 나머지 주변 코드는 반최적화 되므로 피하자.

6.5.2 큰 그림

임계 경로에서 실행되는 코드인지 먼저 확인하자.

임계 경로에 있지 않은 코드면 최적화해도 별 화과가 없을 가능성이 크다.

비 임계 경로의 실행 속도를 신경쓰고 걱정하느라 많은 시간을 낭비한다. 디버깅 및 유지보수 관점에선 이런식으로 효율을 높여보고자 하는행위가 매우 부정적인 결과를 끼친다.

6.6 꼬리 호출 최적화 (TCO)


ES6 이후에 성능 요건이 추가되었다.

function foo(x) { } function bar(y) { return foo(y + 1); // 꼬리호출 } function baz() { return 1 + bar(40); // 꼬리호출 아님 }

bar 함수에서 반환하는 foo가 꼬리호출이다.

foo가 끝나면 bar도 같이 끝나기 때문이다.

하지만baz의 return은 bar가 끝났을때 +1 을 해야하기 때문에 꼬리호출이 아니다.

TCO능력을 갖춘 엔진은 꼬리호출을 인지하여 bar가 끝난뒤 foo를 부를때 새로운 스택 프레임을 생성하지 않고, 기존 bar 의 스택 프레임을 재사용 한다.

속도는 빠르지만 메모리는 적게 사용한다는 장점이 있다.

재귀에서 큰 효과를 볼 수 있다.