-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a minimal global worker pool #2952
Changes from all commits
ffaf8e8
9b887bf
a7d50e8
71c9f6d
63089fe
20bf840
93ba7fd
1747dc6
78540cf
2e65562
ba02e2a
7210aaf
6ac077d
0c687bb
ff7c9e2
13b53ed
1efb12f
ea12906
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
'use strict'; | ||
|
||
var Evented = require('../../js/util/evented'); | ||
var util = require('../../js/util/util'); | ||
var formatNumber = require('../lib/format_number'); | ||
|
||
module.exports = function(options) { | ||
var evented = util.extend({}, Evented); | ||
|
||
var mapsOnPage = 6; | ||
|
||
evented.fire('log', { message: 'Creating ' + mapsOnPage + ' maps' }); | ||
|
||
var loaded = 0; | ||
var start = Date.now(); | ||
for (var i = 0; i < mapsOnPage; i++) { | ||
var map = options.createMap({ | ||
style: { | ||
version: 8, | ||
sources: {}, | ||
layers: [] | ||
} | ||
}); | ||
map.on('load', onload.bind(null, map)); | ||
map.on('error', function (err) { | ||
evented.fire('error', err); | ||
}); | ||
} | ||
|
||
function onload () { | ||
if (++loaded >= mapsOnPage) { | ||
var duration = Date.now() - start; | ||
evented.fire('end', { | ||
message: formatNumber(duration) + ' ms, loaded ' + mapsOnPage + ' maps.', | ||
score: duration | ||
}); | ||
done(); | ||
} | ||
} | ||
|
||
function done () { | ||
} | ||
|
||
return evented; | ||
}; | ||
|
||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
'use strict'; | ||
var WorkerPool = require('./util/worker_pool'); | ||
|
||
var globalWorkerPool; | ||
|
||
/** | ||
* Creates (if necessary) and returns the single, global WorkerPool instance | ||
* to be shared across each Map | ||
* @private | ||
*/ | ||
module.exports = function getGlobalWorkerPool () { | ||
if (!globalWorkerPool) { | ||
globalWorkerPool = new WorkerPool(); | ||
} | ||
return globalWorkerPool; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,34 +15,33 @@ function Worker(self) { | |
this.self = self; | ||
this.actor = new Actor(self, this); | ||
|
||
// simple accessor object for passing to WorkerSources | ||
var styleLayers = { | ||
getLayers: function () { return this.layers; }.bind(this), | ||
getLayerFamilies: function () { return this.layerFamilies; }.bind(this) | ||
}; | ||
this.layers = {}; | ||
this.layerFamilies = {}; | ||
|
||
this.workerSources = { | ||
vector: new VectorTileWorkerSource(this.actor, styleLayers), | ||
geojson: new GeoJSONWorkerSource(this.actor, styleLayers) | ||
this.workerSourceTypes = { | ||
vector: VectorTileWorkerSource, | ||
geojson: GeoJSONWorkerSource | ||
}; | ||
|
||
// [mapId][sourceType] => worker source instance | ||
this.workerSources = {}; | ||
|
||
this.self.registerWorkerSource = function (name, WorkerSource) { | ||
if (this.workerSources[name]) { | ||
if (this.workerSourceTypes[name]) { | ||
throw new Error('Worker source with name "' + name + '" already registered.'); | ||
} | ||
this.workerSources[name] = new WorkerSource(this.actor, styleLayers); | ||
this.workerSourceTypes[name] = WorkerSource; | ||
}.bind(this); | ||
} | ||
|
||
util.extend(Worker.prototype, { | ||
'set layers': function(layers) { | ||
this.layers = {}; | ||
var that = this; | ||
'set layers': function(mapId, layerDefinitions) { | ||
var layers = this.layers[mapId] = {}; | ||
|
||
// Filter layers and create an id -> layer map | ||
var childLayerIndicies = []; | ||
for (var i = 0; i < layers.length; i++) { | ||
var layer = layers[i]; | ||
for (var i = 0; i < layerDefinitions.length; i++) { | ||
var layer = layerDefinitions[i]; | ||
if (layer.type === 'fill' || layer.type === 'line' || layer.type === 'circle' || layer.type === 'symbol') { | ||
if (layer.ref) { | ||
childLayerIndicies.push(i); | ||
|
@@ -54,74 +53,75 @@ util.extend(Worker.prototype, { | |
|
||
// Create an instance of StyleLayer per layer | ||
for (var j = 0; j < childLayerIndicies.length; j++) { | ||
setLayer(layers[childLayerIndicies[j]]); | ||
setLayer(layerDefinitions[childLayerIndicies[j]]); | ||
} | ||
|
||
function setLayer(serializedLayer) { | ||
var styleLayer = StyleLayer.create( | ||
serializedLayer, | ||
serializedLayer.ref && that.layers[serializedLayer.ref] | ||
serializedLayer.ref && layers[serializedLayer.ref] | ||
); | ||
styleLayer.updatePaintTransitions({}, {transition: false}); | ||
that.layers[styleLayer.id] = styleLayer; | ||
layers[styleLayer.id] = styleLayer; | ||
} | ||
|
||
this.layerFamilies = createLayerFamilies(this.layers); | ||
this.layerFamilies[mapId] = createLayerFamilies(this.layers[mapId]); | ||
}, | ||
|
||
'update layers': function(layers) { | ||
var that = this; | ||
'update layers': function(mapId, layerDefinitions) { | ||
var id; | ||
var layer; | ||
|
||
var layers = this.layers[mapId]; | ||
|
||
// Update ref parents | ||
for (id in layers) { | ||
layer = layers[id]; | ||
for (id in layerDefinitions) { | ||
layer = layerDefinitions[id]; | ||
if (layer.ref) updateLayer(layer); | ||
} | ||
|
||
// Update ref children | ||
for (id in layers) { | ||
layer = layers[id]; | ||
for (id in layerDefinitions) { | ||
layer = layerDefinitions[id]; | ||
if (!layer.ref) updateLayer(layer); | ||
} | ||
|
||
function updateLayer(layer) { | ||
var refLayer = that.layers[layer.ref]; | ||
if (that.layers[layer.id]) { | ||
that.layers[layer.id].set(layer, refLayer); | ||
var refLayer = layers[layer.ref]; | ||
if (layers[layer.id]) { | ||
layers[layer.id].set(layer, refLayer); | ||
} else { | ||
that.layers[layer.id] = StyleLayer.create(layer, refLayer); | ||
layers[layer.id] = StyleLayer.create(layer, refLayer); | ||
} | ||
that.layers[layer.id].updatePaintTransitions({}, {transition: false}); | ||
layers[layer.id].updatePaintTransitions({}, {transition: false}); | ||
} | ||
|
||
this.layerFamilies = createLayerFamilies(this.layers); | ||
this.layerFamilies[mapId] = createLayerFamilies(this.layers[mapId]); | ||
}, | ||
|
||
'load tile': function(params, callback) { | ||
'load tile': function(mapId, params, callback) { | ||
var type = params.type || 'vector'; | ||
this.workerSources[type].loadTile(params, callback); | ||
this.getWorkerSource(mapId, type).loadTile(params, callback); | ||
}, | ||
|
||
'reload tile': function(params, callback) { | ||
'reload tile': function(mapId, params, callback) { | ||
var type = params.type || 'vector'; | ||
this.workerSources[type].reloadTile(params, callback); | ||
this.getWorkerSource(mapId, type).reloadTile(params, callback); | ||
}, | ||
|
||
'abort tile': function(params) { | ||
'abort tile': function(mapId, params) { | ||
var type = params.type || 'vector'; | ||
this.workerSources[type].abortTile(params); | ||
this.getWorkerSource(mapId, type).abortTile(params); | ||
}, | ||
|
||
'remove tile': function(params) { | ||
'remove tile': function(mapId, params) { | ||
var type = params.type || 'vector'; | ||
this.workerSources[type].removeTile(params); | ||
this.getWorkerSource(mapId, type).removeTile(params); | ||
}, | ||
|
||
'redo placement': function(params, callback) { | ||
'redo placement': function(mapId, params, callback) { | ||
var type = params.type || 'vector'; | ||
this.workerSources[type].redoPlacement(params, callback); | ||
this.getWorkerSource(mapId, type).redoPlacement(params, callback); | ||
}, | ||
|
||
/** | ||
|
@@ -130,13 +130,28 @@ util.extend(Worker.prototype, { | |
* function taking `(name, workerSourceObject)`. | ||
* @private | ||
*/ | ||
'load worker source': function(params, callback) { | ||
'load worker source': function(map, params, callback) { | ||
try { | ||
this.self.importScripts(params.url); | ||
callback(); | ||
} catch (e) { | ||
callback(e); | ||
} | ||
}, | ||
|
||
getWorkerSource: function(mapId, type) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are source keyed by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are keyed by (mapId, type) pairs. They're keyed by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Dang. I didn't know that. This code is 👍 as written. |
||
if (!this.workerSources[mapId]) | ||
this.workerSources[mapId] = {}; | ||
if (!this.workerSources[mapId][type]) { | ||
// simple accessor object for passing to WorkerSources | ||
var layers = { | ||
getLayers: function () { return this.layers[mapId]; }.bind(this), | ||
getLayerFamilies: function () { return this.layerFamilies[mapId]; }.bind(this) | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you use an accessor object instead of a simpler There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah - I think this is a holdover: before this change, |
||
this.workerSources[mapId][type] = new this.workerSourceTypes[type](this.actor, layers); | ||
} | ||
|
||
return this.workerSources[mapId][type]; | ||
} | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written, this benchmark is highly dependent on the tester's network speed. Can you think of a way to remove that dependency? What are we testing in this benchmark that isn't captured by the
buffer
benchmark?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. My intent in this benchmark was to measure the impact of sending all the worker code to each of the worker threads on the map load time. We can eliminate any outside network use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the benchmark to use an empty style, so it should now not depend at all on network speed