From 1cf7e1e5cde8f4d660179a527b4706b6d32c1e1a Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 13 Dec 2018 14:23:04 -0800 Subject: [PATCH] Hack in pool for ArrayBuffers used by StructArray. --- src/data/bucket/symbol_bucket.js | 18 ++++++++++ src/source/worker_tile.js | 8 +++++ src/util/struct_array.js | 60 ++++++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index d03e8302fb2..11d470504af 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -132,6 +132,15 @@ class SymbolBuffers { this.placedSymbolArray = new PlacedSymbolArray(); } + releaseBuffer() { + const keys = Object.keys(this); + for (const key of keys) { + if (this[key] && this[key].releaseBuffer) { + this[key].releaseBuffer(); + } + } + } + upload(context: Context, dynamicIndexBuffer: boolean, upload?: boolean, update?: boolean) { if (upload) { this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, symbolLayoutAttributes.members); @@ -183,6 +192,15 @@ class CollisionBuffers { this.collisionVertexArray = new CollisionVertexArray(); } + releaseBuffer() { + const keys = Object.keys(this); + for (const key of keys) { + if (this[key] && this[key].releaseBuffer) { + this[key].releaseBuffer(); + } + } + } + upload(context: Context) { this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, this.layoutAttributes); this.indexBuffer = context.createIndexBuffer(this.indexArray); diff --git a/src/source/worker_tile.js b/src/source/worker_tile.js index e70e0a2b9f2..8831d73ad61 100644 --- a/src/source/worker_tile.js +++ b/src/source/worker_tile.js @@ -204,6 +204,14 @@ class WorkerTile { iconMap: this.returnDependencies ? iconMap : null, glyphPositions: this.returnDependencies ? glyphAtlas.positions : null }); + values(buckets).filter(b => b.isEmpty()).forEach(emptyBucket => { + const bucketKeys = Object.keys(emptyBucket); + for (const key of bucketKeys) { + if (emptyBucket[key] && emptyBucket[key].releaseBuffer) { + emptyBucket[key].releaseBuffer(); + } + } + }); } } } diff --git a/src/util/struct_array.js b/src/util/struct_array.js index 9bed2979a7d..53c6bf88448 100644 --- a/src/util/struct_array.js +++ b/src/util/struct_array.js @@ -48,6 +48,16 @@ class Struct { const DEFAULT_CAPACITY = 128; const RESIZE_MULTIPLIER = 5; +let resizeCount = 0; +let reuseCount = 0; +let sliceCount = 0; +let noSliceCount = 0; +let sizeCounts = {}; +let bufferPool = {}; + +let constructedTypes = {}; +let transferedTypes = {}; + export type StructArrayMember = { name: string, type: ViewType, @@ -140,10 +150,25 @@ class StructArray { * Resize the array to discard unused capacity. */ _trim() { + transferedTypes[this.constructor.name] = true; if (this.length !== this.capacity) { + const prevSize = this.capacity * this.bytesPerElement; + if (!bufferPool[prevSize]) { + bufferPool[prevSize] = []; + } + bufferPool[prevSize].push(this.arrayBuffer); this.capacity = this.length; this.arrayBuffer = this.arrayBuffer.slice(0, this.length * this.bytesPerElement); + sliceCount++; + if (sliceCount % 1000 === 0) { + console.log(`${sliceCount} slices on ${resizeCount} allocations and ${reuseCount} buffers reused from pool, with ${noSliceCount} not requiring slice.`); + // for (const size in sizeCounts) { + // console.log(`${size} allocated ${sizeCounts[size]} times`); + // } + } this._refreshViews(); + } else { + noSliceCount++; } } @@ -173,8 +198,29 @@ class StructArray { */ reserve(n: number) { if (n > this.capacity) { - this.capacity = Math.max(n, Math.floor(this.capacity * RESIZE_MULTIPLIER), DEFAULT_CAPACITY); - this.arrayBuffer = new ArrayBuffer(this.capacity * this.bytesPerElement); + constructedTypes[this.constructor.name] = true; + const prevSize = this.capacity * this.bytesPerElement; + const exponent = Math.max(0, Math.ceil(Math.log(n * this.bytesPerElement / DEFAULT_CAPACITY) / Math.log(RESIZE_MULTIPLIER))); + const newSize = DEFAULT_CAPACITY * Math.pow(RESIZE_MULTIPLIER, exponent);//Math.max(n, Math.floor(this.capacity * RESIZE_MULTIPLIER), DEFAULT_CAPACITY); + this.capacity = newSize / this.bytesPerElement; + if (!sizeCounts[newSize]) { + sizeCounts[newSize] = 0; + } + sizeCounts[newSize]++; + if (prevSize > 0) { + if (!bufferPool[prevSize]) { + bufferPool[prevSize] = []; + } + bufferPool[prevSize].push(this.arrayBuffer); + } + + if (bufferPool[newSize] && bufferPool[newSize].length > 0) { + reuseCount++; + this.arrayBuffer = bufferPool[newSize].pop(); + } else { + resizeCount++; + this.arrayBuffer = new ArrayBuffer(this.capacity * this.bytesPerElement); + } const oldUint8Array = this.uint8; this._refreshViews(); @@ -182,6 +228,16 @@ class StructArray { } } + releaseBuffer() { + const prevSize = this.capacity * this.bytesPerElement; + if (prevSize > 0) { + if (!bufferPool[prevSize]) { + bufferPool[prevSize] = []; + } + bufferPool[prevSize].push(this.arrayBuffer); + } + } + /** * Create TypedArray views for the current ArrayBuffer. */