Wednesday, November 29, 2006
Ф.М.
Tuesday, November 28, 2006
Monday, November 27, 2006
Tuesday, November 14, 2006
javascript table creation benchmarks
JavaScript Table Creation Benchmarks
The purpose of this exercise is to find out the fastest way to create html tables with javascript.
I am testing 3 methods of creating a table: pure DOM, strings with innerHTML, and a DOM + innerHTML hybrid, where an off-screen table is constructed using strings and then the rows are copied into the body of the target table. Each test is run with 2 options: with and without content, to estimate which method is better for creating empty tables and populating them later as opposed to creating the tables where the content is known at the time of creation. To benchmark real-world performance, the end time is take after a small timeout to allow the table to render. Additionally, each test is run with and without styles to measure the impact of some common css rules.
Test 1. DOM.
Wrappers around the test function:function domTestWithContent(cnt) {
domTableTest(cnt, true);
}
function domTestWithoutContent(cnt) {
domTableTest(cnt, false);
}
Using cloneNode() whereever possible to avoid the overhead of document.createElement. This turns out quite a bit faster than body.insertRow and row.insertCell. The difference between creating the table with and without content is quite big: setting the data requires a loop through all the cells after the table is constructed.
/**
cnt: an element where to place the result
setCellContent: pass true to populate cells with "row, column"
*/
function domTableTest(cnt, setCellContent) {
if (typeof(setCellContent) == "undefined")
setCellContent = false;
var tab = document.createElement("TABLE");
var bod = document.createElement("TBODY");
tab.appendChild(bod);
for (var r = 0; r < rows; r++) {
if (r == 0) {
var row = bod.insertRow(-1);
for (var c = 0; c < columns; c++) {
if (c == 0) {
var cell = row.insertCell(-1);
if (setCellContent)
cell.appendChild(document.createTextNode("\u00A0"));
}
else {
cell = row.firstChild.cloneNode(true);
row.appendChild(cell);
}
if (setCellContent)
cell.firstChild.nodeValue = r + ", " + c;
}
}
else {
var row = bod.firstChild.cloneNode(true);
if (setCellContent) {
var cell = row.firstChild;
var c = 0;
while (cell) {
cell.firstChild.nodeValue = r + ", " + c;
cell = cell.nextSibling;
c++;
}
}
bod.appendChild(row);
}
}
cnt.appendChild(tab);
}
Test 2. innerHTML.
Wrappers around the test function:function stringTestWithContent(cnt) {
stringTableTest(cnt, true);
}
function stringTestWithoutContent(cnt) {
stringTableTest(cnt, false);
}
Using arrays to speed up strings performance. Push() may not be the fastest way to add array elements but I believe the overhead is negligible in this context. There is practically no difference between populating the cells with real data or blanks.
/*
cnt: the element where to place the result
setCellContent: send true to populate cells with "row, column"
*/
function stringTableTest(cnt, setCellContent) {
if (typeof(setCellContent) == "undefined")
setCellContent = false;
var buffer = new Array();
buffer.push("");
buffer.push("")
for (var r = 0; r < rows; r++) {
buffer.push("");
for (var c = 0; c < columns; c++) {
buffer.push("");
if (setCellContent)
buffer.push(r + ", " + c);
else
buffer.push(" ");
buffer.push(" ");
}
buffer.push(" ");
}
buffer.push("
");
cnt.innerHTML = buffer.join("");
}
Test 3. innerHTML + DOM.
Wrappers around the test function:function hybridTestWithContent(cnt) {
stringTableTest3(cnt, true);
}
function hybridTestWithoutContent(cnt) {
stringTableTest3(cnt, false);
}
If innerHTML turns out to be much faster than dom, this method will be good for adding rows to an existing table.
function hybridTableTest(cnt, setCellContent) {
if (typeof(withstyle) == "undefined")
withstyle = false;
var tab = document.createElement("TABLE");
cnt.appendChild(tab);
var buffer = new Array();
buffer.push("")
for (var r = 0; r < rows; r++) {
buffer.push("");
for (var c = 0; c < columns; c++) {
buffer.push("");
if (setCellContent)
buffer.push(r + ", " + c);
else
buffer.push(" ");
buffer.push(" ");
}
buffer.push(" ");
}
buffer.push("
");
// create the table in an off-screen div
var node = document.createElement("DIV");
var s = buffer.join("");
node.innerHTML = s;
// copy rows
var oldB = document.createElement("TBODY");
tab.appendChild(oldB);
var newB = node.firstChild.firstChild;
for (var i = 0; i < rows; i++) {
var r = newB.firstChild;
oldB.appendChild(r);
}
}
Running the tests
The settings: which tests to run, how many rows & columns to create, etcvar rows = 300;
var columns = 30;
var loops = 3;
var needStyles = false;
var tests = [ domTestWithContent, domTestWithoutContent,
stringTestWithContent, stringTestWithoutContent,
hybridTestWithContent, hybridTestWithoutContent
];
Using recursion and timers instead of a while or for loop to allow each test to complete rendering before proceeding with next iteration. There may be an overhead of a few milliseconds associated with setTimeout(), but it is the same for each run and it is negligible compared to the time taken by the test itself.
function run() {
var contentElement = document.getElementById("cnt");
// set (or unset) the class of the target element, to test with or without styles
setStyle(contentElement, "withstyle", needStyles);
var testTimes = new Array(tests.length);
for (var j = 0; j < testTimes.length; j++)
testTimes[j] = 0;
var i = 0;
var loop = 0;
// this function will be invoked to run each test
runOneTest = function() {
// empty the target element
while (contentElement.firstChild)
contentElement.removeChild(contentElement.firstChild);
// start time
var d1 = (new Date()).getTime();
// run the test
tests[i](contentElement);
setTimeout(function() {
// take the 2nd time measurement after rendering is done
var d2 = (new Date()).getTime();
testTimes[i] += (d2 - d1);
i++;
if (i < tests.length)
// run the next test in tests array
runOneTest();
else if (loop < loops) {
// run the tests again
loop++;
i = 0;
runOneTest();
}
else {
// calculate averages and show results
for (var k = 0; k < testTimes.length; k++)
testTimes[k] = Math.round(testTimes[k]/loops);
showResults(testTimes);
if (!needStyles) {
// chain the 2nd test
// not pretty but does the trick
needStyles = true;
run();
}
}
}, 1);
};
runOneTest()
}
This is the stylesheet applied when testing with styles. Nothing fancy.
.withstyle {
}
.withstyle TABLE {
border-collapse: collapse;
}
.withstyle TD {
border: solid 1px black;
white-space: nowrap;
width: 55px;
font-family: Sans-serif;
font-size: 10px;
}
Run the tests
run();
Results
There results are only good for comparisons with one another and not as any kind of absolute performance guideline. So it makes very little sense to mention what kind of hardware/os combo I got them on. But I will anyway.
These numbers are from a dual Xeon 3.6 Ghz workstation with 3GB of ram and no hyper-threading. Windows XP.
style? | DOM w/content | DOM w/o content | innerHTML w | innerHTML w/o | hybrid w | hybrid w/o |
opera | ||||||
w/o | 787 | 427 | 734 | 625 | 781 | 609 |
w | 1073 | 557 | 1047 | 927 | 1099 | 896 |
firefox 2 | ||||||
w/o | 1843 | 359 | 1276 | 802 | 1198 | 833 |
w | 1781 | 463 | 896 | 891 | 1078 | 911 |
firefox 1.5 | ||||||
w/o | 1906 | 375 | 1193 | 802 | 1229 | 901 |
w | 1651 | 495 | 995 | 922 | 1047 | 974 |
internet explorer 6 | ||||||
w/o | 2406 | 1636 | 1177 | 766 | 1609 | 1198 |
w | 7145 | 6583 | 5828 | 5370 | 6218 | 5994 |
Conclusions
Go Opera. Too bad it's irrelevant. However, on most tests firefox is not far behind, and in the tests with style it is actually ahead. This is, in fact, very suprising - Firefox shows better numbers when styles are set, while common sense tells me that it should be a bit slower, like the rest of the browsers.
What a pathetic display from Internet Explorer. Whoever said it was fast probably never waited for it to finish rendering. Or never tried formatting the pages. Or both. I hope IE7 is better
Internet Explorer screws up all conclusions. It is clear to me that for Firefox and Opera, DOM is the way to go, especially with empty tables. In IE innerHTML is the king. Again, IE7 will hopefully change this.
Run the test here: http://www.benya.com/code/jsbenchmarks/tables.html and post your results. I'd love to see how Safari fares, especially compared to some other browser running on the same platform.
Saturday, November 11, 2006
Спасибо, дорогая редакция
Hello, Your product "ToCyrillic 0.5.8" has been tested by the Softpedia labs and found to be completely clean of adware/spyware components.Неужли они думают, что я этого не знаю? Это скрипт, как там можно что-то спрятать? И взяли они его с сайта, где этот плагин уже протестировали.
we have granted it with the "100% CLEAN" Softpedia awardСпасибо, я польщён. А награда за 99% clean у вас тоже есть? И как это считается? Как получить 95%? И если не 100%, вы всё равно помещаете это в свою "download encyclopedia"? Очень жаль, что есть достаточно много людей, чтобы окупать существование этой туфты