Skip to content

Commit

Permalink
optimize
Browse files Browse the repository at this point in the history
  • Loading branch information
sgratzl committed Dec 16, 2018
1 parent 8d13f59 commit 1d682f9
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 70 deletions.
21 changes: 10 additions & 11 deletions src/provider/LocalDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {IDataProviderOptions} from './interfaces';
import {CompareLookup} from './sort';
import {IRenderTaskExectutor} from './tasks';
import {IEventContext} from '../internal/AEventDispatcher';
import {createIndexArray, sortComplex, concat} from '../internal';
import {createIndexArray, sortComplex} from '../internal';
import {DirectRenderTasks} from './DirectRenderTasks';
import {ScheduleRenderTasks} from './ScheduledTasks';
import {joinGroups} from '../model/internal';
Expand Down Expand Up @@ -271,9 +271,8 @@ export default class LocalDataProvider extends ACommonDataProvider {
const groupOrder: ISortHelper[] = [];
let maxDataIndex = -1;

const sortCriteria = ranking.getSortCriteria();
const groupCriteria = ranking.getGroupCriteria();
const lookups = isSortedBy && needsSorting ? new CompareLookup(this._data.length, sortCriteria, (d) => d.toCompareValueType()) : undefined;
const lookups = isSortedBy && needsSorting ? new CompareLookup(this._data.length, true, ranking, this.tasks.valueCache.bind(this.tasks)) : undefined;

const pushGroup = (group: IGroup, r: IDataRow) => {
const groupKey = group.name.toLowerCase();
Expand All @@ -286,12 +285,12 @@ export default class LocalDataProvider extends ACommonDataProvider {
groupOrder.push(s);
};

const sortCaches = sortCriteria.map((c) => this.tasks.valueCache(c.col));
const groupCaches = groupCriteria.map((c) => this.tasks.valueCache(c));
const filterCaches = filter.map((c) => typeof c === 'function' ? undefined : this.tasks.valueCache(c));

const toCompareValue = (r: IDataRow) => concat(sortCriteria.map((d, i) => d.col.toCompareValue(r, sortCaches[i] ? sortCaches[i]!(r.i) : undefined)));
const toGroup = (r: IDataRow) => joinGroups(groupCriteria.map((c, i) => c.group(r, groupCaches[i] ? groupCaches[i]!(r.i) : undefined)));
const toGroup = groupCriteria.length === 1 ?
(r: IDataRow) => groupCriteria[0].group(r, groupCaches[0] ? groupCaches[0]!(r.i) : undefined) :
(r: IDataRow) => joinGroups(groupCriteria.map((c, i) => c.group(r, groupCaches[i] ? groupCaches[i]!(r.i) : undefined)));

if (needsFiltering) {
// filter, group, sort
Expand All @@ -307,7 +306,7 @@ export default class LocalDataProvider extends ACommonDataProvider {
maxDataIndex = r.i;
}
if (lookups) {
lookups.push(r.i, toCompareValue(r));
lookups.push(r);
}
pushGroup(toGroup(r), r);
}
Expand Down Expand Up @@ -339,7 +338,7 @@ export default class LocalDataProvider extends ACommonDataProvider {
maxDataIndex = i;
}
const r = this._dataRows[i];
lookups.push(r.i, toCompareValue(r));
lookups.push(r);
}
continue;
}
Expand All @@ -353,7 +352,7 @@ export default class LocalDataProvider extends ACommonDataProvider {
}
const r = this._dataRows[i];
if (lookups) {
lookups.push(r.i, toCompareValue(r));
lookups.push(r);
}
pushGroup(needsGrouping ? toGroup(r) : plain, r);
}
Expand All @@ -379,7 +378,7 @@ export default class LocalDataProvider extends ACommonDataProvider {

return Promise.all([sortTask, groupSortTask]).then(([order, groupC]) => {
if (groupLookup && Array.isArray(groupC)) {
groupLookup.push(i, groupC);
groupLookup.pushValues(i, groupC);
}
return Object.assign({order}, group);
});
Expand Down Expand Up @@ -462,7 +461,7 @@ export default class LocalDataProvider extends ACommonDataProvider {
});
}

const groupLookup = isGroupedSortedBy && needsGroupSorting ? new CompareLookup(groupOrder.length, ranking.getGroupSortCriteria(), (d) => d.toCompareGroupValueType()) : undefined;
const groupLookup = isGroupedSortedBy && needsGroupSorting ? new CompareLookup(groupOrder.length, false, ranking) : undefined;

return Promise.all(groupOrder.map((g, i) => {
// not required if: group sort criteria changed -> lookups will be none
Expand Down
133 changes: 74 additions & 59 deletions src/provider/sort.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {createIndexArray, ILookUpArray} from '../internal';
import {ECompareValueType, UIntTypedArray, ISortCriteria} from '../model';
import {ECompareValueType, UIntTypedArray, Ranking, IDataRow} from '../model';
import Column, {ICompareValue} from '../model/Column';
import {FIRST_IS_MISSING, FIRST_IS_NAN} from '../model/missing';

Expand Down Expand Up @@ -72,87 +72,102 @@ function toCompareLookUp(rawLength: number, type: ECompareValueType): ILookUpArr
}
}

function createSetter(type: ECompareValueType, lookup: ILookUpArray, missingCount: number) {
return (index: number, v: ICompareValue) => {
switch (type) {
case ECompareValueType.BINARY: // just 0 or 1 -> convert to 0=-Ininity 1 2 255=+Infinity
lookup[index] = v == null || isNaN(<number>v) ? missingBinary : (<number>v) + 1;
break;
case ECompareValueType.COUNT: // uint32
lookup[index] = v == null || isNaN(<number>v) ? missingCount : (<number>v) + 1;
break;
case ECompareValueType.UINT8: // shift by one to have 0 for -Inf
lookup[index] = v == null || isNaN(<number>v) ? missingInt8 : (<number>v) + 1;
break;
case ECompareValueType.UINT16: // shift by one to have 0 for -Inf
lookup[index] = v == null || isNaN(<number>v) ? missingInt16 : (<number>v) + 1;
break;
case ECompareValueType.UINT32: // shift by one to have 0 for -Inf
lookup[index] = v == null || isNaN(<number>v) ? missingInt32 : (<number>v) + 1;
break;
case ECompareValueType.INT8:
lookup[index] = v == null || isNaN(<number>v) ? missingInt8 : (<number>v);
break;
case ECompareValueType.INT16:
lookup[index] = v == null || isNaN(<number>v) ? missingInt16 : (<number>v);
break;
case ECompareValueType.INT32:
lookup[index] = v == null || isNaN(<number>v) ? missingInt32 : (<number>v);
break;
case ECompareValueType.STRING:
lookup[index] = v == null || v === '' ? missingString : v;
break;
case ECompareValueType.FLOAT:
case ECompareValueType.DOUBLE:
lookup[index] = v == null || isNaN(<number>v) ? missingFloat : v;
break;
case ECompareValueType.FLOAT_ASC:
case ECompareValueType.DOUBLE_ASC:
lookup[index] = v == null || isNaN(<number>v) ? missingFloatAsc : v;
break;
}
};
}

export class CompareLookup {
private readonly lookups: ILookUpArray[];
private readonly missingCount: number; // since length dependent
private readonly comparators: {asc: boolean, v: ECompareValueType}[] = [];
private readonly criteria: {col: Column, valueCache?(dataIndex: number): any}[] = [];
private readonly data: {asc: boolean, v: ECompareValueType, lookup: ILookUpArray, setter: (dataIndex: number, value: ICompareValue) => void}[] = [];

constructor(rawLength: number, isSorting: boolean, ranking: Ranking, valueCaches?: (col: Column) => (undefined | ((i: number) => any))) {
const missingCount = chooseMissingByLength(rawLength + 1); // + 1 for the value shift to have 0 as start

constructor(rawLength: number, criteria: ISortCriteria[], acc: (d: Column) => ECompareValueType | ECompareValueType[]) {
for (const c of criteria) {
const v = acc(c.col);
for (const c of (isSorting ? ranking.getSortCriteria() : ranking.getGroupSortCriteria())) {
const v = (isSorting ? c.col.toCompareValueType() : c.col.toCompareGroupValueType());
const valueCache = valueCaches ? valueCaches(c.col) : undefined;
this.criteria.push({col: c.col, valueCache});
if (!Array.isArray(v)) {
this.comparators.push({asc: c.asc, v});
const lookup = toCompareLookUp(rawLength, v);
this.data.push({asc: c.asc, v, lookup, setter: createSetter(v, lookup, missingCount)});
continue;
}
for (const vi of v) {
this.comparators.push({asc: c.asc, v: vi});
const lookup = toCompareLookUp(rawLength, vi);
this.data.push({asc: c.asc, v: vi, lookup, setter: createSetter(vi, lookup, missingCount)});
}
}

this.lookups = this.comparators.map((d) => toCompareLookUp(rawLength, d.v));

this.missingCount = chooseMissingByLength(rawLength + 1); // + 1 for the value shift to have 0 as start
}

get sortOrders() {
return this.comparators.map((d, i) => ({asc: d.asc, lookup: this.lookups[i]}));
return this.data.map((d) => ({asc: d.asc, lookup: d.lookup}));
}

get transferAbles() {
// so a typed array
return this.lookups.filter((d): d is UIntTypedArray | Float32Array => !Array.isArray(d)).map((d) => d.buffer);
return this.data.map((d) => d.lookup).filter((d): d is UIntTypedArray | Float32Array => !Array.isArray(d)).map((d) => d.buffer);
}

push(index: number, vs: ICompareValue[]) {
const l = this.comparators.length;
for (let i = 0; i < l; ++i) {
const type = this.comparators[i].v;
const lookup = this.lookups[i];
const v = vs[i];
switch (type) {
case ECompareValueType.BINARY: // just 0 or 1 -> convert to 0=-Ininity 1 2 255=+Infinity
lookup[index] = v == null || isNaN(<number>v) ? missingBinary : (<number>v) + 1;
break;
case ECompareValueType.COUNT: // uint32
lookup[index] = v == null || isNaN(<number>v) ? this.missingCount : (<number>v) + 1;
break;
case ECompareValueType.UINT8: // shift by one to have 0 for -Inf
lookup[index] = v == null || isNaN(<number>v) ? missingInt8 : (<number>v) + 1;
break;
case ECompareValueType.UINT16: // shift by one to have 0 for -Inf
lookup[index] = v == null || isNaN(<number>v) ? missingInt16 : (<number>v) + 1;
break;
case ECompareValueType.UINT32: // shift by one to have 0 for -Inf
lookup[index] = v == null || isNaN(<number>v) ? missingInt32 : (<number>v) + 1;
break;
case ECompareValueType.INT8:
lookup[index] = v == null || isNaN(<number>v) ? missingInt8 : (<number>v);
break;
case ECompareValueType.INT16:
lookup[index] = v == null || isNaN(<number>v) ? missingInt16 : (<number>v);
break;
case ECompareValueType.INT32:
lookup[index] = v == null || isNaN(<number>v) ? missingInt32 : (<number>v);
break;
case ECompareValueType.STRING:
lookup[index] = v == null || v === '' ? missingString : v;
break;
case ECompareValueType.FLOAT:
case ECompareValueType.DOUBLE:
lookup[index] = v == null || isNaN(<number>v) ? missingFloat : v;
break;
case ECompareValueType.FLOAT_ASC:
case ECompareValueType.DOUBLE_ASC:
lookup[index] = v == null || isNaN(<number>v) ? missingFloatAsc : v;
break;
push(row: IDataRow) {
let i = 0;
for (const c of this.criteria) {
const r = c.col.toCompareValue(row, c.valueCache ? c.valueCache(row.i) : undefined);
if (!Array.isArray(r)) {
this.data[i++].setter(row.i, r);
continue;
}
for (const ri of r) {
this.data[i++].setter(row.i, ri);
}
}
}

pushValues(dataIndex: number, vs: ICompareValue[]) {
for (let i = 0; i < vs.length; ++i) {
this.data[i].setter(dataIndex, vs[i]);
}
}

free() {
// free up to save memory
this.lookups.splice(0, this.lookups.length);
this.comparators.splice(0, this.comparators.length);
this.data.splice(0, this.data.length);
}
}

0 comments on commit 1d682f9

Please sign in to comment.