ugrás a tartalomhoz

JS benchmark: sok elem beszúrása az oldalba

fchris82 · 2008. Aug. 25. (H), 19.20
Igazából nincs semmi kérdésem, csak gondoltam megosztom veletek, hogy mit csináltam :)

Kíváncsi voltam, hogy ha JS-ben szeretném legenerálni az oldal egy részét, akkor vajon milyen módszerrel érdemes. Az alábbi ötleteim támadtak:
1. createElement + appendChild
2. Létrehozom a stringet, pl "<div></div><div></div>...", és innerHTML-lel "adom" hozzá.
3. Rögtön az innerHTML-t bővítgetem

Továbbá felmerült az is, hogy vajon hogyan érdemes a stílus paramétereket megadni az új elemnek.
1. setAttribute('style', '[komplett stílus]'). Itt van egy olyan probléma, hogy IE alatt ez a megoldás nem működik. Lásd: http://www.quirksmode.org/dom/w3c_core.html és http://www.quirksmode.org/bugreports/archives/2005/03/setAttribute_does_not_work_in_IE_when_used_with_th.html . A probléma megoldását az utóbbiról vettem. (lásd forrás)
2. Egyesével végigmegyek és obj.style.[tulajdonság] = 'érték' megoldást használom.
3. "Stringes" esetben persze az előző kettő opció nem játszik.

A mérés WinXP Prof operációs rendszer alatt készült, a táblázatban az értékek ms-ok, és a kisebb érték a jobb. 10 000 div-et kellett beszúrniuk.

A tapasztalatok:
- A string + innerHTML megoldás annyira lassú minden böngészőben, hogy megkockáztatom a kijelentést: Soha ne használd arra, hogy "HTML elemekkel" így bővítsd az oldalt. A forrásból ki is hagytam ezt a tesztet, mert 1000 div esetén FF és IE is rendesen megizzad, a többin nem is próbáltam, már csak 100-zal.
- Amint a táblázatból kitűnik, minden böngészőben van különbség a két eljárás között, és általában a setAttribute() megoldás előnyére (még ha néha elég kicsi is)
IE6IE7FF3Opera 9.26Safari 3.1.2
setAttribute()106812714851700453
appendChild()23415633434494
style118813918581618523
appendChild()234156348279664

- Az eredmények 4-4 mérés átlagából származnak, kivéve Opera, ahol akkora volt a szórás, hogy inkább 5-öt mértem :D Operánál eléggé ingadozott, a többinél folyamatosan ugyanazok az értékek jöttek nagyjából.
- Érdekes, hogy Safarinál a "style" módon stílusozott elemeket sokkal lassabban adja hozzá az oldalhoz. Ez a jelenség akkor is tapasztalható volt, ha felcseréltem a tesztek futásának sorrendjét. (94 ms vs 664 ms)
- További érdekesség még, hogy FF3 alatt az ilyen elemeket sokkal lassabban hozza létre! (485 ms vs 858 ms)

Forrás:
  1. <html>  
  2. <head>  
  3.   <title>JS Benchmark</title>  
  4.   <script>  
  5.   function test() {  
  6.     var n, matrix;  
  7.     n = 10000;  
  8.     matrix = [];  
  9.     t      = [];  
  10.   
  11.     // Mátrix léterhozása  
  12.     t.push(new Date());  
  13.     for(i=0;i<n;i++) {  
  14.       matrix[i] = document.createElement('div');  
  15.       matrix[i].setAttribute('id', 'div'+i);  
  16.       if(document.all) {  
  17.         matrix[i].style.cssText = 'border: 1px solid black; width: 2px; height: 2px';  
  18.       } else {  
  19.         matrix[i].setAttribute('style', 'border: 1px solid black; width: 2px; height: 2px');  
  20.       }  
  21.     }  
  22.     t.push(new Date());  
  23.   
  24.     // Megjelenítés  
  25.     c = document.getElementById('container');  
  26.     t.push(new Date());  
  27.     for(i=0;i<n;i++) {  
  28.       c.appendChild(matrix[i]);  
  29.     }  
  30.     t.push(new Date());  
  31.   
  32.     // Tartalom törlése  
  33.     c.innerHTML = '';  
  34.   
  35.     // Mátrix léterhozása  
  36.     t.push(new Date());  
  37.     for(i=0;i<n;i++) {  
  38.       matrix[i] = document.createElement('div');  
  39.       matrix[i].setAttribute('id', 'div'+i);  
  40.       matrix[i].style.border = '1px solid black';  
  41.       matrix[i].style.width  = '2px';  
  42.       matrix[i].style.height = '2px';  
  43.     }  
  44.     t.push(new Date());  
  45.   
  46.     // Megjelenítés  
  47.     c = document.getElementById('container');  
  48.     t.push(new Date());  
  49.     for(i=0;i<n;i++) {  
  50.       c.appendChild(matrix[i]);  
  51.     }  
  52.     t.push(new Date());  
  53.   
  54.     document.getElementById('value1').innerHTML = (t[1].getTime()-t[0].getTime()) + ' ms';  
  55.     document.getElementById('value2').innerHTML = (t[3].getTime()-t[2].getTime()) + ' ms';  
  56.     document.getElementById('value3').innerHTML = (t[5].getTime()-t[4].getTime()) + ' ms';  
  57.     document.getElementById('value4').innerHTML = (t[7].getTime()-t[6].getTime()) + ' ms';  
  58.   }  
  59.   
  60.   window.onload = test;  
  61.   </script>  
  62. </head>  
  63. <body>  
  64. <h1>document.createElement() + setAttribute('style', '...')</h1>  
  65.   <div id="value1"></div>  
  66. <h1>container.appendChild()</h1>  
  67.   <div id="value2"></div>  
  68.   
  69. <hr/>  
  70.   
  71. <h1>document.createElement() + style.[...] = '...'</h1>  
  72.   <div id="value3"></div>  
  73. <h1>container.appendChild()</h1>  
  74.   <div id="value4"></div>  
  75.   
  76. <h2>Container</h2>  
  77.   <div id="container"></div>  
  78. </body>  
  79. </html>  
Ui:
A BBEditor a szövegszerkesztőből vagy böngészőből másolt tabokkal elválasztott cellás formázását tudja táblázattá alakítani automatikusan.

Ez nem működik :$ Vagy én csináltam vmit rosszul :?
 
1

A string + innerHTML megoldás annyira lassú minden böngésző

toxin · 2008. Aug. 26. (K), 07.19
valami benézést sejtek, csak nem tudom én vagy a te részedről :) , innerHTML töltésnek sokkal gyorsabbnak kéne lennie, mint a DOM konstrukciós műveletnek, nálam az arányok megfelelnek az itt mértekkel http://bdn.backbase.com/blog/grauw/javascript-html-construction-benchmark

mátrix feltöltése 10000 elemnél is < 100 ms, 1000 elemnél már elhanyaglható 0-20ms különböző böngészőkben, innerHTML töltési idő nagyjából 1/2 az appendChilld-hez képest, inline stílusbeállítást nem használtam, gyakorlatban is alábbi módon töltök

másrészt most ébredtem bővel lehet, hogy vmit benéztem :))
  1.   <html>    
  2.   <head>    
  3.     <title>JS Benchmark</title>  
  4.  <style type="text/css">  
  5.       
  6. .test{  
  7.     border: 1px solid black; width: 2px;   
  8.     height: 2px;  
  9. }  
  10.   
  11.  </style>    
  12.     <script>   
  13.     function test() {    
  14.       var n, matrix;    
  15.       n = 10000;    
  16.       matrix = [];    
  17.       t      = [];    
  18.      
  19.      // Mátrix léterhozása    
  20.      t.push(new Date());    
  21.      for(i=0;i<n;i++) {    
  22.        matrix[i] = document.createElement('div');    
  23.        matrix[i].setAttribute('id', 'div'+i);    
  24.        if(document.all) {    
  25.          matrix[i].style.cssText = 'border: 1px solid black; width: 2px; height: 2px';    
  26.        } else {    
  27.          matrix[i].setAttribute('style', 'border: 1px solid black; width: 2px; height: 2px');    
  28.        }    
  29.      }    
  30.      t.push(new Date());    
  31.      
  32.      // Megjelenítés    
  33.      c = document.getElementById('container');    
  34.      t.push(new Date());    
  35.      for(i=0;i<n;i++) {    
  36.        c.appendChild(matrix[i]);    
  37.      }    
  38.      t.push(new Date());    
  39.      
  40.      // Tartalom törlése    
  41.      c.innerHTML = '';    
  42.      
  43.      // Mátrix léterhozása    
  44.      t.push(new Date());    
  45.      for(i=0;i<n;i++) {    
  46.        matrix[i] = document.createElement('div');    
  47.        matrix[i].setAttribute('id', 'div'+i);    
  48.        matrix[i].style.border = '1px solid black';    
  49.        matrix[i].style.width  = '2px';    
  50.        matrix[i].style.height = '2px';    
  51.      }    
  52.      t.push(new Date());    
  53.      
  54.      // Megjelenítés    
  55.      c = document.getElementById('container');    
  56.      t.push(new Date());    
  57.      for(i=0;i<n;i++) {    
  58.        c.appendChild(matrix[i]);    
  59.      }    
  60.      t.push(new Date());   
  61.     
  62.          // Tartalom törlése    
  63.      c.innerHTML = '';    
  64.      
  65.      // Mátrix léterhozása    
  66.      t.push(new Date());    
  67.      for(i=0;i<n;i++) {    
  68.        matrix[i] = '<div class="test"></div>';    
  69.      }    
  70.      t.push(new Date());    
  71.      
  72.      // Megjelenítés    
  73.      c = document.getElementById('container');    
  74.      t.push(new Date());    
  75.      c.innerHTML = matrix.join("");    
  76.      t.push(new Date());    
  77.      
  78.      document.getElementById('value1').innerHTML = (t[1].getTime()-t[0].getTime()) + ' ms';    
  79.      document.getElementById('value2').innerHTML = (t[3].getTime()-t[2].getTime()) + ' ms';    
  80.      document.getElementById('value3').innerHTML = (t[5].getTime()-t[4].getTime()) + ' ms';    
  81.      document.getElementById('value4').innerHTML = (t[7].getTime()-t[6].getTime()) + ' ms';  
  82.   document.getElementById('value5').innerHTML = (t[9].getTime()-t[8].getTime()) + ' ms';    
  83.      document.getElementById('value6').innerHTML = (t[11].getTime()-t[10].getTime()) + ' ms';    
  84.    }    
  85.      
  86.    window.onload = test;   
  87.    </script>    
  88.  </head>    
  89.  <body>    
  90.  <h1>document.createElement() + setAttribute('style', '...')</h1>    
  91.    <div id="value1"></div>    
  92.  <h1>container.appendChild()</h1>    
  93.    <div id="value2"></div>    
  94.      
  95.  <hr/>    
  96.      
  97.  <h1>document.createElement() + style.[...] = '...'</h1>    
  98.    <div id="value3"></div>    
  99.  <h1>container.appendChild()</h1>    
  100.    <div id="value4"></div>  
  101.   
  102.  <hr/>   
  103.    
  104. <h1>string + style</h1>    
  105.    <div id="value5"></div>    
  106.  <h1>container innerHTML</h1>    
  107.    <div id="value6"></div>     
  108.      
  109.  <h2>Container</h2>    
  110.    <div id="container"></div>    
  111.  </body>    
  112.  </html>    
érdekes lehet még a stringek összefűzése különböző módszerekkel,
Conclusions

Though this analysis, a number of things about string performance have been observed:

* Native string operations in all browsers have been optimized to the point where borrowing techniques from other languages (such as passing around a single buffer for use by many methods) is for the most part unneeded.
* Array.join still seems to be the fastest method with Internet Explorer; either += or String.prototype.concat.apply(”", arguments) work best for all other browsers.
* Firefox has definite issues with accessing argument members via dynamic/variables
* And of course, the reminder to not ignore the data
http://www.sitepen.com/blog/2008/05/09/string-performance-an-analysis/

fent én is az Array.join-t használtam

üdv Csaba
2

Wow

fchris82 · 2008. Aug. 26. (K), 15.25
Valóban. Vmit írtózatosan elkavarhattam :D Ma is tanultam vmit :) Ugyanakkor a scriptedben volt egy kis csalás:
- nem adtál id attributumot a diveknek
- class-t használtál nem pedig style-t, ami szintén gyorsított
FF3 alatt hozzáadva ezt a két dolgot (5, 249)-ről (18, 541)-re változott az eredmény! De még így is gyorsabb volt úgy 40%-al.

Tudom, hogy a class gyorsabb egyébként, de nem életszerű, hogy üres diveket akar az ember beszúrkálni, ugyanolyan tulajdonsággal. Legalábbis nálam nem ez a helyzet :)

Egyébként a "javascript benchmark dom"-ra rákeresve jó sok ilyen cuccot találtam, de ez a szóhármas csak ma jutott eszembe... Itt van egy rossz példa az innerHTML használatra: http://andrew.hedges.name/experiments/innerhtml/ . Nekem nagyságrendekkel több jött ki innerHTML-re. Most így elsőre nem is látom, hogy miért :-/ Nah, mindegy :)
3

http://www.liligo.hu/

toxin · 2008. Aug. 26. (K), 18.01
indíts el egy keresést , és középen a találati elemek vannak így kirakva, bár a generálás ott velocity2js-el történik, ami már átvezett js template motorok világába http://weblabor.hu/blog/20061119/kliensoldalisablon#comment-36747 lehet frissíteni kéne :)

üdv Csaba

jav: márha vannak találatok, szóval a http://www.liligo.fr -en