Skip to content
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

refacto(cache): ditch Map() in Cache for simple tree #1264

Merged
merged 2 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Core/Prefab/TileBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ export default function newTileGeometry(builder, params) {
const { sharableExtent, quaternion, position } = builder.computeSharableExtent(params.extent);
const south = sharableExtent.south.toFixed(6);
const bufferKey = `${builder.projection}_${params.disableSkirt ? 0 : 1}_${params.segment}`;
const geometryKey = `${bufferKey}_${params.level}_${south}`;
let promiseGeometry = Cache.get(geometryKey);
let promiseGeometry = Cache.get(bufferKey, params.level, south);

// build geometry if doesn't exist
if (!promiseGeometry) {
let resolve;
promiseGeometry = new Promise((r) => { resolve = r; });
Cache.set(geometryKey, promiseGeometry);
Cache.set(promiseGeometry, Cache.POLICIES.INFINITE, bufferKey, params.level, south);

params.extent = sharableExtent;
params.center = builder.center(params.extent).clone();
Expand Down Expand Up @@ -49,7 +48,7 @@ export default function newTileGeometry(builder, params) {
geometry._count--;
if (geometry._count == 0) {
THREE.BufferGeometry.prototype.dispose.call(geometry);
Cache.delete(bufferKey);
Cache.delete(bufferKey, params.level, south);
}
};
resolve(geometry);
Expand Down
133 changes: 85 additions & 48 deletions src/Core/Scheduler/Cache.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const data = new Map();
const stats = new Map();
let data = {};
let entry;

/**
* This is a copy of the Map object, except that it also store a value for last
Expand All @@ -13,14 +13,16 @@ const stats = new Map();
* @example
* import Cache from './Cache';
*
* Cache.set('foo', { bar: 1 }, Cache.POLICIES.TEXTURE);
* Cache.set('acme', { bar: 32 });
* Cache.set({ bar: 1 }, Cache.POLICIES.TEXTURE, 'foo');
* Cache.set({ bar: 32 }, Cache.POLICIES.INFINITE, 'foo', 'toto');
*
* Cache.get('foo');
*
* Cache.delete('foo');
*
* Cache.clear();
*
* Cache.flush();
*/
const Cache = {
/**
Expand Down Expand Up @@ -49,44 +51,73 @@ const Cache = {
* @name module:Cache.get
* @function
*
* @param {string} key
* @param {string} key1
* @param {string} [key2]
* @param {string} [key3]
*
* @return {Object}
*/
get: (key) => {
const entry = data.get(key);
const stat = stats.get(key) || stats.set(key, { hit: 0, miss: 0 });
get: (key1, key2, key3) => {
if (data[key1] == undefined) {
// eslint-disable-next-line
return;
} else if (data[key1][key2] == undefined) {
entry = data[key1];
} else if (data[key1][key2][key3] == undefined) {
entry = data[key1][key2];
} else {
entry = data[key1][key2][key3];
}

if (entry) {
stat.hit++;
if (entry.value) {
entry.lastTimeUsed = Date.now();
return entry.value;
}

stat.miss++;
},

/**
* Adds or updates an entry with a specified key. A lifetime can be added,
* by specifying a numerical value or using the {@link Cache.POLICIES}
* values. By default an entry has an infinite lifetime.
* Adds or updates an entry with specified keys (up to 3). A lifetime can be
* added, by specifying a numerical value or using the {@link
* Cache.POLICIES} values. By default an entry has an infinite lifetime.
* Caution: it overrides any existing entry already set at this/those key/s.
*
* @name module:Cache.set
* @function
*
* @param {string} key
* @param {Object} value
* @param {number} [lifetime]
* @param {number} lifetime
* @param {string} key1
* @param {string} [key2]
* @param {string} [key3]
*
* @return {Object} the added value
*/
set: (key, value, lifetime = Infinity) => {
const entry = {
set: (value, lifetime, key1, key2, key3) => {
entry = {
value,
lastTimeUsed: Date.now(),
lifetime,
};
data.set(key, entry);

if (key2 == undefined) {
data[key1] = entry;
return value;
}

if (!data[key1]) {
data[key1] = {};
}

if (key3 == undefined) {
data[key1][key2] = entry;
return value;
}

if (!data[key1][key2]) {
data[key1][key2] = {};
}

data[key1][key2][key3] = entry;

return value;
},
Expand All @@ -97,19 +128,31 @@ const Cache = {
* @name module:Cache.delete
* @function
*
* @param {string} key
*
* @return {boolean} - Confirmation that the entry has been deleted.
* @param {string} key1
* @param {string} [key2]
* @param {string} [key3]
*/
delete: key => data.delete(key),
delete: (key1, key2, key3) => {
if (data[key1] == undefined) {
throw Error('Please specify at least a key of something to delete');
} else if (data[key1][key2] == undefined) {
delete data[key1];
} else if (data[key1][key2][key3] == undefined) {
delete data[key1][key2];
} else {
delete data[key1][key2][key3];
}
},

/**
* Removes all entries of the cache.
*
* @name module:Cache.clear
* @function
*/
clear: data.clear(),
clear: () => {
data = {};
},

/**
* Flush the cache: entries that have been present for too long since the
Expand All @@ -121,32 +164,26 @@ const Cache = {
* @name module:Cache.flush
* @function
*
* @param {number} [time]
*
* @return {Object} Statistics about the flush: `before` gives the number of
* entries before flushing, `after` the number after flushing, `hit` the
* number of total successful hit on resources in the cache, and `miss` the
* number of failed hit. The hit and miss are based since the last flush,
* and are reset on every flush.
* @param {number} [time=Date.now()]
*/
flush: (time = Date.now()) => {
const before = data.size;

data.forEach((entry, key) => {
if (entry.lifetime < time - entry.lastTimeUsed) {
data.delete(key);
for (const i in data) {
if (data[i].lifetime < time - data[i].lastTimeUsed) {
delete data[i];
} else {
for (const j in data[i]) {
if (data[i][j].lifetime < time - data[i][j].lastTimeUsed) {
delete data[i][j];
} else {
for (const k in data[i][j]) {
if (data[i][j][k].lifetime < time - data[i][j][k].lastTimeUsed) {
delete data[i][j][k];
}
}
}
}
}
});

let hit = 0;
let miss = 0;
stats.forEach((stat) => {
hit += stat.hit;
miss += stat.miss;
});
stats.clear();

return { before, after: data.size, hit, miss };
}
},
};

Expand Down
5 changes: 2 additions & 3 deletions src/Provider/DataSourceProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ export default {

// Tag to Cache data
const exTag = source.isVectorSource ? extentsDestination[i] : extSource;
const tag = `${source.uid}-${exTag.toString('-')}`;

// Get converted source data, in cache
let convertedSourceData = Cache.get(tag);
let convertedSourceData = Cache.get(source.uid, layer.id, exTag.toString('-'));

// If data isn't in cache
if (!convertedSourceData) {
Expand All @@ -85,7 +84,7 @@ export default {
.then(parsedData => layer.convert(parsedData, extDest, layer), err => error(err, source));
}
// Put converted data in cache
Cache.set(tag, convertedSourceData, Cache.POLICIES.TEXTURE);
Cache.set(convertedSourceData, Cache.POLICIES.TEXTURE, source.uid, layer.id, exTag.toString('-'));
}

// Verify some command is resolved
Expand Down