diff --git a/bench/benchmarks.js b/bench/benchmarks.js index 83ee21fd21b..048dc6502e3 100644 --- a/bench/benchmarks.js +++ b/bench/benchmarks.js @@ -17,6 +17,7 @@ function register(Benchmark) { import Layout from './benchmarks/layout'; import LayoutDDS from './benchmarks/layout_dds'; +import WorkerTransfer from './benchmarks/worker_transfer'; import Paint from './benchmarks/paint'; import PaintStates from './benchmarks/paint_states'; import LayerBenchmarks from './benchmarks/layers'; @@ -31,6 +32,7 @@ import FilterEvaluate from './benchmarks/filter_evaluate'; register(Layout); register(LayoutDDS); +register(WorkerTransfer); register(Paint); register(PaintStates); LayerBenchmarks.forEach(register); diff --git a/bench/benchmarks/layout.js b/bench/benchmarks/layout.js index f6860c1d753..6763b296f0d 100644 --- a/bench/benchmarks/layout.js +++ b/bench/benchmarks/layout.js @@ -1,134 +1,44 @@ // @flow import Benchmark from '../lib/benchmark'; - -import createStyle from '../lib/create_style'; -import VT from '@mapbox/vector-tile'; -import Protobuf from 'pbf'; -import assert from 'assert'; -import promisify from 'pify'; -import WorkerTile from '../../src/source/worker_tile'; -import StyleLayerIndex from '../../src/style/style_layer_index'; -import deref from '../../src/style-spec/deref'; +import fetchStyle from '../lib/fetch_style'; +import TileParser from '../lib/tile_parser'; import { OverscaledTileID } from '../../src/source/tile_id'; -import { normalizeStyleURL, normalizeSourceURL, normalizeTileURL } from '../../src/util/mapbox'; - -import type {TileJSON} from '../../src/types/tilejson'; -import type {StyleSpecification} from '../../src/style-spec/types'; -// Note: this class is extended in turn by the LayoutDDS benchmark. export default class Layout extends Benchmark { - glyphs: Object; - icons: Object; - workerTile: WorkerTile; - layerIndex: StyleLayerIndex; tiles: Array<{tileID: OverscaledTileID, buffer: ArrayBuffer}>; + parser: TileParser; - tileIDs(): Array { - return [ + setup(): Promise { + const tileIDs = [ new OverscaledTileID(12, 0, 12, 655, 1583), new OverscaledTileID(8, 0, 8, 40, 98), new OverscaledTileID(4, 0, 4, 3, 6), new OverscaledTileID(0, 0, 0, 0, 0) ]; - } - - sourceID(): string { - return 'composite'; - } - - fetchStyle(): Promise { - return fetch(normalizeStyleURL(`mapbox://styles/mapbox/streets-v9`)) - .then(response => response.json()); - } - - fetchTiles(styleJSON: StyleSpecification): Promise> { - const sourceURL: string = (styleJSON.sources[this.sourceID()]: any).url; - return fetch(normalizeSourceURL(sourceURL)) - .then(response => response.json()) - .then((tileJSON: TileJSON) => { - return Promise.all(this.tileIDs().map(tileID => { - return fetch((normalizeTileURL(tileID.canonical.url(tileJSON.tiles)))) - .then(response => response.arrayBuffer()) - .then(buffer => ({tileID, buffer})); - })); - }); - } - - setup(): Promise { - return this.fetchStyle() + return fetchStyle(`mapbox://styles/mapbox/streets-v9`) .then((styleJSON) => { - this.layerIndex = new StyleLayerIndex(deref(styleJSON.layers)); - return Promise.all([createStyle(styleJSON), this.fetchTiles(styleJSON)]); + this.parser = new TileParser(styleJSON, 'composite'); + return this.parser.setup(); }) - .then(([style, tiles]) => { + .then(() => { + return Promise.all(tileIDs.map(tileID => this.parser.fetchTile(tileID))); + }) + .then((tiles) => { this.tiles = tiles; - this.glyphs = {}; - this.icons = {}; - - const preloadGlyphs = (params, callback) => { - style.getGlyphs('', params, (err, glyphs) => { - this.glyphs[JSON.stringify(params)] = glyphs; - callback(err, glyphs); - }); - }; - - const preloadImages = (params, callback) => { - style.getImages('', params, (err, icons) => { - this.icons[JSON.stringify(params)] = icons; - callback(err, icons); - }); - }; - - return this.bench(preloadGlyphs, preloadImages); - }); + // parse tiles once to populate glyph/icon cache + return Promise.all(tiles.map(tile => this.parser.parseTile(tile))); + }) + .then(() => {}); } - bench(getGlyphs: Function = (params, callback) => callback(null, this.glyphs[JSON.stringify(params)]), - getImages: Function = (params, callback) => callback(null, this.icons[JSON.stringify(params)])) { - - const actor = { - send(action, params, callback) { - setTimeout(() => { - if (action === 'getImages') { - getImages(params, callback); - } else if (action === 'getGlyphs') { - getGlyphs(params, callback); - } else assert(false); - }, 0); - } - }; - - let promise: Promise = Promise.resolve(); - - for (const {tileID, buffer} of this.tiles) { + bench() { + let promise = Promise.resolve(); + for (const tile of this.tiles) { promise = promise.then(() => { - const workerTile = new WorkerTile({ - tileID: tileID, - zoom: tileID.overscaledZ, - tileSize: 512, - overscaling: 1, - showCollisionBoxes: false, - source: this.sourceID(), - uid: '0', - maxZoom: 22, - pixelRatio: 1, - request: { - url: '' - }, - angle: 0, - pitch: 0, - cameraToCenterDistance: 0, - cameraToTileDistance: 0 - }); - - const tile = new VT.VectorTile(new Protobuf(buffer)); - const parse = promisify(workerTile.parse.bind(workerTile)); - - return parse(tile, this.layerIndex, actor); + return this.parser.parseTile(tile).then(() => {}); }); } - return promise; } } diff --git a/bench/benchmarks/layout_dds.js b/bench/benchmarks/layout_dds.js index 0751faf96fa..27979d4e36f 100644 --- a/bench/benchmarks/layout_dds.js +++ b/bench/benchmarks/layout_dds.js @@ -1,26 +1,21 @@ // @flow -import Layout from './layout'; - +import Benchmark from '../lib/benchmark'; +import TileParser from '../lib/tile_parser'; import { OverscaledTileID } from '../../src/source/tile_id'; -import type {StyleSpecification} from '../../src/style-spec/types'; - const LAYER_COUNT = 2; -export default class LayoutDDS extends Layout { - tileIDs(): Array { - return [ +export default class LayoutDDS extends Benchmark { + tiles: Array<{tileID: OverscaledTileID, buffer: ArrayBuffer}>; + parser: TileParser; + + setup(): Promise { + const tileIDs = [ new OverscaledTileID(15, 0, 15, 9373, 12535) ]; - } - sourceID(): string { - return 'mapbox'; - } - - fetchStyle(): Promise { - const style = { + const styleJSON = { "version": 8, "sources": { "mapbox": { "type": "vector", "url": "mapbox://mapbox.mapbox-streets-v7" } @@ -85,14 +80,34 @@ export default class LayoutDDS extends Layout { } ]; - while (style.layers.length < LAYER_COUNT) { + while (styleJSON.layers.length < LAYER_COUNT) { for (const layer of layers) { - style.layers.push(Object.assign(({}: any), layer, { - id: layer.id + style.layers.length + styleJSON.layers.push(Object.assign(({}: any), layer, { + id: layer.id + styleJSON.layers.length })); } } - return Promise.resolve(style); + this.parser = new TileParser(styleJSON, 'mapbox'); + return this.parser.setup() + .then(() => { + return Promise.all(tileIDs.map(tileID => this.parser.fetchTile(tileID))); + }) + .then((tiles) => { + this.tiles = tiles; + // parse tiles once to populate glyph/icon cache + return Promise.all(tiles.map(tile => this.parser.parseTile(tile))); + }) + .then(() => {}); + } + + bench() { + let promise = Promise.resolve(); + for (const tile of this.tiles) { + promise = promise.then(() => { + return this.parser.parseTile(tile).then(() => {}); + }); + } + return promise; } } diff --git a/bench/benchmarks/worker_transfer.js b/bench/benchmarks/worker_transfer.js new file mode 100644 index 00000000000..ecaa3894590 --- /dev/null +++ b/bench/benchmarks/worker_transfer.js @@ -0,0 +1,81 @@ +// @flow + +import Benchmark from '../lib/benchmark'; +import fetchStyle from '../lib/fetch_style'; +import TileParser from '../lib/tile_parser'; +import { OverscaledTileID } from '../../src/source/tile_id'; +import { serialize, deserialize } from '../../src/util/web_worker_transfer'; +import { values } from '../../src/util/util'; + +export default class WorkerTransfer extends Benchmark { + parser: TileParser; + payloadTiles: Array; + payloadJSON: Array; + worker: Worker; + + setup(): Promise { + const src = ` + onmessage = (e) => { + postMessage(e.data); + }; + `; + const url = window.URL.createObjectURL(new Blob([src], {type: 'text/javascript'})); + this.worker = new Worker(url); + + const tileIDs = [ + new OverscaledTileID(8, 0, 8, 73, 97), + new OverscaledTileID(11, 0, 11, 585, 783), + new OverscaledTileID(11, 0, 11, 596, 775), + new OverscaledTileID(13, 0, 13, 2412, 3079) + ]; + + return fetchStyle(`mapbox://styles/mapbox/streets-v9`) + .then((styleJSON) => { + this.parser = new TileParser(styleJSON, 'composite'); + return this.parser.setup(); + }) + .then(() => { + return Promise.all(tileIDs.map(tileID => this.parser.fetchTile(tileID))); + }) + .then((tiles) => { + return Promise.all(tiles.map(tile => this.parser.parseTile(tile))); + }).then((tileResults) => { + const payload = tileResults + .concat(values(this.parser.icons)) + .concat(values(this.parser.glyphs)).map((obj) => serialize(obj, [])); + this.payloadJSON = payload.map(barePayload); + this.payloadTiles = payload.slice(0, tileResults.length); + }); + } + + sendPayload(obj: any) { + return new Promise((resolve) => { + this.worker.onmessage = () => resolve(); + this.worker.postMessage(obj); + }); + } + + bench(): Promise { + let promise: Promise = Promise.resolve(); + + // benchmark sending raw JSON payload + for (const obj of this.payloadJSON) { + promise = promise.then(() => { + return this.sendPayload(obj); + }); + } + + return promise.then(() => { + // benchmark deserializing full tile payload because it happens on the main thread + for (const obj of this.payloadTiles) { + deserialize(obj); + } + }); + } +} + +function barePayload(obj) { + // strip all transferables from a worker payload, because we can't transfer them repeatedly in the bench: + // as soon as it's transfered once, it's no longer available on the main thread + return JSON.parse(JSON.stringify(obj, (key, value) => ArrayBuffer.isView(value) ? {} : value)); +} diff --git a/bench/lib/create_style.js b/bench/lib/create_style.js deleted file mode 100644 index 622247c0c8a..00000000000 --- a/bench/lib/create_style.js +++ /dev/null @@ -1,24 +0,0 @@ -// @flow - -import Style from '../../src/style/style'; - -import { Evented } from '../../src/util/evented'; - -import type {StyleSpecification} from '../../src/style-spec/types'; - -class StubMap extends Evented { - _transformRequest(url) { - return { url }; - } -} - -export default function (styleJSON: StyleSpecification): Promise