Optimize iD.Connection

The big win here is using direct property accessors on
node attributes rather than iteration. The rest is just
micro-optimization.
This commit is contained in:
John Firebaugh
2013-02-09 20:48:25 -08:00
parent d1f6f43ad6
commit 3e7b0b0d98
2 changed files with 173 additions and 61 deletions
+74 -61
View File
@@ -7,7 +7,13 @@ iD.Connection = function(context) {
keys,
inflight = {},
loadedTiles = {},
oauth = iD.OAuth(context).url(url);
oauth = iD.OAuth(context).url(url),
ndStr = 'nd',
tagStr = 'tag',
memberStr = 'member',
nodeStr = 'node',
wayStr = 'way',
relationStr = 'relation';
function changesetUrl(changesetId) {
return url + '/browse/changeset/' + changesetId;
@@ -34,92 +40,99 @@ iD.Connection = function(context) {
}
function getNodes(obj) {
var nelems = obj.getElementsByTagName('nd'), nodes = new Array(nelems.length);
for (var i = 0, l = nelems.length; i < l; i++) {
nodes[i] = 'n' + nelems[i].attributes.ref.nodeValue;
var elems = obj.getElementsByTagName(ndStr),
nodes = new Array(elems.length);
for (var i = 0, l = elems.length; i < l; i++) {
nodes[i] = 'n' + elems[i].attributes.ref.nodeValue;
}
return nodes;
}
function getTags(obj) {
var tags = {}, tagelems = obj.getElementsByTagName('tag');
for (var i = 0, l = tagelems.length; i < l; i++) {
var item = tagelems[i];
tags[item.attributes.k.nodeValue] = item.attributes.v.nodeValue;
var elems = obj.getElementsByTagName(tagStr),
tags = {};
for (var i = 0, l = elems.length; i < l; i++) {
var attrs = elems[i].attributes;
tags[attrs.k.nodeValue] = attrs.v.nodeValue;
}
return tags;
}
function getMembers(obj) {
var elems = obj.getElementsByTagName('member'),
var elems = obj.getElementsByTagName(memberStr),
members = new Array(elems.length);
for (var i = 0, l = elems.length; i < l; i++) {
var attrs = elems[i].attributes;
members[i] = {
id: elems[i].attributes.type.nodeValue[0] + elems[i].attributes.ref.nodeValue,
type: elems[i].attributes.type.nodeValue,
role: elems[i].attributes.role.nodeValue
id: attrs.type.nodeValue[0] + attrs.ref.nodeValue,
type: attrs.type.nodeValue,
role: attrs.role.nodeValue
};
}
return members;
}
function nodeData(obj) {
var o = { type: 'node', tags: getTags(obj) };
for (var i = 0, l = obj.attributes.length; i < l; i++) {
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
}
if (o.lon && o.lat) {
o.loc = [parseFloat(o.lon), parseFloat(o.lat)];
delete o.lon; delete o.lat;
}
o.id = iD.Entity.id.fromOSM('node', o.id);
return new iD.Node(o);
}
var parsers = {
node: function nodeData(obj) {
var attrs = obj.attributes;
return new iD.Node({
id: iD.Entity.id.fromOSM(nodeStr, attrs.id.nodeValue),
loc: [parseFloat(attrs.lon.nodeValue), parseFloat(attrs.lat.nodeValue)],
version: attrs.version.nodeValue,
changeset: attrs.changeset.nodeValue,
user: attrs.user.nodeValue,
uid: attrs.uid.nodeValue,
visible: attrs.visible.nodeValue,
timestamp: attrs.timestamp.nodeValue,
tags: getTags(obj)
});
},
function wayData(obj) {
var o = { type: 'way', nodes: getNodes(obj),
tags: getTags(obj)
};
for (var i = 0, l = obj.attributes.length; i < l; i++) {
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
}
o.id = iD.Entity.id.fromOSM('way', o.id);
return new iD.Way(o);
}
way: function wayData(obj) {
var attrs = obj.attributes;
return new iD.Way({
id: iD.Entity.id.fromOSM(wayStr, attrs.id.nodeValue),
version: attrs.version.nodeValue,
changeset: attrs.changeset.nodeValue,
user: attrs.user.nodeValue,
uid: attrs.uid.nodeValue,
visible: attrs.visible.nodeValue,
timestamp: attrs.timestamp.nodeValue,
tags: getTags(obj),
nodes: getNodes(obj)
});
},
function relationData(obj) {
var o = {
type: 'relation', members: getMembers(obj),
tags: getTags(obj)
};
for (var i = 0, l = obj.attributes.length; i < l; i++) {
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
relation: function relationData(obj) {
var attrs = obj.attributes;
return new iD.Relation({
id: iD.Entity.id.fromOSM(relationStr, attrs.id.nodeValue),
version: attrs.version.nodeValue,
changeset: attrs.changeset.nodeValue,
user: attrs.user.nodeValue,
uid: attrs.uid.nodeValue,
visible: attrs.visible.nodeValue,
timestamp: attrs.timestamp.nodeValue,
tags: getTags(obj),
members: getMembers(obj)
});
}
o.id = iD.Entity.id.fromOSM('relation', o.id);
return new iD.Relation(o);
}
};
function parse(dom) {
if (!dom || !dom.childNodes) return new Error('Bad request');
var root = dom.childNodes[0];
var entities = {};
var root = dom.childNodes[0],
children = root.childNodes,
entities = {};
var i, o, l;
for (i = 0, l = root.childNodes.length; i < l; i++) {
switch(root.childNodes[i].nodeName) {
case 'node':
o = nodeData(root.childNodes[i]);
entities[o.id] = o;
break;
case 'way':
o = wayData(root.childNodes[i]);
entities[o.id] = o;
break;
case 'relation':
o = relationData(root.childNodes[i]);
entities[o.id] = o;
break;
for (i = 0, l = children.length; i < l; i++) {
var child = children[i],
parser = parsers[child.nodeName];
if (parser) {
o = parser(child);
entities[o.id] = o;
}
}
+99
View File
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html>
<head>
<title>Node XML</title>
<style>
#mover {
width:10px;
height:10px;
background:black;
}
#moved {
width:5px;
height:5px;
background:red;
}
pre {
position:absolute;
top:120px;
width:500px;
}
</style>
</head>
<body>
<div id='mover'>
<div id='moved'></div>
</div>
<pre id='report'></pre>
<div style="display: none" id="xml" lat="12.34" lon="12.34" version="0" user="foo">
</div>
<script src="benchmark.js"></script>
<script>
var suite = new Benchmark.Suite;
var obj = document.getElementById("xml");
var fns = {
lon: function(o, v) { o.loc[0] = parseFloat(v); },
lat: function(o, v) { o.loc[1] = parseFloat(v); }
},
def = function(o, v, k) { o[k] = v; };
// add tests
suite
.add('one', function() {
var o = { type: 'node' },
attrs = obj.attributes;
for (var i = 0, l = attrs.length; i < l; i++) {
var attr = attrs[i];
o[attr.nodeName] = attr.nodeValue;
}
if (o.lon && o.lat) {
o.loc = [parseFloat(o.lon), parseFloat(o.lat)];
delete o.lon; delete o.lat;
}
})
.add('two', function() {
var o = { type: 'node', loc: [0, 0] },
attrs = obj.attributes;
for (var i = 0, l = attrs.length; i < l; i++) {
var attr = attrs[i],
k = attr.nodeName,
v = attr.nodeValue;
if (k === 'lon') {
o.loc[0] = parseFloat(v);
} else if (k === 'lat') {
o.loc[1] = parseFloat(v);
} else {
o[k] = v;
}
}
})
.add('three', function() {
var o = { type: 'node', loc: [0, 0] },
attrs = obj.attributes;
for (var i = 0, l = attrs.length; i < l; i++) {
var attr = attrs[i];
(fns[attr.nodeName] || def)(o, attr.nodeValue, attr.nodeName);
}
})
.add('four', function() {
var o = { type: 'node', loc: [0, 0] },
attrs = obj.attributes;
o.id = attrs.id.nodeValue;
o.loc[0] = parseFloat(attrs.lon.nodeValue);
o.loc[1] = parseFloat(attrs.lat.nodeValue);
o.version = attrs.version.nodeValue;
o.user = attrs.user.nodeValue;
})
// add listeners
.on('cycle', function(event) {
document.getElementById('report').innerHTML +=
String(event.target) + '\n';
})
.on('complete', function() {
document.getElementById('report').innerHTML +=
'Fastest is ' + this.filter('fastest').pluck('name') + '\n';
})
// run async
.run({ 'async': true });
</script>
</body>