문법적 성능개선
프로그래밍을 하다 보면 다양한 문제에 직면한다. 그리고 그 문제를 해결할 수 있는 방법은 매우 다양하다. 그중 우리는 대부분 최고의 성능을 내는 방법을 사용한다. 성능의 기준은 환경과 기타 제약 조건에 따라 달라질 수 있다. 메모리가 충분히 제공되지 않거나 시스템 연산 장치의 성능이 좋지 못한 환경일 수도 있다. 그래서 우리는 문제 해결을 위한 알고리즘의 공간 복잡도와 시간 복잡도를 따진다. 보통은 시간 복잡도가 가장 작은 알고리즘을 선택해 문제를 해결한다. 그렇다면 최적의 시간 복잡도를 갖는 알고리즘을 선택하여 문제를 해결할 때 여기서 더 성능개선을 할 수는 없을까?
이전 글에서 퀵 정렬과 힙 정렬의 미세한 성능 차이를 따져 보았다. 스왑 연산의 횟수 때문에 시간 복잡도에는 영향을 주지 않는 부분에서 성능 차이가 발 생 했다. 정렬된 결과물은 같지만 방법이 다르기에 성능의 차이가 발생한다. 사실, 속도라는 성능 기준에서 최적의 시간 복잡도를 갖는 해결책이 나왔다면 더 이상의 최적화를 고려하지 않을 수 있다. 그렇지만, 아주 미세하게 성능에 영향을 주는 요소들에 대해서도 최적화를 할 수도 있다. 다시 말해, 문제 해결의 접근 방법이 근본적으로 같을 경우 언어 차원에서 다른 문법을 사용하거나 다른 구현을 이용해 좀 더 성능을 쥐어짠다는 이야기이다.
예를 들어 소수점 이하 버림을 보자. 자바스크립트 언어에는 다양한 방법으로 소수점 이하 버림을 구현할 수 있다. Math 오브젝트를 이용한다던지 parseInt 함수를 호출하거나 논리 연산을 이용해 구현할 수 있다.
Math.floor(7/2) // 3
parseInt(7/2) // 3
7/2 | 0 // 3
~~(7/2) // 3
4가지 방법 모두 결과는 같지만 동작은 다르다. 그렇다면, 저 4개들의 성능에는 어떤 차이가 있을까? 차이점을 측정하기 위해 jsperf.com에서 테스트를 수행했다.
Ops/sec의 수치가 높을수록 빠른 성능을 내는 코드이다. Math.floor 함수가 가장 빠른 성능을 낸다. 그렇지만 tildes와 or연산을 이용한 방법과 큰 차이는 나지 않는다.
위에서 본 소수점 이하 버림 예제는 비교적 큰 성능 차이를 보이는 케이스이다. parseInt 함수과 Math.floor
함수의 성능 차이가 10배 이상 난다. 하지만 그렇지 않은 다양한 케이스들이 존재한다.
반복문끼리의 비교는 사실상 큰 차이는 나지 않지만 그들끼리 비교했을 경우 분명 빠르고 느리고 가 나뉜다. 이런 미세한 차이도 분명 차이이다. 그렇다면 이런 비교를 통해(특히 문법적이 부분에서) 성능을 극한까지 쥐어짠다면 자바스크립트 애플리케이션에 성능을 조금이라도 높일 수 있을까? 그리고 정말 이렇게 까지 해서 성능을 쥐어 짜야할까? 개인적인 견해는 케이스 바이 케이스이다.
대부분의 환경에서는 미세한 성능 차이를 신경 쓰지 않아도 된다. 요즘은 하드웨어 성능이 좋다 보니 이런 수준의 성능개선은 굳이 필요 없다. 그리고 프런트 엔드 UI 동작을 위한 자바스크립트 앱을 개발할 때 대부분의 성능은 렌더 동작에서 좌우된다. 얼마나 많은 reflow와 repaint가 이루어지는 지에 따라 성능이 크게 좌우될 수 있다. 그리고 애니메이션을 구현하기 위해 사용하는 requestAnimationFrame 가 반복적으로 호출될 때, 그 사이사이에 실행되는 로직들도 대부분 60 FPS를 유지하는데 문제없이 간단 코드들이고 하드웨어 또한 충분히 뒷받침해준다. 또한 미세한 성능을 개선하기 위해 코드의 문법을 바꾸고, 익숙하지 않은 표현을 쓰게 된다면 오히려 코드의 가독성을 떨어트릴 수 있게 된다. 작은 성능에 신경 쓰다가 더 큰 부분을 잃게 될 수도 있다.
자바스크립트는 더 이상 클라이언트 사이드만의 언어가 아니다. 서버에서 대용량 데이터를 처리하거나 수많은 요청에 대한 응답을 짧은 시간 내에 처리하기 위한 언어로도 사용될 수 있다. 따라서 성능을 극한까지 끌어올려야 품질 좋은 애플리케이션이 탄생된다. 또한 브라우저가 PC와 스마트폰과 같이 좋은 환경에서만 돌아가는 것이 아니라 저사양의 하드웨어에서도 충분히 돌아갈 수 있다. 저사양의 환경에서 좋은 성능을 내기 위해서는 렌더 동작의 최적화는 물론이고 코드 자체의 최적화를 통해 성능을 끌어낼 수 도 있다. 조금 더 빠른 코드를 작성하면 고사양 환경에서보다 저사양 환경에서 그 변화를 쉽게 체감할 수 있다. 작은 차이가 명품을 만든다고 하지 않던가.
개인적으로는 7:3 정도의 스코어로 고려하지 않아야 된다는 의견에 찬성이다. 성능에 강박관념이 있는 사람이 아니고서야 정말 미세한 성능을 고려하여 코딩하지는 않을 거라 생각한다. 사실 문법적으로 성능을 최적화한다면 코드의 가독성을 잃게 될 수도 있다. 작은 성능 향상 때문에 가독성을 잃는다는 것은 너무 큰 것을 잃는 것 같다. 하지만 조금의 욕심은 생긴다. 조금이라도 성능을 쥐어짜고 싶은 심정은 누구에게나 있을 테니까.