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/24 05:13:13 UTC

[incubator-echarts] 01/01: refact: remove chunks in List.

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch list-remove-chunk
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 41988f298f6931891cf8f40d9d718f1dda87caef
Author: pissang <bm...@gmail.com>
AuthorDate: Thu Sep 24 13:12:50 2020 +0800

    refact: remove chunks in List.
---
 src/data/List.ts                | 448 ++++++++++++++++------------------------
 src/data/helper/dataProvider.ts |  46 ++++-
 2 files changed, 225 insertions(+), 269 deletions(-)

diff --git a/src/data/List.ts b/src/data/List.ts
index 4298f9e..d13ae70 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -47,6 +47,7 @@ import { isSourceInstance } from './Source';
 
 const mathFloor = Math.floor;
 const isObject = zrUtil.isObject;
+const map = zrUtil.map;
 
 const UNDEFINED = 'undefined';
 const INDEX_NOT_FOUND = -1;
@@ -88,7 +89,7 @@ type DimValueGetter = (
 ) => ParsedValue;
 
 type DataValueChunk = ArrayLike<ParsedValue>;
-type DataStorage = {[dimName: string]: DataValueChunk[]};
+type DataStorage = {[dimName: string]: DataValueChunk};
 type NameRepeatCount = {[name: string]: number};
 
 
@@ -161,8 +162,8 @@ let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
 let prepareInvertedIndex: (list: List) => void;
 let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => ParsedValue | OrdinalRawValue;
 let getIndicesCtor: (list: List) => DataArrayLikeConstructor;
-let prepareChunks: (
-    storage: DataStorage, dimInfo: DataDimensionInfo, chunkSize: number, chunkCount: number, end: number
+let prepareStorage: (
+    storage: DataStorage, dimInfo: DataDimensionInfo, end: number, append?: boolean
 ) => void;
 let getRawIndexWithoutIndices: (this: List, idx: number) => number;
 let getRawIndexWithIndices: (this: List, idx: number) => number;
@@ -170,7 +171,6 @@ let getId: (list: List, rawIndex: number) => string;
 let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
 let validateDimensions: (list: List, dims: DimensionName[]) => void;
 let cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List;
-let cloneDimStore: (originalDimStore: DataValueChunk[]) => DataValueChunk[];
 let getInitialExtent: () => [number, number];
 let setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
 let transferProperties: (target: List, source: List) => void;
@@ -213,7 +213,7 @@ class List<
     private _count: number = 0;
     private _rawCount: number = 0;
     private _storage: DataStorage = {};
-    private _storageArr: DataValueChunk[][] = [];
+    private _storageArr: DataValueChunk[] = [];
     private _nameList: string[] = [];
     private _idList: string[] = [];
 
@@ -236,11 +236,6 @@ class List<
     // Graphic elemnents
     private _graphicEls: Element[] = [];
 
-    // Max size of each chunk.
-    private _chunkSize: number = 1e5;
-
-    private _chunkCount: number = 0;
-
     private _rawData: DataProvider;
 
     // Raw extent will not be cloned, but only transfered.
@@ -446,7 +441,6 @@ class List<
 
         // Clear
         this._storage = {};
-        this._storageArr = [];
         this._indices = null;
 
         this._nameList = nameList || [];
@@ -516,48 +510,40 @@ class List<
      *        Each item is exaclty cooresponding to a dimension.
      */
     appendValues(values: any[][], names?: string[]): void {
-        const chunkSize = this._chunkSize;
         const storage = this._storage;
-        const storageArr = this._storageArr;
         const dimensions = this.dimensions;
         const dimLen = dimensions.length;
         const rawExtent = this._rawExtent;
 
         const start = this.count();
         const end = start + Math.max(values.length, names ? names.length : 0);
-        const originalChunkCount = this._chunkCount;
 
         for (let i = 0; i < dimLen; i++) {
             const dim = dimensions[i];
             if (!rawExtent[dim]) {
                 rawExtent[dim] = getInitialExtent();
             }
-            if (!storage[dim]) {
-                const store: DataValueChunk[] = [];
-                storage[dim] = store;
-                storageArr.push(store);
-            }
-            prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
-            this._chunkCount = storage[dim].length;
+            prepareStorage(storage, this._dimensionInfos[dim], end, true);
         }
 
-        const rawExtentArr = zrUtil.map(dimensions, (dim) => {
+        const rawExtentArr = map(dimensions, (dim) => {
             return rawExtent[dim];
         });
 
-        const emptyDataItem = new Array(dimLen);
+        const storageArr = this._storageArr = map(dimensions, (dim) => {
+            return storage[dim];
+        });
+
+        const emptyDataItem: number[] = [];
         for (let idx = start; idx < end; idx++) {
             const sourceIdx = idx - start;
-            const chunkIndex = mathFloor(idx / chunkSize);
-            const chunkOffset = idx % chunkSize;
-
             // Store the data by dimensions
             for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
                 const dim = dimensions[dimIdx];
                 const val = this._dimValueGetterArrayRows(
                     values[sourceIdx] || emptyDataItem, dim, sourceIdx, dimIdx
                 ) as ParsedValueNumeric;
-                storageArr[dimIdx][chunkIndex][chunkOffset] = val;
+                storageArr[dimIdx][idx] = val;
 
                 const dimRawExtent = rawExtentArr[dimIdx];
                 val < dimRawExtent[0] && (dimRawExtent[0] = val);
@@ -582,10 +568,8 @@ class List<
             return;
         }
 
-        const chunkSize = this._chunkSize;
         const rawData = this._rawData;
         const storage = this._storage;
-        const storageArr = this._storageArr;
         const dimensions = this.dimensions;
         const dimLen = dimensions.length;
         const dimensionInfoMap = this._dimensionInfos;
@@ -595,8 +579,6 @@ class List<
         const nameRepeatCount: NameRepeatCount = this._nameRepeatCount = {};
         let nameDimIdx;
 
-        const originalChunkCount = this._chunkCount;
-
         for (let i = 0; i < dimLen; i++) {
             const dim = dimensions[i];
             if (!rawExtent[dim]) {
@@ -611,92 +593,99 @@ class List<
                 this._idDimIdx = i;
             }
 
-            if (!storage[dim]) {
-                const store: DataValueChunk[] = [];
-                storage[dim] = store;
-                storageArr.push(store);
+            if (!rawData.getStorage) {
+                prepareStorage(storage, dimInfo, end);
             }
-
-            prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
-
-            this._chunkCount = storage[dim].length;
         }
 
-        const rawExtentArr = zrUtil.map(dimensions, (dim) => {
-            return rawExtent[dim];
+        const storageArr = this._storageArr = map(dimensions, (dim) => {
+            return storage[dim];
         });
 
-        let dataItem = [] as OptionDataItem;
-        for (let idx = start; idx < end; idx++) {
-            // NOTICE: Try not to write things into dataItem
-            dataItem = rawData.getItem(idx, dataItem);
-            // Each data item is value
-            // [1, 2]
-            // 2
-            // 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 = mathFloor(idx / chunkSize);
-            const chunkOffset = idx % chunkSize;
-
-            // Store the data by dimensions
-            for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
-                const dim = dimensions[dimIdx];
-                const dimStorage = storageArr[dimIdx][chunkIndex];
-                // PENDING NULL is empty or zero
-                const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric;
-                dimStorage[chunkOffset] = val;
+        const rawExtentArr = map(dimensions, (dim) => {
+            return rawExtent[dim];
+        });
 
-                const dimRawExtent = rawExtentArr[dimIdx];
-                val < dimRawExtent[0] && (dimRawExtent[0] = val);
-                val > dimRawExtent[1] && (dimRawExtent[1] = val);
+        if (rawData.getStorage) {
+            const ret = rawData.getStorage(start, end);
+            const rawStorage = ret.storage;
+            const extent = ret.extent;
+            for (let dimIdx = 0; dimIdx < rawStorage.length; dimIdx++) {
+                storage[dimensions[dimIdx]] = storageArr[dimIdx] = rawStorage[dimIdx];
+                rawExtentArr[dimIdx][0] = Math.min(rawExtentArr[dimIdx][0], extent[dimIdx][0]);
+                rawExtentArr[dimIdx][1] = Math.max(rawExtentArr[dimIdx][1], extent[dimIdx][1]);
             }
+        }
+        else {
+            let dataItem = [] as OptionDataItem;
+            for (let idx = start; idx < end; idx++) {
+                // NOTICE: Try not to write things into dataItem
+                dataItem = rawData.getItem(idx, dataItem);
+                // Each data item is value
+                // [1, 2]
+                // 2
+                // 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
+
+                // Store the data by dimensions
+                for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
+                    const dim = dimensions[dimIdx];
+                    const dimStorage = storageArr[dimIdx];
+                    // PENDING NULL is empty or zero
+                    const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric;
+                    dimStorage[idx] = val;
+
+                    const dimRawExtent = rawExtentArr[dimIdx];
+                    val < dimRawExtent[0] && (dimRawExtent[0] = val);
+                    val > dimRawExtent[1] && (dimRawExtent[1] = val);
+                }
 
-            // ??? FIXME not check by pure but sourceFormat?
-            // TODO refactor these logic.
-            if (!rawData.pure) {
-                let name: string = nameList[idx];
-
-                if (dataItem && name == null) {
-                    // If dataItem is {name: ...}, it has highest priority.
-                    // That is appropriate for many common cases.
-                    if ((dataItem as any).name != null) {
-                        // There is no other place to persistent dataItem.name,
-                        // so save it to nameList.
-                        nameList[idx] = name = convertOptionIdName((dataItem as any).name, null);
-                    }
-                    else if (nameDimIdx != null) {
-                        const nameDim = dimensions[nameDimIdx];
-                        const nameDimChunk = storage[nameDim][chunkIndex];
-                        if (nameDimChunk) {
-                            const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
-                            name = convertOptionIdName(
-                                (ordinalMeta && ordinalMeta.categories.length)
-                                    ? ordinalMeta.categories[nameDimChunk[chunkOffset] as number]
-                                    : nameDimChunk[chunkOffset],
-                                null
-                            );
+                // ??? FIXME not check by pure but sourceFormat?
+                // TODO refactor these logic.
+                if (!rawData.pure) {
+                    let name: string = nameList[idx];
+
+                    if (dataItem && name == null) {
+                        // If dataItem is {name: ...}, it has highest priority.
+                        // That is appropriate for many common cases.
+                        if ((dataItem as any).name != null) {
+                            // There is no other place to persistent dataItem.name,
+                            // so save it to nameList.
+                            nameList[idx] = name = convertOptionIdName((dataItem as any).name, null);
+                        }
+                        else if (nameDimIdx != null) {
+                            const nameDim = dimensions[nameDimIdx];
+                            const nameDimChunk = storage[nameDim];
+                            if (nameDimChunk) {
+                                const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
+                                name = convertOptionIdName(
+                                    (ordinalMeta && ordinalMeta.categories.length)
+                                        ? ordinalMeta.categories[nameDimChunk[idx] as number]
+                                        : nameDimChunk[idx],
+                                    null
+                                );
+                            }
                         }
                     }
-                }
 
-                // Try using the id in option
-                // id or name is used on dynamical data, mapping old and new items.
-                let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null);
+                    // Try using the id in option
+                    // id or name is used on dynamical data, mapping old and new items.
+                    let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null);
 
-                if (id == null && name != null) {
-                    // Use name as id and add counter to avoid same name
-                    nameRepeatCount[name] = nameRepeatCount[name] || 0;
-                    id = name;
-                    if (nameRepeatCount[name] > 0) {
-                        id += '__ec__' + nameRepeatCount[name];
+                    if (id == null && name != null) {
+                        // Use name as id and add counter to avoid same name
+                        nameRepeatCount[name] = nameRepeatCount[name] || 0;
+                        id = name;
+                        if (nameRepeatCount[name] > 0) {
+                            id += '__ec__' + nameRepeatCount[name];
+                        }
+                        nameRepeatCount[name]++;
                     }
-                    nameRepeatCount[name]++;
+                    id != null && (idList[idx] = id);
                 }
-                id != null && (idList[idx] = id);
             }
         }
-
         if (!rawData.persistent && rawData.clean) {
             // Clean unused data if data source is typed array.
             rawData.clean();
@@ -752,18 +741,8 @@ class List<
         if (!(idx >= 0 && idx < this._count)) {
             return NaN;
         }
-
         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];
+        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
     }
 
     /**
@@ -775,17 +754,7 @@ class List<
             return NaN;
         }
         const dimStore = this._storage[dim];
-        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];
+        return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
     }
 
     /**
@@ -796,27 +765,7 @@ class List<
             return NaN;
         }
         const dimStore = this._storage[dim];
-        const chunkSize = this._chunkSize;
-        if (!dimStore) {
-            // TODO Warn ?
-            return NaN;
-        }
-
-        const chunkIndex = mathFloor(rawIdx / chunkSize);
-        const chunkOffset = rawIdx % chunkSize;
-        const chunkStore = dimStore[chunkIndex];
-        return chunkStore[chunkOffset];
-    }
-
-    /**
-     * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
-     * Hack a much simpler _getFast
-     */
-    private _getFast(dimIdx: number, rawIdx: number): ParsedValue {
-        const chunkSize = this._chunkSize;
-        const chunkIndex = mathFloor(rawIdx / chunkSize);
-        const chunkOffset = rawIdx % chunkSize;
-        return this._storageArr[dimIdx][chunkIndex][chunkOffset];
+        return dimStore ? dimStore[rawIdx] : NaN;
     }
 
     /**
@@ -866,7 +815,6 @@ class List<
         dim = this.getDimension(dim);
         const dimData = this._storage[dim];
         const initialExtent = getInitialExtent();
-        const chunkSize = this._chunkSize;
 
         // stack = !!((stack || false) && this.getCalculationInfo(dim));
 
@@ -899,9 +847,7 @@ class List<
 
         for (let i = 0; i < currEnd; i++) {
             const rawIdx = this.getRawIndex(i);
-            const chunkIndex = mathFloor(rawIdx / chunkSize);
-            const chunkOffset = rawIdx % chunkSize;
-            const value = dimData[chunkIndex][chunkOffset] as ParsedValueNumeric;
+            const value = dimData[rawIdx] as ParsedValueNumeric;
             value < min && (min = value);
             value > max && (max = value);
         }
@@ -1120,7 +1066,6 @@ class List<
         const storage = this._storage;
         const dimData = storage[dim];
         const nearestIndices: number[] = [];
-        const chunkSize = this._chunkSize;
 
         if (!dimData) {
             return nearestIndices;
@@ -1138,9 +1083,7 @@ class List<
         // Check the test case of `test/ut/spec/data/List.js`.
         for (let i = 0, len = this.count(); i < len; i++) {
             const dataIndex = this.getRawIndex(i);
-            const chunkIndex = mathFloor(dataIndex / chunkSize);
-            const chunkOffset = dataIndex % chunkSize;
-            const diff = value - (dimData[chunkIndex][chunkOffset] as number);
+            const diff = value - (dimData[dataIndex] 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)`,
@@ -1248,36 +1191,37 @@ class List<
         // ctxCompat just for compat echarts3
         const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
 
-        const dimNames = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
+        const dimNames = map(normalizeDimensions(dims), this.getDimension, this);
 
         if (__DEV__) {
             validateDimensions(this, dimNames);
         }
 
         const dimSize = dimNames.length;
-        const dimIndices = zrUtil.map(dimNames, (dimName) => {
+        const dimIndices = map(dimNames, (dimName) => {
             return this._dimensionInfos[dimName].index;
         });
+        const storageArr = this._storageArr;
 
-        for (let i = 0; i < this.count(); i++) {
+        for (let i = 0, len = this.count(); i < len; i++) {
             // Simple optimization
             switch (dimSize) {
                 case 0:
                     (cb as EachCb0<Ctx>).call(fCtx, i);
                     break;
                 case 1:
-                    (cb as EachCb1<Ctx>).call(fCtx, this._getFast(dimIndices[0], i), i);
+                    (cb as EachCb1<Ctx>).call(fCtx, storageArr[dimIndices[0]][i], i);
                     break;
                 case 2:
                     (cb as EachCb2<Ctx>).call(
-                        fCtx, this._getFast(dimIndices[0], i), this._getFast(dimIndices[1], i), i
+                        fCtx, storageArr[dimIndices[0]][i], storageArr[dimIndices[1]][i], i
                     );
                     break;
                 default:
                     let k = 0;
                     const value = [];
                     for (; k < dimSize; k++) {
-                        value[k] = this._getFast(dimIndices[k], i);
+                        value[k] = storageArr[dimIndices[k]][i];
                     }
                     // Index
                     value[k] = i;
@@ -1316,7 +1260,7 @@ class List<
         // ctxCompat just for compat echarts3
         const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
 
-        const dimNames = zrUtil.map(
+        const dimNames = map(
             normalizeDimensions(dims), this.getDimension, this
         );
 
@@ -1332,10 +1276,11 @@ class List<
         const dimSize = dimNames.length;
 
         let offset = 0;
-        const dimIndices = zrUtil.map(dimNames, (dimName) => {
+        const dimIndices = map(dimNames, (dimName) => {
             return this._dimensionInfos[dimName].index;
         });
         const dim0 = dimIndices[0];
+        const storageArr = this._storageArr;
 
         for (let i = 0; i < count; i++) {
             let keep;
@@ -1345,13 +1290,13 @@ class List<
                 keep = (cb as FilterCb0<Ctx>).call(fCtx, i);
             }
             else if (dimSize === 1) {
-                const val = this._getFast(dim0, rawIdx);
+                const val = storageArr[dim0][rawIdx];
                 keep = (cb as FilterCb1<Ctx>).call(fCtx, val, i);
             }
             else {
                 let k = 0;
                 for (; k < dimSize; k++) {
-                    value[k] = this._getFast(dimIndices[k], rawIdx);
+                    value[k] = storageArr[dimIndices[k]][rawIdx];
                 }
                 value[k] = i;
                 keep = (cb as FilterCb<Ctx>).apply(fCtx, value);
@@ -1381,7 +1326,9 @@ class List<
     selectRange(range: {[dimName: string]: [number, number]}): List {
         'use strict';
 
-        if (!this._count) {
+        const len = this._count;
+
+        if (!len) {
             return;
         }
 
@@ -1407,63 +1354,55 @@ class List<
 
         let offset = 0;
         const dim0 = dimensions[0];
-        const dimIndices = zrUtil.map(dimensions, (dimName) => {
+        const dimIndices = map(dimensions, (dimName) => {
             return this._dimensionInfos[dimName].index;
         });
 
         const min = range[dim0][0];
         const max = range[dim0][1];
+        const storageArr = this._storageArr;
 
         let quickFinished = false;
         if (!this._indices) {
             // Extreme optimization for common case. About 2x faster in chrome.
             let idx = 0;
             if (dimSize === 1) {
-                const dimStorage = this._storage[dim0];
-                for (let k = 0; k < this._chunkCount; k++) {
-                    const chunkStorage = dimStorage[k];
-                    const len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
-                    for (let i = 0; i < len; i++) {
-                        const val = chunkStorage[i];
-                        // NaN will not be filtered. Consider the case, in line chart, empty
-                        // value indicates the line should be broken. But for the case like
-                        // scatter plot, a data item with empty value will not be rendered,
-                        // but the axis extent may be effected if some other dim of the data
-                        // item has value. Fortunately it is not a significant negative effect.
-                        if (
-                            (val >= min && val <= max) || isNaN(val as any)
-                        ) {
-                            newIndices[offset++] = idx;
-                        }
-                        idx++;
+                const dimStorage = storageArr[dimIndices[0]];
+                for (let i = 0; i < len; i++) {
+                    const val = dimStorage[i];
+                    // NaN will not be filtered. Consider the case, in line chart, empty
+                    // value indicates the line should be broken. But for the case like
+                    // scatter plot, a data item with empty value will not be rendered,
+                    // but the axis extent may be effected if some other dim of the data
+                    // item has value. Fortunately it is not a significant negative effect.
+                    if (
+                        (val >= min && val <= max) || isNaN(val as any)
+                    ) {
+                        newIndices[offset++] = idx;
                     }
+                    idx++;
                 }
                 quickFinished = true;
             }
             else if (dimSize === 2) {
-                const dimStorage = this._storage[dim0];
-                const dimStorage2 = this._storage[dimensions[1]];
+                const dimStorage = storageArr[dimIndices[0]];
+                const dimStorage2 = storageArr[dimIndices[1]];
                 const min2 = range[dimensions[1]][0];
                 const max2 = range[dimensions[1]][1];
-                for (let k = 0; k < this._chunkCount; k++) {
-                    const chunkStorage = dimStorage[k];
-                    const chunkStorage2 = dimStorage2[k];
-                    const len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
-                    for (let i = 0; i < len; i++) {
-                        const val = chunkStorage[i];
-                        const val2 = chunkStorage2[i];
-                        // Do not filter NaN, see comment above.
-                        if ((
-                                (val >= min && val <= max) || isNaN(val as any)
-                            )
-                            && (
-                                (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
-                            )
-                        ) {
-                            newIndices[offset++] = idx;
-                        }
-                        idx++;
+                for (let i = 0; i < len; i++) {
+                    const val = dimStorage[i];
+                    const val2 = dimStorage2[i];
+                    // Do not filter NaN, see comment above.
+                    if ((
+                            (val >= min && val <= max) || isNaN(val as any)
+                        )
+                        && (
+                            (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
+                        )
+                    ) {
+                        newIndices[offset++] = idx;
                     }
+                    idx++;
                 }
                 quickFinished = true;
             }
@@ -1472,7 +1411,7 @@ class List<
             if (dimSize === 1) {
                 for (let i = 0; i < originalCount; i++) {
                     const rawIndex = this.getRawIndex(i);
-                    const val = this._getFast(dimIndices[0], rawIndex);
+                    const val = storageArr[dimIndices[0]][rawIndex];
                     // Do not filter NaN, see comment above.
                     if (
                         (val >= min && val <= max) || isNaN(val as any)
@@ -1487,7 +1426,7 @@ class List<
                     const rawIndex = this.getRawIndex(i);
                     for (let k = 0; k < dimSize; k++) {
                         const dimk = dimensions[k];
-                        const val = this._getFast(dimIndices[k], rawIndex);
+                        const val = storageArr[dimIndices[k]][rawIndex];
                         // Do not filter NaN, see comment above.
                         if (val < range[dimk][0] || val > range[dimk][1]) {
                             keep = false;
@@ -1565,7 +1504,7 @@ class List<
         // ctxCompat just for compat echarts3
         const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
 
-        const dimNames = zrUtil.map(
+        const dimNames = map(
             normalizeDimensions(dims), this.getDimension, this
         );
 
@@ -1574,16 +1513,14 @@ class List<
         }
 
         const list = cloneListForMapAndSample(this, dimNames);
+        const storage = list._storage;
 
         // Following properties are all immutable.
         // So we can reference to the same value
         list._indices = this._indices;
         list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
 
-        const storage = list._storage;
-
         const tmpRetValue = [];
-        const chunkSize = this._chunkSize;
         const dimSize = dimNames.length;
         const dataCount = this.count();
         const values = [];
@@ -1604,8 +1541,6 @@ class List<
                 }
 
                 const rawIndex = this.getRawIndex(dataIndex);
-                const chunkIndex = mathFloor(rawIndex / chunkSize);
-                const chunkOffset = rawIndex % chunkSize;
 
                 for (let i = 0; i < retValue.length; i++) {
                     const dim = dimNames[i];
@@ -1614,7 +1549,7 @@ class List<
 
                     const dimStore = storage[dim];
                     if (dimStore) {
-                        dimStore[chunkIndex][chunkOffset] = val;
+                        dimStore[rawIndex] = val;
                     }
 
                     if (val < rawExtentOnDim[0]) {
@@ -1648,7 +1583,6 @@ class List<
 
         const dimStore = targetStorage[dimension];
         const len = this.count();
-        const chunkSize = this._chunkSize;
         const rawExtentOnDim = list._rawExtent[dimension];
 
         const newIndices = new (getIndicesCtor(this))(len);
@@ -1662,18 +1596,14 @@ class List<
             }
             for (let k = 0; k < frameSize; k++) {
                 const dataIdx = this.getRawIndex(i + k);
-                const originalChunkIndex = mathFloor(dataIdx / chunkSize);
-                const originalChunkOffset = dataIdx % chunkSize;
-                frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
+                frameValues[k] = dimStore[dataIdx];
             }
             const value = sampleValue(frameValues);
             const sampleFrameIdx = this.getRawIndex(
                 Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
             );
-            const sampleChunkIndex = mathFloor(sampleFrameIdx / chunkSize);
-            const sampleChunkOffset = sampleFrameIdx % chunkSize;
             // Only write value on the filtered data
-            dimStore[sampleChunkIndex][sampleChunkOffset] = value;
+            dimStore[sampleFrameIdx] = value;
 
             if (value < rawExtentOnDim[0]) {
                 rawExtentOnDim[0] = value;
@@ -1704,12 +1634,11 @@ class List<
         valueDimension: DimensionName,
         targetCount: number
     ) {
-        const list = cloneListForMapAndSample(this, [baseDimension, valueDimension]);
+        const list = cloneListForMapAndSample(this, []);
         const targetStorage = list._storage;
         const baseDimStore = targetStorage[baseDimension];
         const valueDimStore = targetStorage[valueDimension];
         const len = this.count();
-        const chunkSize = this._chunkSize;
         const newIndices = new (getIndicesCtor(this))(len);
 
         let sampledIndex = 0;
@@ -1732,15 +1661,13 @@ class List<
 
             for (let idx = avgRangeStart; idx < avgRangeEnd; idx++) {
                 const rawIndex = this.getRawIndex(idx);
-                const chunkIndex = mathFloor(rawIndex / chunkSize);
-                const chunkOffset = rawIndex % chunkSize;
-                const x = baseDimStore[chunkIndex][chunkOffset] as number;
-                const y = valueDimStore[chunkIndex][chunkOffset] as number;
+                const x = baseDimStore[rawIndex] as number;
+                const y = valueDimStore[rawIndex] as number;
                 if (isNaN(x) || isNaN(y)) {
                     continue;
                 }
-                avgX += baseDimStore[chunkIndex][chunkOffset] as number;
-                avgY += valueDimStore[chunkIndex][chunkOffset] as number;
+                avgX += x as number;
+                avgY += y as number;
             }
             avgX /= avgRangeLength;
             avgY /= avgRangeLength;
@@ -1748,21 +1675,17 @@ class List<
             const rangeOffs = mathFloor((i) * frameSize) + 1;
             const rangeTo = mathFloor((i + 1) * frameSize) + 1;
 
-            const chunkIndex = mathFloor(currentRawIndex / chunkSize);
-            const chunkOffset = currentRawIndex % chunkSize;
-            const pointAX = baseDimStore[chunkIndex][chunkOffset] as number;
-            const pointAY = valueDimStore[chunkIndex][chunkOffset] as number;
+            const pointAX = baseDimStore[currentRawIndex] as number;
+            const pointAY = valueDimStore[currentRawIndex] as number;
 
-            maxArea = area = -1;
+            maxArea = -1;
 
             // Find a point from current frame that construct a triangel with largest area with previous selected point
             // And the average of next frame.
             for (let idx = rangeOffs; idx < rangeTo; idx++) {
                 const rawIndex = this.getRawIndex(idx);
-                const chunkIndex = mathFloor(rawIndex / chunkSize);
-                const chunkOffset = rawIndex % chunkSize;
-                const x = baseDimStore[chunkIndex][chunkOffset] as number;
-                const y = valueDimStore[chunkIndex][chunkOffset] as number;
+                const x = baseDimStore[rawIndex] as number;
+                const y = valueDimStore[rawIndex] as number;
                 if (isNaN(x) || isNaN(y)) {
                     continue;
                 }
@@ -2023,7 +1946,7 @@ class List<
      */
     cloneShallow(list?: List<HostModel>): List<HostModel> {
         if (!list) {
-            const dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
+            const dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
             list = new List(dimensionInfoList, this.hostModel);
         }
 
@@ -2157,13 +2080,10 @@ class List<
         ): ParsedValue | OrdinalRawValue {
             let val;
             if (dimIndex != null) {
-                const chunkSize = list._chunkSize;
-                const chunkIndex = mathFloor(rawIndex / chunkSize);
-                const chunkOffset = rawIndex % chunkSize;
                 const dim = list.dimensions[dimIndex];
-                const chunk = list._storage[dim][chunkIndex];
+                const chunk = list._storage[dim];
                 if (chunk) {
-                    val = chunk[chunkOffset];
+                    val = chunk[rawIndex];
                     const ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
                     if (ordinalMeta && ordinalMeta.categories.length) {
                         val = ordinalMeta.categories[val as OrdinalNumber];
@@ -2178,30 +2098,30 @@ class List<
             return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
         };
 
-        prepareChunks = function (
+        prepareStorage = function (
             storage: DataStorage,
             dimInfo: DataDimensionInfo,
-            chunkSize: number,
-            chunkCount: number,
-            end: number
+            end: number,
+            append?: boolean
         ): void {
             const DataCtor = dataCtors[dimInfo.type];
-            const lastChunkIndex = chunkCount - 1;
             const dim = dimInfo.name;
-            const resizeChunkArray = storage[dim][lastChunkIndex];
-            if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
-                const newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
-                // The cost of the copy is probably inconsiderable
-                // within the initial chunkSize.
-                for (let j = 0; j < resizeChunkArray.length; j++) {
-                    newStore[j] = resizeChunkArray[j];
+
+            if (append) {
+                const oldStore = storage[dim];
+                const oldLen = oldStore.length;
+                if (oldStore && oldLen < end) {
+                    const newStore = new DataCtor(end);
+                    // The cost of the copy is probably inconsiderable
+                    // within the initial chunkSize.
+                    for (let j = 0; j < oldLen; j++) {
+                        newStore[j] = oldStore[j];
+                    }
+                    storage[dim] = newStore;
                 }
-                storage[dim][lastChunkIndex] = newStore;
             }
-
-            // Create new chunks.
-            for (let k = chunkCount * chunkSize; k < end; k += chunkSize) {
-                storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
+            else {
+                storage[dim] = new DataCtor(end);
             }
         };
 
@@ -2256,7 +2176,7 @@ class List<
         ): List {
             const allDimensions = original.dimensions;
             const list = new List(
-                zrUtil.map(allDimensions, original.getDimensionInfo, original),
+                map(allDimensions, original.getDimensionInfo, original),
                 original.hostModel
             );
             // FIXME If needs stackedOn, value may already been stacked
@@ -2264,7 +2184,7 @@ class List<
 
             const storage = list._storage = {} as DataStorage;
             const originalStorage = original._storage;
-            const storageArr: DataValueChunk[][] = list._storageArr = [];
+            const storageArr: DataValueChunk[] = list._storageArr = [];
 
             // Init storage
             for (let i = 0; i < allDimensions.length; i++) {
@@ -2273,7 +2193,7 @@ class List<
                     // Notice that we do not reset invertedIndicesMap here, becuase
                     // there is no scenario of mapping or sampling ordinal dimension.
                     if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
-                        storage[dim] = cloneDimStore(originalStorage[dim]);
+                        storage[dim] = cloneChunk(originalStorage[dim]);
                         list._rawExtent[dim] = getInitialExtent();
                         list._extent[dim] = null;
                     }
@@ -2287,14 +2207,6 @@ class List<
             return list;
         };
 
-        cloneDimStore = function (originalDimStore: DataValueChunk[]): DataValueChunk[] {
-            const newDimStore = new Array(originalDimStore.length);
-            for (let j = 0; j < originalDimStore.length; j++) {
-                newDimStore[j] = cloneChunk(originalDimStore[j]);
-            }
-            return newDimStore;
-        };
-
         function cloneChunk(originalChunk: DataValueChunk): DataValueChunk {
             const Ctor = originalChunk.constructor;
             // Only shallow clone is enough when Array.
diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts
index b3c5290..5f48956 100644
--- a/src/data/helper/dataProvider.ts
+++ b/src/data/helper/dataProvider.ts
@@ -38,7 +38,6 @@ import {
 } from '../../util/types';
 import List from '../List';
 
-
 export interface DataProvider {
     // If data is pure without style configuration
     pure: boolean;
@@ -48,6 +47,10 @@ export interface DataProvider {
     getSource(): Source;
     count(): number;
     getItem(idx: number, out?: OptionDataItem): OptionDataItem;
+    getStorage?(start: number, end: number): {
+        storage: ArrayLike<number>[]
+        extent: number[][]
+    }
     appendData(newData: ArrayLike<OptionDataItem>): void;
     clean(): void;
 }
@@ -56,6 +59,12 @@ export interface DataProvider {
 let providerMethods: Dictionary<any>;
 let mountMethods: (provider: DefaultDataProvider, data: OptionSourceData, source: Source) => void;
 
+export interface DefaultDataProvider {
+    getStorage?(start: number, end: number): {
+        storage: ArrayLike<number>[],
+        extent: number[][]
+    }
+}
 /**
  * If normal array used, mutable chunk size is supported.
  * If typed array used, chunk size must be fixed.
@@ -144,6 +153,7 @@ export class DefaultDataProvider implements DataProvider {
             if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
                 provider.getItem = getItemForTypedArray;
                 provider.count = countForTypedArray;
+                provider.getStorage = getStorageForTypedArray;
             }
             else {
                 const rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy);
@@ -167,6 +177,40 @@ export class DefaultDataProvider implements DataProvider {
             return out;
         };
 
+        const getStorageForTypedArray: DefaultDataProvider['getStorage'] = function (
+            this: DefaultDataProvider, start: number, end: number
+        ) {
+            const data = this._data as ArrayLike<number>;
+            const Ctor = data.constructor;
+            const dimSize = this._dimSize;
+            const offset = this._offset;
+            const storage: ArrayLike<number>[] = [];
+            const extent = [];
+
+            start -= offset;
+            end -= offset;
+
+            for (let dim = 0; dim < dimSize; dim++) {
+                let min = Infinity;
+                let max = -Infinity;
+                const count = end - start;
+                const arr = new (Ctor as any)(count);
+                for (let i = 0; i < count; i++) {
+                    const val = data[(offset + start + i) * dimSize + dim];
+                    arr[i] = val;
+                    val < min && (min = val);
+                    val > max && (max = val);
+                }
+                storage.push(arr);
+                extent.push([min, max]);
+            }
+
+            return {
+                storage,
+                extent
+            };
+        };
+
         const countForTypedArray: DefaultDataProvider['count'] = function (
             this: DefaultDataProvider
         ) {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org