Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
半栈学徒
incubator-echarts
提交
d6f63003
I
incubator-echarts
项目概览
半栈学徒
/
incubator-echarts
与 Fork 源项目一致
从无法访问的项目Fork
通知
5
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
I
incubator-echarts
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
d6f63003
编写于
9月 23, 2020
作者:
P
pissang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(sample): optimize performance of lttb sampling
上级
e9164538
变更
3
显示空白变更内容
内联
并排
Showing
3 changed file
with
201 addition
and
202 deletion
+201
-202
src/data/List.ts
src/data/List.ts
+65
-54
src/processor/dataSample.ts
src/processor/dataSample.ts
+1
-1
test/sample-compare.html
test/sample-compare.html
+135
-147
未找到文件。
src/data/List.ts
浏览文件 @
d6f63003
...
@@ -1694,15 +1694,14 @@ class List<
...
@@ -1694,15 +1694,14 @@ class List<
/**
/**
* Large data down sampling using largest-triangle-three-buckets
* Large data down sampling using largest-triangle-three-buckets
* https://github.com/pingec/downsample-lttb
* @param {string} baseDimension
* @param {string} baseDimension
* @param {string} valueDimension
* @param {string} valueDimension
* @param {number}
threshold target counts
* @param {number}
rate
*/
*/
lttbDownSample
(
lttbDownSample
(
baseDimension
:
DimensionName
,
baseDimension
:
DimensionName
,
valueDimension
:
DimensionName
,
valueDimension
:
DimensionName
,
threshold
:
number
rate
:
number
)
{
)
{
const
list
=
cloneListForMapAndSample
(
this
,
[
baseDimension
,
valueDimension
]);
const
list
=
cloneListForMapAndSample
(
this
,
[
baseDimension
,
valueDimension
]);
const
targetStorage
=
list
.
_storage
;
const
targetStorage
=
list
.
_storage
;
...
@@ -1711,72 +1710,84 @@ class List<
...
@@ -1711,72 +1710,84 @@ class List<
const
len
=
this
.
count
();
const
len
=
this
.
count
();
const
chunkSize
=
this
.
_chunkSize
;
const
chunkSize
=
this
.
_chunkSize
;
const
newIndices
=
new
(
getIndicesCtor
(
this
))(
len
);
const
newIndices
=
new
(
getIndicesCtor
(
this
))(
len
);
const
getPair
=
(
i
:
number
)
:
Array
<
any
>
=>
{
const
originalChunkIndex
=
mathFloor
(
i
/
chunkSize
);
const
originalChunkOffset
=
i
%
chunkSize
;
return
[
baseDimStore
[
originalChunkIndex
][
originalChunkOffset
],
valueDimStore
[
originalChunkIndex
][
originalChunkOffset
]
];
};
let
sampledIndex
=
0
;
let
sampledIndex
=
0
;
const
every
=
(
len
-
2
)
/
(
threshold
-
2
);
const
frameSize
=
mathFloor
(
1
/
rate
);
let
a
=
0
;
let
currentSelectedIdx
=
0
;
let
maxArea
;
let
maxArea
;
let
area
;
let
area
;
let
nextA
;
let
nextSelectedIdx
;
for
(
let
chunkIdx
=
0
;
chunkIdx
<
this
.
_chunkCount
;
chunkIdx
++
)
{
const
chunkOffset
=
chunkSize
*
chunkIdx
;
const
selfChunkSize
=
Math
.
min
(
len
-
chunkOffset
,
chunkSize
);
const
chunkFrameCount
=
Math
.
ceil
((
selfChunkSize
-
2
)
/
frameSize
);
const
baseDimChunk
=
baseDimStore
[
chunkIdx
];
const
valueDimChunk
=
valueDimStore
[
chunkIdx
];
newIndices
[
sampledIndex
++
]
=
a
;
// The first frame is the first data.
for
(
let
i
=
0
;
i
<
threshold
-
2
;
i
++
)
{
newIndices
[
sampledIndex
++
]
=
currentSelectedIdx
;
for
(
let
frame
=
0
;
frame
<
chunkFrameCount
-
2
;
frame
++
)
{
let
avgX
=
0
;
let
avgX
=
0
;
let
avgY
=
0
;
let
avgY
=
0
;
let
avgRangeStart
=
mathFloor
((
i
+
1
)
*
every
)
+
1
;
let
avgRangeStart
=
(
frame
+
1
)
*
frameSize
+
1
+
chunkOffset
;
let
avgRangeEnd
=
mathFloor
((
i
+
2
)
*
every
)
+
1
;
const
avgRangeEnd
=
Math
.
min
((
frame
+
2
)
*
frameSize
+
1
,
selfChunkSize
)
+
chunkOffset
;
avgRangeEnd
=
avgRangeEnd
<
len
?
avgRangeEnd
:
len
;
const
avgRangeLength
=
avgRangeEnd
-
avgRangeStart
;
const
avgRangeLength
=
avgRangeEnd
-
avgRangeStart
;
for
(;
avgRangeStart
<
avgRangeEnd
;
avgRangeStart
++
)
{
for
(;
avgRangeStart
<
avgRangeEnd
;
avgRangeStart
++
)
{
avgX
+=
getPair
(
avgRangeStart
)[
0
]
*
1
;
// * 1 enforces Number (value may be Date)
const
x
=
baseDimChunk
[
avgRangeStart
]
as
number
;
avgY
+=
getPair
(
avgRangeStart
)[
1
]
*
1
;
const
y
=
valueDimChunk
[
avgRangeStart
]
as
number
;
if
(
isNaN
(
x
)
||
isNaN
(
y
))
{
continue
;
}
avgX
+=
x
;
avgY
+=
y
;
}
}
avgX
/=
avgRangeLength
;
avgX
/=
avgRangeLength
;
avgY
/=
avgRangeLength
;
avgY
/=
avgRangeLength
;
// Get the range for this bucket
// Get the range for this bucket
let
rangeOffs
=
mathFloor
((
i
+
0
)
*
every
)
+
1
;
let
rangeOffs
=
(
frame
)
*
frameSize
+
1
+
chunkOffset
;
const
rangeTo
=
mathFloor
((
i
+
1
)
*
every
)
+
1
;
const
rangeTo
=
(
frame
+
1
)
*
frameSize
+
1
+
chunkOffset
;
// Point a
// Point A
const
pointAX
=
getPair
(
a
)[
0
]
*
1
;
// enforce Number (value may be Date)
const
pointAX
=
baseDimChunk
[
currentSelectedIdx
]
as
number
;
const
pointAY
=
getPair
(
a
)[
1
]
*
1
;
const
pointAY
=
valueDimChunk
[
currentSelectedIdx
]
as
number
;
let
allNaN
=
true
;
maxArea
=
area
=
-
1
;
maxArea
=
area
=
-
1
;
for
(;
rangeOffs
<
rangeTo
;
rangeOffs
++
)
{
for
(;
rangeOffs
<
rangeTo
;
rangeOffs
++
)
{
const
y
=
valueDimChunk
[
rangeOffs
]
as
number
;
const
x
=
baseDimChunk
[
rangeOffs
]
as
number
;
if
(
isNaN
(
x
)
||
isNaN
(
y
))
{
continue
;
}
allNaN
=
false
;
// Calculate triangle area over three buckets
// Calculate triangle area over three buckets
area
=
Math
.
abs
((
pointAX
-
avgX
)
*
(
getPair
(
rangeOffs
)[
1
]
-
pointAY
)
area
=
Math
.
abs
((
pointAX
-
avgX
)
*
(
y
-
pointAY
)
-
(
pointAX
-
getPair
(
rangeOffs
)[
0
]
)
*
(
avgY
-
pointAY
)
-
(
pointAX
-
x
)
*
(
avgY
-
pointAY
)
)
*
0.5
;
)
;
if
(
area
>
maxArea
)
{
if
(
area
>
maxArea
)
{
maxArea
=
area
;
maxArea
=
area
;
nextA
=
rangeOffs
;
// Next a is this b
nextSelectedIdx
=
rangeOffs
;
// Next a is this b
}
}
}
}
newIndices
[
sampledIndex
++
]
=
nextA
;
if
(
!
allNaN
)
{
newIndices
[
sampledIndex
++
]
=
nextSelectedIdx
;
}
a
=
nextA
;
// This a is the next a (chosen b)
currentSelectedIdx
=
nextSelectedIdx
;
// This a is the next a (chosen b)
}
// The last frame is the last data.
newIndices
[
sampledIndex
++
]
=
selfChunkSize
-
1
;
}
}
newIndices
[
sampledIndex
++
]
=
len
-
1
;
list
.
_count
=
sampledIndex
;
list
.
_count
=
sampledIndex
;
list
.
_indices
=
newIndices
;
list
.
_indices
=
newIndices
;
...
...
src/processor/dataSample.ts
浏览文件 @
d6f63003
...
@@ -95,7 +95,7 @@ export default function (seriesType: string): StageHandler {
...
@@ -95,7 +95,7 @@ export default function (seriesType: string): StageHandler {
if
(
rate
>
1
)
{
if
(
rate
>
1
)
{
if
(
sampling
===
'
lttb
'
)
{
if
(
sampling
===
'
lttb
'
)
{
seriesModel
.
setData
(
data
.
lttbDownSample
(
seriesModel
.
setData
(
data
.
lttbDownSample
(
data
.
mapDimension
(
baseAxis
.
dim
),
data
.
mapDimension
(
valueAxis
.
dim
),
siz
e
data
.
mapDimension
(
baseAxis
.
dim
),
data
.
mapDimension
(
valueAxis
.
dim
),
1
/
rat
e
));
));
}
}
let
sampler
;
let
sampler
;
...
...
test/sample-compare.html
浏览文件 @
d6f63003
...
@@ -20,17 +20,17 @@ under the License.
...
@@ -20,17 +20,17 @@ under the License.
<!doctype html>
<!doctype html>
<html>
<html>
<head>
<head>
<meta
charset=
"utf-8"
>
<meta
charset=
'utf-8'
>
<title>
ECharts Demo
</title>
<title>
Downsample Comparasions
</title>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
<meta
name=
'viewport'
content=
'width=device-width, initial-scale=1'
>
</head>
</head>
<body>
<body>
<h2
id=
"wait"
>
Loading lib....
</h2>
<h2
id=
'wait'
>
Loading lib....
</h2>
<div
id=
"container"
style=
"height: 600px; width: 100%;"
></div>
<div
id=
'container'
style=
'height: 600px; width: 1200px;'
></div>
<script
src=
"lib/esl.js"
></script>
<script
src=
'lib/esl.js'
></script>
<script
src=
"lib/config.js"
></script>
<script
src=
'lib/config.js'
></script>
<script>
<script>
require
([
require
([
'
echarts
'
'
echarts
'
...
@@ -46,135 +46,123 @@ under the License.
...
@@ -46,135 +46,123 @@ under the License.
}
}
function
prepData
(
packed
)
{
function
prepData
(
packed
)
{
//
console.time('prep');
console
.
time
(
'
prep
'
);
// epoch,idl,recv,send,read,writ,used,free
// epoch,idl,recv,send,read,writ,used,free
const
numFields
=
packed
[
0
];
var
numFields
=
packed
[
0
];
packed
=
packed
.
slice
(
numFields
+
1
);
packed
=
packed
.
slice
(
numFields
+
1
);
var
cpu
=
Array
(
packed
.
length
/
numFields
)
;
var
repeatTimes
=
1
;
var
data
=
new
Float64Array
((
packed
.
length
/
numFields
)
*
4
*
repeatTimes
);
var
off
=
0
;
var
date
=
packed
[
0
];
for
(
let
repeat
=
0
;
repeat
<
repeatTimes
;
repeat
++
)
{
for
(
let
i
=
0
,
j
=
0
;
i
<
packed
.
length
;
i
+=
numFields
,
j
++
)
{
for
(
let
i
=
0
,
j
=
0
;
i
<
packed
.
length
;
i
+=
numFields
,
j
++
)
{
let
date
=
packed
[
i
]
*
60
*
1000
;
date
+=
1
;
cpu
[
j
]
=
[
date
,
round3
(
100
-
packed
[
i
+
1
])];
data
[
off
++
]
=
date
*
60
*
1000
;
data
[
off
++
]
=
round3
(
100
-
packed
[
i
+
1
]);
data
[
off
++
]
=
round2
(
(
100
*
packed
[
i
+
5
])
/
(
packed
[
i
+
5
]
+
packed
[
i
+
6
])
);
data
[
off
++
]
=
packed
[
i
+
3
];
}
}
}
console
.
timeEnd
(
'
prep
'
);
// console.timeEnd('prep');
return
data
;
return
[
cpu
];
}
}
function
makeChart
(
data
)
{
function
makeChart
(
data
)
{
console
.
time
(
'
chart
'
);
var
dom
=
document
.
getElementById
(
'
container
'
);
var
dom
=
document
.
getElementById
(
"
container
"
);
var
myChart
=
echarts
.
init
(
dom
);
var
myChart
=
echarts
.
init
(
dom
);
let
opts
=
{
let
opts
=
{
animation
:
false
,
dataset
:
{
source
:
data
,
dimensions
:
[
'
date
'
,
'
cpu
'
,
'
ram
'
,
'
tcpout
'
]
},
tooltip
:
{
trigger
:
'
axis
'
},
legend
:
{},
grid
:
{
grid
:
{
left
:
40
,
containLabel
:
true
,
top
:
0
,
left
:
0
,
top
:
50
,
right
:
0
,
right
:
0
,
bottom
:
30
,
bottom
:
30
},
},
xAxis
:
{
xAxis
:
{
type
:
'
time
'
,
type
:
'
time
'
splitLine
:
{
show
:
false
},
data
:
data
[
0
],
},
},
yAxis
:
{
yAxis
:
[{
type
:
'
value
'
type
:
'
value
'
,
},
max
:
100
,
legend
:
{
axisLabel
:
{
},
formatter
:
'
{value} %
'
series
:
[
{
name
:
'
none
'
,
type
:
'
line
'
,
showSymbol
:
false
,
hoverAnimation
:
false
,
data
:
data
[
0
],
lineStyle
:
{
normal
:
{
opacity
:
0.5
,
width
:
1
}
}
},
{
type
:
'
value
'
,
max
:
100
,
axisLabel
:
{
formatter
:
'
{value} MB
'
}
}
}
,
}]
,
{
series
:
[
{
name
:
'
lttb
'
,
name
:
'
CPU
'
,
type
:
'
line
'
,
type
:
'
line
'
,
showSymbol
:
false
,
showSymbol
:
false
,
hoverAnimation
:
false
,
data
:
data
[
0
],
sampling
:
'
lttb
'
,
sampling
:
'
lttb
'
,
lineStyle
:
{
lineStyle
:
{
width
:
1
},
normal
:
{
emphasis
:
{
lineStyle
:
{
width
:
1
}
},
opacity
:
0.5
,
encode
:
{
width
:
1
x
:
'
date
'
,
}
y
:
'
cpu
'
}
}
},
},
{
{
name
:
'
RAM
'
,
name
:
'
average
'
,
type
:
'
line
'
,
type
:
'
line
'
,
yAxisIndex
:
1
,
showSymbol
:
false
,
showSymbol
:
false
,
hoverAnimation
:
false
,
sampling
:
'
lttb
'
,
data
:
data
[
0
],
lineStyle
:
{
width
:
1
},
sampling
:
'
average
'
,
emphasis
:
{
lineStyle
:
{
width
:
1
}
},
lineStyle
:
{
encode
:
{
normal
:
{
x
:
'
date
'
,
opacity
:
0.5
,
y
:
'
ram
'
width
:
1
}
}
}
},
},
{
{
name
:
'
TCP Out
'
,
name
:
'
max
'
,
type
:
'
line
'
,
type
:
'
line
'
,
yAxisIndex
:
1
,
showSymbol
:
false
,
showSymbol
:
false
,
hoverAnimation
:
false
,
sampling
:
'
lttb
'
,
data
:
data
[
0
],
lineStyle
:
{
width
:
1
},
sampling
:
'
max
'
,
emphasis
:
{
lineStyle
:
{
width
:
1
}
},
lineStyle
:
{
encode
:
{
normal
:
{
x
:
'
date
'
,
opacity
:
0.5
,
y
:
'
tcpout
'
width
:
1
}
}
},
{
name
:
'
min
'
,
type
:
'
line
'
,
showSymbol
:
false
,
hoverAnimation
:
false
,
data
:
data
[
0
],
sampling
:
'
min
'
,
lineStyle
:
{
normal
:
{
opacity
:
0.5
,
width
:
1
}
}
}
},
}]
]
};
};
const
startTime
=
performance
.
now
();
myChart
.
setOption
(
opts
,
true
);
myChart
.
setOption
(
opts
,
true
);
const
endTime
=
performance
.
now
();
wait
.
textContent
=
"
Done!
"
;
wait
.
textContent
=
'
Done!
'
+
(
endTime
-
startTime
).
toFixed
(
0
)
+
'
ms
'
;
console
.
timeEnd
(
'
chart
'
);
}
}
let
wait
=
document
.
getElementById
(
"
wait
"
);
let
wait
=
document
.
getElementById
(
'
wait
'
);
wait
.
textContent
=
"
Fetching data.json (2.07MB)....
"
;
wait
.
textContent
=
'
Fetching data.json (2.07MB)....
'
;
fetch
(
"
./data/large-data.json
"
).
then
(
r
=>
r
.
json
()).
then
(
packed
=>
{
fetch
(
'
data/large-data.json
'
)
wait
.
textContent
=
"
Rendering...
"
;
.
then
(
r
=>
r
.
json
())
.
then
(
packed
=>
{
wait
.
textContent
=
'
Rendering...
'
;
let
data
=
prepData
(
packed
);
let
data
=
prepData
(
packed
);
setTimeout
(()
=>
makeChart
(
data
),
0
);
setTimeout
(()
=>
makeChart
(
data
),
20
0
);
});
});
});
});
</script>
</script>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录