forked from mapbox/geobuf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add compress function to return object with reduced memory usage
Objects are modified in place, arrays are replaced with an array that only has exactly the amount of capacity needed. This is useful in cases where the polygons will be used for a long time. By default, arrays are reserved with extra capacity that won't be used. (The empty array starts with a capacity of 16 elements by now, which is inefficient for decoded points of length 2) slice() allocates a new array, seemingly with shrunken capacity according to process.memoryUsage. This has an optional option to deduplicate identical points, which may be useful for collections of polygons sharing points as well as for calling compress multiple times with different objects. It's only safe for read-only uses, so it is disabled by default. For example, in node-geo-tz issue 131, I saw this change to memory usage and decoding time on Linux. This is useful for long-running processes that repeatedly use the objects. 1. No Override: 1.280 GB (1.8 seconds) 2. Defaults for cache(no numericArrayCache): 0.708 GB (3.4 seconds) 3. Adding the second Map (numericArrayCache): 0.435 GB (6.7 seconds) Closes mapbox#122
- Loading branch information
1 parent
daad5e0
commit 18f68ab
Showing
4 changed files
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
'use strict'; | ||
|
||
if (typeof Map == 'undefined' || !Object.entries) { | ||
module.exports = function compress(value) { | ||
return value; | ||
}; | ||
return; | ||
} | ||
|
||
/** | ||
* @param {array} value | ||
* @returns {Boolean} is this an array where all fields are numbers (including the empty array). | ||
*/ | ||
function isNumericArray(value) { | ||
for (var i = 0; i < value.length; i++) { | ||
if (typeof(value[i]) !== 'number') { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Compress data returned by geobuf's decode function. | ||
* Objects are modified in place. | ||
* | ||
* This is useful in cases where the polygons will be used for a long time. | ||
* By default, arrays are reserved with extra capacity that won't be used. | ||
* (The empty array starts with a capacity of 16 elements by now, | ||
* which is inefficient for decoded points of length 2) | ||
* | ||
* This has an optional option to deduplicate identical points, | ||
* which may be useful for collections of polygons sharing points as well | ||
* as for calling compress multiple times with different objects. | ||
* | ||
* @param {any} value the value to compress. | ||
* @param {Map} [cache] by default, a new cache is created each time for external calls to compress. | ||
* Must support get/has/set. | ||
* @param {null|Map} [numericArrayCache] if non-null, this will be used to deduplicate | ||
* numeric arrays of any length, including empty arrays. | ||
* | ||
* This deduplication may be unsafe if callers would modify arrays. | ||
* @return {any} value with all fields compressed. | ||
*/ | ||
function compress(value, cache = new Map(), numericArrayCache = null) { | ||
if (cache.has(value)) { | ||
return cache.get(value); | ||
} | ||
if (Array.isArray(value)) { | ||
// By default, v8 allocates an array with a capacity of 16 elements. | ||
// This wastes memory for small arrays such as Points of length 2. | ||
// | ||
// The function slice is used because it was available in older JS versions | ||
// and experimentally appears to reduce capacity used. | ||
var result = value.slice(); | ||
if (numericArrayCache && isNumericArray(result)) { | ||
var cacheKey = JSON.stringify(result); | ||
var cachedEntry = numericArrayCache.get(cacheKey); | ||
if (cachedEntry) { | ||
cache.set(value, cachedEntry); | ||
return cachedEntry; | ||
} | ||
// Reuse array instances such as [], [1.5, 1.5] | ||
numericArrayCache.set(cacheKey, result); | ||
cache.set(value, result); | ||
// Nothing left to compress. | ||
return result; | ||
} | ||
// Store this in the cache immediately to guard against infinite recursion on | ||
// invalid inputs. | ||
cache.set(value, result); | ||
for (var i = 0; i < result.length; i++) { | ||
result[i] = compress(result[i], cache, numericArrayCache); | ||
} | ||
return result; | ||
} else if (value && typeof value === 'object') { | ||
// Compress fields of the object in place. | ||
// Set this to the cache immediately to prevent infinite recursion on invalid data. | ||
cache.set(value, value) | ||
var entries = Object.entries(value); | ||
for (var i = 0; i < entries.length; i++) { | ||
var entry = entries[i]; | ||
var field = entry[1]; | ||
var compressedValue = compress(field, cache, numericArrayCache); | ||
if (field !== compressedValue) { | ||
// Replace object field for this key with the compressed version | ||
value[entry[0]] = compressedValue; | ||
} | ||
} | ||
} else if (typeof value === 'string') { | ||
// Deduplicate strings. | ||
cache.set(value, value); | ||
} | ||
return value; | ||
} | ||
module.exports = compress; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters