You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/09/17 15:27:40 UTC
[incubator-echarts] branch line-optimize updated: perf: optimize
storage access in List for large data.
This is an automated email from the ASF dual-hosted git repository.
shenyi pushed a commit to branch line-optimize
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
The following commit(s) were added to refs/heads/line-optimize by this push:
new 6f0f9eb perf: optimize storage access in List for large data.
6f0f9eb is described below
commit 6f0f9ebf8a25e00716e5383a7fd22561a0c24b76
Author: pissang <bm...@gmail.com>
AuthorDate: Thu Sep 17 23:27:22 2020 +0800
perf: optimize storage access in List for large data.
---
src/data/List.ts | 80 ++++++++++++++++++++++++++++-------------
src/data/helper/dataProvider.ts | 8 +++--
2 files changed, 60 insertions(+), 28 deletions(-)
diff --git a/src/data/List.ts b/src/data/List.ts
index 3908244..f01aab8 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -45,7 +45,7 @@ import type { VisualMeta } from '../component/visualMap/VisualMapModel';
import { parseDataValue } from './helper/dataValueHelper';
import { isSourceInstance } from './Source';
-
+const mathFloor = Math.floor;
const isObject = zrUtil.isObject;
const UNDEFINED = 'undefined';
@@ -213,6 +213,7 @@ class List<
private _count: number = 0;
private _rawCount: number = 0;
private _storage: DataStorage = {};
+ private _storageArr: DataValueChunk[][] = [];
private _nameList: string[] = [];
private _idList: string[] = [];
@@ -539,7 +540,7 @@ class List<
const emptyDataItem = new Array(dimLen);
for (let idx = start; idx < end; idx++) {
const sourceIdx = idx - start;
- const chunkIndex = Math.floor(idx / chunkSize);
+ const chunkIndex = mathFloor(idx / chunkSize);
const chunkOffset = idx % chunkSize;
// Store the data by dimensions
@@ -609,6 +610,14 @@ class List<
this._chunkCount = storage[dim].length;
}
+ const storageArr: DataValueChunk[][] = this._storageArr = [];
+ const rawExtentArr = [];
+ for (let k = 0; k < dimLen; k++) {
+ const dim = dimensions[k];
+ storageArr.push(storage[dim]);
+ rawExtentArr.push(rawExtent[dim]);
+ }
+
let dataItem = new Array(dimLen) as OptionDataItem;
for (let idx = start; idx < end; idx++) {
// NOTICE: Try not to write things into dataItem
@@ -619,18 +628,18 @@ class List<
// Bar chart, line chart which uses category axis
// only gives the 'y' value. 'x' value is the indices of category
// Use a tempValue to normalize the value to be a (x, y) value
- const chunkIndex = Math.floor(idx / chunkSize);
+ const chunkIndex = mathFloor(idx / chunkSize);
const chunkOffset = idx % chunkSize;
// Store the data by dimensions
for (let k = 0; k < dimLen; k++) {
const dim = dimensions[k];
- const dimStorage = storage[dim][chunkIndex];
+ const dimStorage = storageArr[k][chunkIndex];
// PENDING NULL is empty or zero
const val = this._dimValueGetter(dataItem, dim, idx, k) as ParsedValueNumeric;
dimStorage[chunkOffset] = val;
- const dimRawExtent = rawExtent[dim];
+ const dimRawExtent = rawExtentArr[k];
val < dimRawExtent[0] && (dimRawExtent[0] = val);
val > dimRawExtent[1] && (dimRawExtent[1] = val);
}
@@ -728,6 +737,23 @@ class List<
return newIndices;
}
+ // Get data by index of dimension.
+ // Because in v8 access array by number variable is faster than access object by string variable
+ // Not sure why but the optimization just works.
+ getByDimIdx(dimIdx: number, idx: number): ParsedValue {
+ const dimStore = this._storageArr[dimIdx];
+ const chunkSize = this._chunkSize;
+ if (!dimStore) {
+ return NaN;
+ }
+ idx = this.getRawIndex(idx);
+
+ const chunkIndex = mathFloor(idx / chunkSize);
+ const chunkOffset = idx % chunkSize;
+
+ return dimStore[chunkIndex][chunkOffset];
+ }
+
/**
* Get value. Return NaN if idx is out of range.
* @param dim Dim must be concrete name.
@@ -736,21 +762,18 @@ class List<
if (!(idx >= 0 && idx < this._count)) {
return NaN;
}
- const storage = this._storage;
- if (!storage[dim]) {
- // TODO Warn ?
+ const dimStore = this._storage[dim];
+ const chunkSize = this._chunkSize;
+ if (!dimStore) {
return NaN;
}
idx = this.getRawIndex(idx);
- const chunkIndex = Math.floor(idx / this._chunkSize);
- const chunkOffset = idx % this._chunkSize;
+ const chunkIndex = mathFloor(idx / chunkSize);
+ const chunkOffset = idx % chunkSize;
- const chunkStore = storage[dim][chunkIndex];
- const value = chunkStore[chunkOffset];
-
- return value;
+ return dimStore[chunkIndex][chunkOffset];
}
/**
@@ -761,13 +784,14 @@ class List<
return NaN;
}
const dimStore = this._storage[dim];
+ const chunkSize = this._chunkSize;
if (!dimStore) {
// TODO Warn ?
return NaN;
}
- const chunkIndex = Math.floor(rawIdx / this._chunkSize);
- const chunkOffset = rawIdx % this._chunkSize;
+ const chunkIndex = mathFloor(rawIdx / chunkSize);
+ const chunkOffset = rawIdx % chunkSize;
const chunkStore = dimStore[chunkIndex];
return chunkStore[chunkOffset];
}
@@ -777,8 +801,9 @@ class List<
* Hack a much simpler _getFast
*/
private _getFast(dim: DimensionName, rawIdx: number): ParsedValue {
- const chunkIndex = Math.floor(rawIdx / this._chunkSize);
- const chunkOffset = rawIdx % this._chunkSize;
+ const chunkSize = this._chunkSize;
+ const chunkIndex = mathFloor(rawIdx / chunkSize);
+ const chunkOffset = rawIdx % chunkSize;
const chunkStore = this._storage[dim][chunkIndex];
return chunkStore[chunkOffset];
}
@@ -985,7 +1010,7 @@ class List<
// let chunkSize = this._chunkSize;
// if (dimData) {
// for (let i = 0, len = this.count(); i < len; i++) {
- // let chunkIndex = Math.floor(i / chunkSize);
+ // let chunkIndex = mathFloor(i / chunkSize);
// let chunkOffset = i % chunkSize;
// if (dimData[chunkIndex][chunkOffset] === value) {
// return i;
@@ -1081,6 +1106,7 @@ class List<
const storage = this._storage;
const dimData = storage[dim];
const nearestIndices: number[] = [];
+ const chunkSize = this._chunkSize;
if (!dimData) {
return nearestIndices;
@@ -1094,9 +1120,12 @@ class List<
let minDiff = -1;
let nearestIndicesLen = 0;
+
// Check the test case of `test/ut/spec/data/List.js`.
for (let i = 0, len = this.count(); i < len; i++) {
- const diff = value - (this.get(dim, i) as number);
+ const chunkIndex = mathFloor(i / chunkSize);
+ const chunkOffset = i % chunkSize;
+ const diff = value - (dimData[chunkIndex][chunkOffset] as number);
const dist = Math.abs(diff);
if (dist <= maxDistance) {
// When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
@@ -1549,7 +1578,7 @@ class List<
}
const rawIndex = this.getRawIndex(dataIndex);
- const chunkIndex = Math.floor(rawIndex / chunkSize);
+ const chunkIndex = mathFloor(rawIndex / chunkSize);
const chunkOffset = rawIndex % chunkSize;
for (let i = 0; i < retValue.length; i++) {
@@ -1589,7 +1618,7 @@ class List<
const targetStorage = list._storage;
const frameValues = [];
- let frameSize = Math.floor(1 / rate);
+ let frameSize = mathFloor(1 / rate);
const dimStore = targetStorage[dimension];
const len = this.count();
@@ -1607,7 +1636,7 @@ class List<
}
for (let k = 0; k < frameSize; k++) {
const dataIdx = this.getRawIndex(i + k);
- const originalChunkIndex = Math.floor(dataIdx / chunkSize);
+ const originalChunkIndex = mathFloor(dataIdx / chunkSize);
const originalChunkOffset = dataIdx % chunkSize;
frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
}
@@ -1615,7 +1644,7 @@ class List<
const sampleFrameIdx = this.getRawIndex(
Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
);
- const sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
+ const sampleChunkIndex = mathFloor(sampleFrameIdx / chunkSize);
const sampleChunkOffset = sampleFrameIdx % chunkSize;
// Only write value on the filtered data
dimStore[sampleChunkIndex][sampleChunkOffset] = value;
@@ -1877,6 +1906,7 @@ class List<
// FIXME
list._storage = this._storage;
+ list._storageArr = this._storageArr;
transferProperties(list, this);
@@ -2005,7 +2035,7 @@ class List<
let val;
if (dimIndex != null) {
const chunkSize = list._chunkSize;
- const chunkIndex = Math.floor(rawIndex / chunkSize);
+ const chunkIndex = mathFloor(rawIndex / chunkSize);
const chunkOffset = rawIndex % chunkSize;
const dim = list.dimensions[dimIndex];
const chunk = list._storage[dim][chunkIndex];
diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts
index 98faac7..b3c5290 100644
--- a/src/data/helper/dataProvider.ts
+++ b/src/data/helper/dataProvider.ts
@@ -158,9 +158,11 @@ export class DefaultDataProvider implements DataProvider {
): ArrayLike<number> {
idx = idx - this._offset;
out = out || [];
- const offset = this._dimSize * idx;
- for (let i = 0; i < this._dimSize; i++) {
- out[i] = (this._data as ArrayLike<number>)[offset + i];
+ const data = this._data;
+ const dimSize = this._dimSize;
+ const offset = dimSize * idx;
+ for (let i = 0; i < dimSize; i++) {
+ out[i] = (data as ArrayLike<number>)[offset + i];
}
return out;
};
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org