Skip to content

Commit

Permalink
track and dirty cache
Browse files Browse the repository at this point in the history
  • Loading branch information
sgratzl committed Dec 9, 2018
1 parent b1bcae1 commit c7f4741
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 24 deletions.
8 changes: 4 additions & 4 deletions src/model/Column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ export default class Column extends AEventDispatcher {
return Column.DEFAULT_COLOR;
}

toCompareValue(_row: IDataRow): ICompareValue | ICompareValue[] {
toCompareValue(_row: IDataRow, _valueCache?: any): ICompareValue | ICompareValue[] {
return 0;
}

Expand All @@ -551,11 +551,11 @@ export default class Column extends AEventDispatcher {
* @param _row
* @return {IGroup}
*/
group(_row: IDataRow): IGroup {
group(_row: IDataRow, _valueCache?: any): IGroup {
return defaultGroup;
}

toCompareGroupValue(_rows: ISequence<IDataRow>, group: IGroup): ICompareValue | ICompareValue[] {
toCompareGroupValue(_rows: ISequence<IDataRow>, group: IGroup, _valueCache?: ISequence<any>): ICompareValue | ICompareValue[] {
return group.name.toLowerCase();
}

Expand All @@ -576,7 +576,7 @@ export default class Column extends AEventDispatcher {
* @param row
* @return {boolean}
*/
filter(row: IDataRow) {
filter(row: IDataRow, _valueCache?: any) {
return row != null;
}

Expand Down
75 changes: 70 additions & 5 deletions src/provider/LocalDataProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {ISequence, lazySeq} from '../internal/interable';
import Column, {defaultGroup, IColumnDesc, ICompareValue, IDataRow, IGroup, IndicesArray, INumberColumn, IOrderedGroup, mapIndices} from '../model';
import Column, {defaultGroup, IColumnDesc, ICompareValue, IDataRow, IGroup, IndicesArray, INumberColumn, IOrderedGroup, mapIndices, CompositeColumn} from '../model';
import Ranking, {EDirtyReason} from '../model/Ranking';
import ACommonDataProvider from './ACommonDataProvider';
import ADataProvider from './ADataProvider';
import {IDataProviderOptions} from './interfaces';
import {createIndexArray, CompareLookup, ISortWorker, local, sortComplex, WorkerSortWorker} from './sort';
import {DirectRenderTasks, IRenderTaskExectutor, ScheduleRenderTasks} from './tasks';
import {IEventContext} from '../internal/AEventDispatcher';


export interface ILocalDataProviderOptions {
Expand Down Expand Up @@ -148,22 +149,86 @@ export default class LocalDataProvider extends ACommonDataProvider {
}

cloneRanking(existing?: Ranking) {
const clone = super.cloneRanking(existing);
const ranking = super.cloneRanking(existing);

if (this.options.filterGlobally) {
clone.on(`${Ranking.EVENT_FILTER_CHANGED}.reorderAll`, this.reorderAll);
ranking.on(`${Ranking.EVENT_FILTER_CHANGED}.reorderAll`, this.reorderAll);
}

// TODO cached summaries, data from the old one or trigger
this.trackRanking(ranking, existing);
return ranking;
}

private trackRanking(ranking: Ranking, existing?: Ranking) {

const that = this;
ranking.on(`${Column.EVENT_DIRTY_CACHES}.cache`, function (this: IEventContext) {
let col: any = this.origin;
while (col instanceof Column) {
that.tasks.dirtyColumn(col, 'data');
that.tasks.preComputeCol(col);
col = col.parent;
}
});

const cols = ranking.flatColumns;
const addKey = `${Ranking.EVENT_ADD_COLUMN}.cache`;
const removeKey = `${Ranking.EVENT_REMOVE_COLUMN}.cache`;

const addCol = (col: Column) => {
this.tasks.preComputeCol(col);
if (col instanceof CompositeColumn) {
col.on(addKey, addCol);
col.on(removeKey, removeCol);
}
};

const removeCol = (col: Column) => {
this.tasks.dirtyColumn(col, 'data');
if (col instanceof CompositeColumn) {
col.on(addKey, null);
col.on(removeKey, null);
}
};


ranking.on(addKey, addCol);
ranking.on(removeKey, removeCol);
for (const col of cols) {
if (col instanceof CompositeColumn) {
col.on(addKey, addCol);
col.on(removeKey, removeCol);
}
}

if (existing) {
const copy = existing.flatColumns;
for (let i = 0; i < cols.length; ++i) {
this.tasks.copyCache(cols[i], copy[i]);
}
}

return clone;
this.tasks.preComputeData(ranking);
}

cleanUpRanking(ranking: Ranking) {
if (this.options.filterGlobally) {
ranking.on(`${Ranking.EVENT_FILTER_CHANGED}.reorderAll`, null);
}


const cols = ranking.flatColumns;
const addKey = `${Ranking.EVENT_ADD_COLUMN}.cache`;
const removeKey = `${Ranking.EVENT_REMOVE_COLUMN}.cache`;
ranking.on(addKey, null);
ranking.on(removeKey, null);
for (const col of cols) {
if (col instanceof CompositeColumn) {
col.on(addKey, null);
col.on(removeKey, null);
}
}

this.tasks.dirtyRanking(ranking, 'data');

super.cleanUpRanking(ranking);
Expand Down
104 changes: 89 additions & 15 deletions src/provider/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ export interface IRenderTaskExectutor extends IRenderTasks {
groupCompare(ranking: Ranking, group: IGroup, rows: ISequence<IDataRow>): IRenderTask<ICompareValue[]>;

preCompute(ranking: Ranking, groups: {rows: IndicesArray, group: IGroup}[]): void;
preComputeCol(col: Column): void;
preComputeData(ranking: Ranking): void;
copyData2Summary(ranking: Ranking): void;
copyCache(col: Column, from: Column): void;
}

class MultiIndices {
Expand All @@ -91,8 +94,6 @@ class MultiIndices {
export class ARenderTasks {
protected readonly byIndex = (i: number) => this.data[i];

protected readonly dataCache = new Map<string, any>();

constructor(protected data: IDataRow[] = []) {

}
Expand Down Expand Up @@ -187,18 +188,20 @@ export class ARenderTasks {

export class DirectRenderTasks extends ARenderTasks implements IRenderTaskExectutor {

protected readonly cache = new Map<string, any>();

setData(data: IDataRow[]) {
this.data = data;
this.dataCache.clear();
this.cache.clear();
}


dirtyColumn(col: Column, type: 'data' | 'summary' | 'group') {
const prefix = type === 'group' ? 'summary' : type;
this.dataCache.delete(`${prefix}.${col.id}`);
this.dataCache.delete(`${prefix}.${col.id}.raw`);
this.dataCache.delete(`${prefix}.${col.id}.b`);
this.dataCache.delete(`${prefix}.${col.id}.braw`);
this.cache.delete(`${col.id}:${prefix}`);
this.cache.delete(`${col.id}:${prefix}:raw`);
this.cache.delete(`${col.id}:${prefix}:b`);
this.cache.delete(`${col.id}:${prefix}:braw`);
}

dirtyRanking(ranking: Ranking, type: 'data' | 'summary' | 'group') {
Expand All @@ -215,10 +218,26 @@ export class DirectRenderTasks extends ARenderTasks implements IRenderTaskExectu
// dummy
}

preComputeCol() {
// dummy
}

copyData2Summary() {
// dummy
}

copyCache(col: Column, from: Column) {
const fromPrefix = `${from.id}:`;

for (const key of Array.from(this.cache.keys()).sort()) {
if (!key.startsWith(fromPrefix)) {
continue;
}
const tkey = `${col.id}:${key.slice(fromPrefix.length)}`;
this.cache.set(tkey, this.cache.get(key)!);
}
}

groupCompare(ranking: Ranking, group: IGroup, rows: ISequence<IDataRow>) {
return taskNow(ranking.toGroupCompareValue(rows, group));
}
Expand Down Expand Up @@ -272,15 +291,15 @@ export class DirectRenderTasks extends ARenderTasks implements IRenderTaskExectu
const ranking = col.findMyRanker()!.getOrder();
const data = this.dataNumberStats(col, raw);
return {summary: this.normalizedStatsBuilder(ranking, col, data.hist.length, raw), data};
}, raw ? '.raw' : '');
}, raw ? ':raw' : '');
}

private summaryBoxPlotStatsD(col: Column & INumberColumn, raw?: boolean) {
return this.cached('summary', col, () => {
const ranking = col.findMyRanker()!.getOrder();
const data = this.dataBoxPlotStats(col, raw);
return {summary: this.boxplotBuilder(ranking, col, raw), data};
}, raw ? '.braw' : '.b');
}, raw ? ':braw' : ':b');
}

private summaryCategoricalStatsD(col: Column & ISetColumn) {
Expand All @@ -300,21 +319,21 @@ export class DirectRenderTasks extends ARenderTasks implements IRenderTaskExectu
}

private cached<T>(prefix: string, col: Column, creator: () => T, suffix: string = ''): T {
const key = `${prefix}.${col.id}${suffix}`;
if (this.dataCache.has(key)) {
return this.dataCache.get(key)!;
const key = `${col.id}:${prefix}${suffix}`;
if (this.cache.has(key)) {
return this.cache.get(key)!;
}
const s = creator();
this.dataCache.set(key, s);
this.cache.set(key, s);
return s;
}

dataBoxPlotStats(col: Column & INumberColumn, raw?: boolean) {
return this.cached('data', col, () => this.boxplotBuilder(null, col, raw), raw ? '.braw' : '.b');
return this.cached('data', col, () => this.boxplotBuilder(null, col, raw), raw ? ':braw' : ':b');
}

dataNumberStats(col: Column & INumberColumn, raw?: boolean) {
return this.cached('data', col, () => this.normalizedStatsBuilder(null, col, getNumberOfBins(this.data.length), raw), raw ? '.raw' : '');
return this.cached('data', col, () => this.normalizedStatsBuilder(null, col, getNumberOfBins(this.data.length), raw), raw ? ':raw' : '');
}

dataCategoricalStats(col: Column & ISetColumn) {
Expand Down Expand Up @@ -439,6 +458,49 @@ export class ScheduleRenderTasks extends ARenderTasks implements IRenderTaskExec
}
}

preComputeCol(col: Column) {
const ranking = col.findMyRanker();

if (isCategoricalLikeColumn(col)) {
this.dataCategoricalStats(col);
if (!ranking) {
return;
}
this.summaryCategoricalStats(col);
for (const group of ranking.getGroups()) {
this.groupCategoricalStats(col, group);
}
return;
}

if (isNumberColumn(col)) {
this.dataNumberStats(col);

if (!ranking) {
return;
}
this.summaryNumberStats(col);
for (const group of ranking.getGroups()) {
this.groupNumberStats(col, group);
}
return;
}

if (!isDateColumn(col)) {
return;
}

this.dataDateStats(col);

if (!ranking) {
return;
}
this.summaryDateStats(col);
for (const group of ranking.getGroups()) {
this.groupDateStats(col, group);
}
}

copyData2Summary(ranking: Ranking) {
for (const col of ranking.flatColumns) {
if (isCategoricalLikeColumn(col)) {
Expand All @@ -455,6 +517,18 @@ export class ScheduleRenderTasks extends ARenderTasks implements IRenderTaskExec
}
}

copyCache(col: Column, from: Column) {
const fromPrefix = `${from.id}:`;

for (const key of Array.from(this.cache.keys()).sort()) {
if (!key.startsWith(fromPrefix)) {
continue;
}
const tkey = `${col.id}:${key.slice(fromPrefix.length)}`;
this.chainCopy(tkey, this.cache.get(key)!, (data: any) => data);
}
}

groupCompare(ranking: Ranking, group: IGroup, rows: ISequence<IDataRow>) {
return taskLater(this.tasks.push(`r${ranking.id}:${group.name}`, () => ranking.toGroupCompareValue(rows, group)));
}
Expand Down

0 comments on commit c7f4741

Please sign in to comment.