Rev. 2.73

그동안 Prototype 자바스크립트 프레임웍의 사용법을 익히느라 필요이상으로 무리하게 사용해 왔습니다. 간단하게 크로스-브라우저가 보장되며 코드를 아름답게(?) 만들 수 있다는 장점 때문이였는데요. 개발하기 편하여 생산성 높은 환경을 제공하는 반면 원시코드에 비해 9배이상의 시스템 성능저하가 나타나기도 합니다. 아래와 같은 테스트용 루프 함수를 만들고 결과는 동일하면서 모든 브라우저에서 통용되며 상습적으로 사용되는 Prototype함수와 자바스크립트 메서드를 벤치마킹해 보았습니다. 테스트에 사용된 Prototype 프레임웍 라이브러리의 버전은 Scriptaculous1.6.1에 포함된 1.5.0_rc0이며, 브라우저는 파이어폭스2.0b, 시스템사양은 Pentium4, 3.00GHz입니다.

// 타임체크 함수
function timeChecker() {
    var befor, loops, after, tpo, tet, result
    before = new Date()
    loops = 500 //루프 수
    for (var i=0; i < loops; i++) {
        Element.update('element', '내용')// 태스트함수
    }
    after = new Date()
    tpo = Math.round(1000*(after-before)/loops)
    tet = (after-before)/1000
    result = 'Time per operation: '+tpo+', Total excuted time: '+tet; //결과값
    throw(result)
    //return result
}
timeChecker();

1. DOM 엘리먼트 스타일 설정:
프로토 타입에서는 여러가지 형태로 DOM을 주무를수 있게 해 주는데요. 1만회에 걸쳐 객체를 width:'300px', height:'100px'으로 변경하는 테스트입니다. 아래의 결과를 살펴봅시다.

1. document.getElementById('엘리먼트').style.스타일 = '값' // (원시코드, 2라인) -->  1.14초
2. $('엘리먼트').style.스타일 = '값' // (2라인) -->  1.656초
3. Element.setStyle('엘리먼트', {'스타일:값의 배열x2'})  // --> 1.985초 
4. $('엘리먼트').setStyle({'스타일:값의 배열x2'}) // --> 3.047초

동일한 결과값을 가지는 위 4가지 코드는 처리에 필요한 시간이 서로 다릅니다. 4번은 원시코드와 2배이상 차이가 벌어지는 재미있는 결과를 보여줍니다. 반복성이 짙은 경우, 즉 엘리먼트를 실시간으로 업데이트하거나 모션에 사용할 코드는 4번의 형태는 가급적 피하는 것이 좋습니다. 당연히 1.번이나 2번의 형태가 좋겠죠?

2. DOM 엘리먼트 스타일 값 구하기:
DOM의 위치또는 모양을 확인하기 위해 prototype은 getStyle, getDimensions 메서드를 제공하고 있으며, 엘리먼트의 높이 값만을 구하기 위한 getHeight와 같은 메서드도 있습니다.(그나저나 getWidht는 왜 없는걸까요?) 엘리먼트의 높이값(height)을 알기위한 가장 빠른 방법은 무엇인지 살펴 봅시다.

1. document.getElementById('엘리먼트').offsetHeight  // (원시코드) -->  Number 0.391초
2. $('엘리먼트').offsetHeight //Number --> 0.625초
3. Element.getHeight('엘리먼트') //Number --> 0.641초
4. document.getElementById('엘리먼트').getHeight() //Number --> 1.328초
5. $('엘리먼트').getHeight() //Number --> 1.563초
6. Element.getStyle('엘리먼트', '스타일')  //String  --> 2.812초 
7. $('엘리먼트').getStyle('스타일') //String  --> 3.843초

높이 값이 '123px'와 같은 스트링으로 반환된 6, 7번은 'px' 문자열을 없애기 위해 parseInt()하거나 '스트링.substring(0,스트링.length-2)'을 사용해야 합니다. 6, 7번은 쓸일이 없을 것 같은데 왜 집어넣었냐구요? 하지만 아래와 같은 경우(다중 이미지 비율계산)에는 벤치마크 결과와는 정 반대로 6, 7번이 훨씬 빠른 성능을 내기도 합니다.(슬라이드를 움직여 프레임을 비교해보세요.)

// Realtime image Ratio
function setRatio(v) {
	var x,y,h
	var w=Math.round(v)
	$$('array').each(function(e){
		x=Element.getStyle(e, 'width'); y=Element.getStyle(e, 'height')
		h=Math.round(v*y.substring(0,y.length-2)/x.substring(0,x.length-2));
		e.style.width=w+'px'
		e.style.height=h+'px'
		e.style.margin=(w-h)/10+'px'
	})
}

Element.getHeight('엘리먼트')

Width: 75px

Element.getStylet('엘리먼트', 'height')

Width: 75px

3. DOM 엘리먼트.Show/Hide:

1. document.getElementById('엘리먼트').style.display = 'block' // -->  0.547초
2. $('엘리먼트').style.display = 'block' // -->  0.812초
3. Element.show('엘리먼트')  // --> 1.312초
4. $('엘리먼트').show()  // --> 2.609초

또한 가장 많이 사용하는 것 중의 하나인 엘리먼트 Show/Hide를 봅시다. 1만회를 루프로 돌린 결과입니다. 스타일의 결과와 비슷하지요? 역시 원시코드가 가장빠릅니다. 하지만 이것은 결과값이 브라우저에 따라 서로 다르게 나타나기도 합니다. IE에서는 $('element').style.display = 'block', 파이어폭스에서는 Element.show('element')가 잘 돌아간다는 느낌입니다.

4. DOM 엘리먼트.InnerHTML:

1. document.getElementById('엘리먼트').innerHTML // -->  0.672초
2. $('엘리먼트').innerHTML = '내용' // -->  0.766초
3. Element.update('엘리먼트', '내용') // -->  2.39초

이것은 3천회 루프의 결과입니다. 이 또한 원시코드를 사용하는 것이 3배 빠르군요. 눈치 채셨겠지만 "오브젝트.메서드"의 결합과 "메서드(오브젝트)"에도 결과는 같지만 소요시간이 다릅니다. DOM 스타일링에는 메서드(오브젝트)와 같은 형태가 좋습니다. 뭐 결과적으로는 원시코드를 적절히 혼용하면 시스템 퍼포먼스를 크게 끌어올릴 수 있다는 것이지만요.

5. 정수 구하기:

1. Math.round(넘버) // -->  1.453초
2. (넘버).toFixed(0) // -->  3.859초

소수점 이하의 수를 반올림하는 경우 100만회 루프 결과 toFixed(0) 보다 Math.round()가 2배이상 빠릅니다.

6. 브라우저별 색상처리:
좀 다른 얘기지만, getStyle로 색상 값에 따른 이벤트를 처리하는 경우 브라우저 마다 가져오는 색상 값이 틀립니다. 스타일스트(CSS)에 '#f80'으로 설정되었을 경우 브라우저별로 가지고 온 결과는 아래와 같습니다.

오페라 = '#ff8800'
파이어폭스 = 'rgb(255, 136, 0)'
익스플로러 = '#f80'

7. 홀수 짝수 구하기:
홀수나 짝수를 루프에서 처리할 때 'n%2'를 쓰거나 'n&1'을 쓰는 두가지 방법이 있습니다. 이럴땐 'n&1'이 미약하게 나마 약간 더 빠릅니다.

for (var n=0; n < 100;n++) if(n%2==1)$('element').innerHTML = 'test';
for (var n=0; n < 100;n++) if(n&1==1)$('element').innerHTML = 'test';

추가. 문자열비교 : match 보다 test가 빠르고 indexOf가 두배 빠름(50만회)

var testing = 'test test test';
testing.match(/test/); // -->  0.702초
/test/.test(testing); // -->  0.643초
testing .indexOf('test ') != -1; // -->  0.395초

추가. if문과 단순 조건문 속도차이 없음.(500만회)

var boo = true; 
boo = boo? 'true' : 'false'; // -->  0.736초
if(boo){ boo = 'true'; } else { boo = 'false'; } // -->  0.726초

추가. DOM Selecter(500회)

$$('#taglist .row');// -->  1.496초
$$('.row');// -->  tset failed
document.getElementsByClassName('row', 'taglist');// -->  0.193초
document.getElementsByClassName('row');// -->  3.321초

Comments

Got something to add? You can just leave a comment.

  • 원시 인터페이스(document.getElementById)를 혼용해서 쓰는 것보다는 한 번 억세스한 값을 바인딩해놓고 쓰는 전략이 낫지 않을까요? 효과가 의심스러운 섣부른 최적화 기법을 적용하는 것 보다는 프로파일러 같은 것을 이용해서 실제 병목이 되는 지점을 정확하게 파악하는 작업이 우선되어야 할 것 같습니다. 자바스크립트 인터프리터 구현에 따라 다양한 특징이 존재하고, obj.method가 반드시 더 빠르리란 보장은 없습니다--물론 실제로는 대부분의 구현에서 스태틱 디스패칭이 동적 디스패칭보다 빠르겠지만요.

    var el = document.getElementById('somediv');
    for (var i = 0; i < 500; i++) { el.innerHTML = "foobar"; }

    "Premature optimization is the root of all evil." -- Donald Knuth

    reply edit

  • 조언감사합니다. nohmad 팀장님~ 이 누추한곳까지 방문해주시궁... 프로파일러도 꼭 사용해 보겠습니다.(근데, 무슨 말씀인지 하나도 모르겠어요. 열공하겠습니다. ㅡ.ㅡ;)

    reply edit

  • 중복되는 부분이나 특정한 부분에선 때때로 부분변수를 이용하는게 나을수도 있다는 말인것 같네요..

    reply edit

  • 아.. 그렇군요.
    이글의 요지는 같은역할을 하는 코드들의 벤치마킹이였는데... ㅠ.ㅠ;

    reply edit

  • 자바스크립트(Prototype) 코드 스피드 밴치마킹

Your Reaction Time!

captcha

avatar