. tris XML en Javascript: Comparaison des performances .
Depuis sa version 1.5, firefox supporte la spécification
e4x (ECMAScript for XML) specification
qui facilite la manipulation d'arbres XML.
Avant:
var a = document.createElement('personne');
a.setAttribute('prenom', 'paul');
a.setAttribute('nom', 'dupond');
a.setAttribute('age', '32');
alert(a.getAttribute('age'));
Après:
var tree = <personne prenom="paul" nom="dupond" age="32" />; // affiche l'age de paul alert(tree.@age);
Cependant, la spécification ne définit pas de méthode de tri d'un arbre XML. Cet article présente 5 méthodes différentes de tri d'un arbre, en terminant par une comparaison de leurs performances respectives.
Tri par tas (en javascript)
function swap(array, x, y)
{
if(x == y)
{ return; }
let elt1 = array.node[x];
let elt2 = array.node[y];
array.node[y] = <{elt1.name()}/>;
array.node[x] = elt2;
array.node[y] = elt1;
}
function heapify(array, count)
{
start = count / 2 - 1;
while(start >= 0)
{
siftDown(array, start, count-1);
start = start - 1;
}
}
function siftDown(array, start, end)
{
root = start;
while(root * 2 + 1 <= end)
{
child = root * 2 + 1;
if(child < end && array.node[child].@key < array.node[child + 1].@key)
{ child = child + 1; }
if(array.node[root].@key < array.node[child].@key)
{
swap(array, root, child);
root = child;
} else {
return;
}
}
}
function heapsort(array)
{
heapify(array, array.node.length());
end = array.node.length() - 1;
while(end > 0)
{
swap(array, end, 0);
end = end - 1;
siftDown(array, 0, end);
}
}
// Génération des données de test et tri de ces données
var xml = <xml></xml>;
for(let i = 0; i < 500; i++)
{ xml.node[i] = <node key={Math.floor(Math.random() * 2000)}/>; }
s = Date.now();
heapsort(xml);
alert('heapsorted (' + (Date.now() - s) + ')');
» Référence | Télécharger l'exemple
Tri rapide (en javascript)
function swap(array, x, y)
{
if(x == y)
{ return; }
let elt1 = array.node[x];
let elt2 = array.node[y];
array.node[y] = <{elt1.name()}/>;
array.node[x] = elt2;
array.node[y] = elt1;
}
function partition(array, left, right, pivotIndex)
{
let pivotValue = array.node[pivotIndex].@key;
swap(array, pivotIndex, right);
let storeIndex = left;
for(let i = left; i < right; i++)
{
if(array.node[i].@key <= pivotValue)
{
swap(array, storeIndex, i);
storeIndex += 1;
}
}
swap(array, right, storeIndex);
return storeIndex;
}
function quicksort(array, left, right)
{
if(right > left)
{
let pivotIndex = left;
pivotNewIndex = partition(array, left, right, pivotIndex);
quicksort(array, left, pivotNewIndex-1);
quicksort(array, pivotNewIndex+1, right);
}
}
// Génération des données de test et tri de ces données
var xml = <xml></xml>;
for(let i = 0; i < 500; i++)
{ xml.node[i] = <node key={Math.floor(Math.random() * 2000)}/>; }
s = Date.now();
quicksort(xml, 0, xml.node.length()-1);
alert('quicksorted (' + (Date.now() - s) + ')');
» Référence | Télécharger l'exemple
Tri de tableau, en utilisant une fonction de comparaison javascript
// Génération des données
var xml = <xml></xml>;
for(let i = 0; i < 500; i++)
{
xml.node[i] = <node key={Math.floor(Math.random() * 2000)}/>;
}
// Remplissage du tableau avec ces données
s = Date.now();
ar = new Array();
for each(let node in xml.node)
{ ar.push(node); }
// Tri du tableau
ar.sort(function(a, b)
{
if(a.@key < b.@key)
{ return - 1; }
else if(a.@key > b.@key)
{ return 1; }
return 0;
});
alert('arraysorted (' + (Date.now() - s) + ')');
Tri de tableau, en utilisant la fonction native de comparaison
// Génération des données
var xml = <xml></xml>;faire chuter
for(let i = 0; i < 500; i++)
{
xml.node[i] = <node key={Math.floor(Math.random() * 2000)}/>;
}
// Remplissage du tableau avec ces données
s = Date.now();
ar = new Array();
for each(let node in xml.node)
{ ar.push(node.@key); }
// Tri du tableau
ar.sort();
alert('arraysorted (' + (Date.now() - s) + ')');
Tri XSLT
// Génération des données
var xml = <xml></xml>;
for(let i = 0; i < 500; i++)
{
xml.node[i] = <node key={Math.floor(Math.random() * 2000)}/>;
}
// Tri XSLT
s = Date.now();
// xslt stylesheet
var xslt = '<?xml version="1.0" encoding="UTF-8"?>' +
'<xsl:stylesheet version="1.0"' +
' xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml"'+
' xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
'<xsl:output method="xml" indent="yes" />' +
'<xsl:template match="/">' +
' <xsl:apply-templates select="//node">' +
' <xsl:sort select="@key" />' +
' </xsl:apply-templates>' +
'</xsl:template>' +
'<xsl:template match="node">' +
' <xsl:copy-of select="."/>' +
'</xsl:template>' +
'</xsl:stylesheet>';
var xsltproc = new XSLTProcessor();
xsltproc.importStylesheet(new DOMParser().parseFromString(xslt, "text/xml"));
var sorted = new XML(new XMLSerializer().serializeToString(
xsltproc.transformToDocument(new DOMParser().parseFromString(xml, "text/xml")))
);
alert('xsltsorted (' + (Date.now() - s) + ')');
» Référence | Télécharger l'exemple
Conclusion
Les résultats obtenus sont présentés dans le tableau suivant (NOTE: les vitesses ne sont pas intéressantes en tant que telles, c'est leur variation d'une méthode à une autre qui l'est) :
| Méthode/Vitesse | Tri par tas | Tri rapide | Tri de tableau (indirect) | Tri de tableau (direct) | tri via XSLT |
| 5500 ms | 4200 ms | 220 ms | 13 ms | 35 ms |
Les tri par tas et tri rapide étant écrit entièrement en javascript, ce sont naturellement les deux méthodes les plus lentes, Mais on peut faire deux remarques:
- le tri XSLT est étonnament très rapide (presque autant que celui d'un tableau),
- utiliser une méthode de comparaison écrite en javascript fait chuter les performances
En conclusion, s'il est naturel d'utiliser un tableau pour trier des données basiques (entiers, chaînes de caractères), il sera préférable d'utiliser le tri XSLT pour trier un objet XML. Les performances sont très proches d'un "tri tableau", tout en étant plus souple à utiliser (tris multi-critères notamment).
$2007/06/22$