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
;
newIndices
[
sampledIndex
++
]
=
a
;
for
(
let
chunkIdx
=
0
;
chunkIdx
<
this
.
_chunkCount
;
chunkIdx
++
)
{
for
(
let
i
=
0
;
i
<
threshold
-
2
;
i
++
)
{
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
];
// The first frame is the first data.
newIndices
[
sampledIndex
++
]
=
currentSelectedIdx
;
for
(
let
frame
=
0
;
frame
<
chunkFrameCount
-
2
;
frame
++
)
{
let
avgX
=
0
;
let
avgY
=
0
;
let
avgRangeStart
=
(
frame
+
1
)
*
frameSize
+
1
+
chunkOffset
;
const
avgRangeEnd
=
Math
.
min
((
frame
+
2
)
*
frameSize
+
1
,
selfChunkSize
)
+
chunkOffset
;
const
avgRangeLength
=
avgRangeEnd
-
avgRangeStart
;
for
(;
avgRangeStart
<
avgRangeEnd
;
avgRangeStart
++
)
{
const
x
=
baseDimChunk
[
avgRangeStart
]
as
number
;
const
y
=
valueDimChunk
[
avgRangeStart
]
as
number
;
if
(
isNaN
(
x
)
||
isNaN
(
y
))
{
continue
;
}
avgX
+=
x
;
avgY
+=
y
;
}
avgX
/=
avgRangeLength
;
avgY
/=
avgRangeLength
;
let
avgX
=
0
;
// Get the range for this bucket
let
avgY
=
0
;
let
rangeOffs
=
(
frame
)
*
frameSize
+
1
+
chunkOffset
;
let
avgRangeStart
=
mathFloor
((
i
+
1
)
*
every
)
+
1
;
const
rangeTo
=
(
frame
+
1
)
*
frameSize
+
1
+
chunkOffset
;
let
avgRangeEnd
=
mathFloor
((
i
+
2
)
*
every
)
+
1
;
avgRangeEnd
=
avgRangeEnd
<
len
?
avgRangeEnd
:
len
;
// Point A
const
pointAX
=
baseDimChunk
[
currentSelectedIdx
]
as
number
;
const
pointAY
=
valueDimChunk
[
currentSelectedIdx
]
as
number
;
let
allNaN
=
true
;
const
avgRangeLength
=
avgRangeEnd
-
avgRangeStart
;
maxArea
=
area
=
-
1
;
for
(;
avgRangeStart
<
avgRangeEnd
;
avgRangeStart
++
)
{
for
(;
rangeOffs
<
rangeTo
;
rangeOffs
++
)
{
avgX
+=
getPair
(
avgRangeStart
)[
0
]
*
1
;
// * 1 enforces Number (value may be Date)
const
y
=
valueDimChunk
[
rangeOffs
]
as
number
;
avgY
+=
getPair
(
avgRangeStart
)[
1
]
*
1
;
const
x
=
baseDimChunk
[
rangeOffs
]
as
number
;
}
if
(
isNaN
(
x
)
||
isNaN
(
y
))
{
avgX
/=
avgRangeLength
;
continue
;
avgY
/=
avgRangeLength
;
}
allNaN
=
false
;
// Get the range for this bucket
// Calculate triangle area over three buckets
let
rangeOffs
=
mathFloor
((
i
+
0
)
*
every
)
+
1
;
area
=
Math
.
abs
((
pointAX
-
avgX
)
*
(
y
-
pointAY
)
const
rangeTo
=
mathFloor
((
i
+
1
)
*
every
)
+
1
;
-
(
pointAX
-
x
)
*
(
avgY
-
pointAY
)
);
// Point a
if
(
area
>
maxArea
)
{
const
pointAX
=
getPair
(
a
)[
0
]
*
1
;
// enforce Number (value may be Date)
maxArea
=
area
;
const
pointAY
=
getPair
(
a
)[
1
]
*
1
;
nextSelectedIdx
=
rangeOffs
;
// Next a is this b
}
maxArea
=
area
=
-
1
;
for
(;
rangeOffs
<
rangeTo
;
rangeOffs
++
)
{
// Calculate triangle area over three buckets
area
=
Math
.
abs
((
pointAX
-
avgX
)
*
(
getPair
(
rangeOffs
)[
1
]
-
pointAY
)
-
(
pointAX
-
getPair
(
rangeOffs
)[
0
])
*
(
avgY
-
pointAY
)
)
*
0.5
;
if
(
area
>
maxArea
)
{
maxArea
=
area
;
nextA
=
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,162 +20,150 @@ under the License.
...
@@ -20,162 +20,150 @@ 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
'
// 'echarts/chart/sankey',
// 'echarts/chart/sankey',
// 'echarts/component/tooltip'
// 'echarts/component/tooltip'
],
function
(
echarts
)
{
],
function
(
echarts
)
{
function
round2
(
val
)
{
function
round2
(
val
)
{
return
Math
.
round
(
val
*
100
)
/
100
;
return
Math
.
round
(
val
*
100
)
/
100
;
}
}
function
round3
(
val
)
{
function
round3
(
val
)
{
return
Math
.
round
(
val
*
1000
)
/
1000
;
return
Math
.
round
(
val
*
1000
)
/
1000
;
}
}
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
;
for
(
let
i
=
0
,
j
=
0
;
i
<
packed
.
length
;
i
+=
numFields
,
j
++
)
{
var
data
=
new
Float64Array
((
packed
.
length
/
numFields
)
*
4
*
repeatTimes
);
let
date
=
packed
[
i
]
*
60
*
1000
;
cpu
[
j
]
=
[
date
,
round3
(
100
-
packed
[
i
+
1
])];
var
off
=
0
;
}
var
date
=
packed
[
0
];
for
(
let
repeat
=
0
;
repeat
<
repeatTimes
;
repeat
++
)
{
// console.timeEnd('prep');
for
(
let
i
=
0
,
j
=
0
;
i
<
packed
.
length
;
i
+=
numFields
,
j
++
)
{
date
+=
1
;
return
[
cpu
];
data
[
off
++
]
=
date
*
60
*
1000
;
}
data
[
off
++
]
=
round3
(
100
-
packed
[
i
+
1
]);
data
[
off
++
]
=
round2
(
function
makeChart
(
data
)
{
(
100
*
packed
[
i
+
5
])
/
(
packed
[
i
+
5
]
+
packed
[
i
+
6
])
console
.
time
(
'
chart
'
);
);
data
[
off
++
]
=
packed
[
i
+
3
];
var
dom
=
document
.
getElementById
(
"
container
"
);
}
var
myChart
=
echarts
.
init
(
dom
);
}
console
.
timeEnd
(
'
prep
'
);
let
opts
=
{
grid
:
{
return
data
;
left
:
40
,
}
top
:
0
,
right
:
0
,
function
makeChart
(
data
)
{
bottom
:
30
,
var
dom
=
document
.
getElementById
(
'
container
'
);
},
var
myChart
=
echarts
.
init
(
dom
);
xAxis
:
{
type
:
'
time
'
,
let
opts
=
{
splitLine
:
{
animation
:
false
,
show
:
false
dataset
:
{
},
source
:
data
,
data
:
data
[
0
],
dimensions
:
[
'
date
'
,
'
cpu
'
,
'
ram
'
,
'
tcpout
'
]
},
},
yAxis
:
{
tooltip
:
{
type
:
'
value
'
trigger
:
'
axis
'
},
},
legend
:
{
legend
:
{},
},
grid
:
{
series
:
[
containLabel
:
true
,
{
left
:
0
,
name
:
'
none
'
,
top
:
50
,
type
:
'
line
'
,
right
:
0
,
showSymbol
:
false
,
bottom
:
30
hoverAnimation
:
false
,
},
data
:
data
[
0
],
xAxis
:
{
lineStyle
:
{
type
:
'
time
'
normal
:
{
},
opacity
:
0.5
,
yAxis
:
[{
width
:
1
type
:
'
value
'
,
}
max
:
100
,
}
axisLabel
:
{
},
formatter
:
'
{value} %
'
{
}
name
:
'
lttb
'
,
},
{
type
:
'
line
'
,
type
:
'
value
'
,
showSymbol
:
false
,
max
:
100
,
hoverAnimation
:
false
,
axisLabel
:
{
data
:
data
[
0
],
formatter
:
'
{value} MB
'
sampling
:
'
lttb
'
,
}
lineStyle
:
{
}],
normal
:
{
series
:
[{
opacity
:
0.5
,
name
:
'
CPU
'
,
width
:
1
type
:
'
line
'
,
}
showSymbol
:
false
,
}
sampling
:
'
lttb
'
,
},
lineStyle
:
{
width
:
1
},
{
emphasis
:
{
lineStyle
:
{
width
:
1
}
},
name
:
'
average
'
,
encode
:
{
type
:
'
line
'
,
x
:
'
date
'
,
showSymbol
:
false
,
y
:
'
cpu
'
hoverAnimation
:
false
,
}
data
:
data
[
0
],
},
{
sampling
:
'
average
'
,
name
:
'
RAM
'
,
lineStyle
:
{
type
:
'
line
'
,
normal
:
{
yAxisIndex
:
1
,
opacity
:
0.5
,
showSymbol
:
false
,
width
:
1
sampling
:
'
lttb
'
,
}
lineStyle
:
{
width
:
1
},
}
emphasis
:
{
lineStyle
:
{
width
:
1
}
},
},
encode
:
{
{
x
:
'
date
'
,
name
:
'
max
'
,
y
:
'
ram
'
type
:
'
line
'
,
}
showSymbol
:
false
,
},
{
hoverAnimation
:
false
,
name
:
'
TCP Out
'
,
data
:
data
[
0
],
type
:
'
line
'
,
sampling
:
'
max
'
,
yAxisIndex
:
1
,
lineStyle
:
{
showSymbol
:
false
,
normal
:
{
sampling
:
'
lttb
'
,
opacity
:
0.5
,
lineStyle
:
{
width
:
1
},
width
:
1
emphasis
:
{
lineStyle
:
{
width
:
1
}
},
}
encode
:
{
}
x
:
'
date
'
,
},
y
:
'
tcpout
'
{
}
name
:
'
min
'
,
}]
type
:
'
line
'
,
};
showSymbol
:
false
,
const
startTime
=
performance
.
now
();
hoverAnimation
:
false
,
myChart
.
setOption
(
opts
,
true
);
data
:
data
[
0
],
const
endTime
=
performance
.
now
();
sampling
:
'
min
'
,
wait
.
textContent
=
'
Done!
'
+
(
endTime
-
startTime
).
toFixed
(
0
)
+
'
ms
'
;
lineStyle
:
{
}
normal
:
{
opacity
:
0.5
,
let
wait
=
document
.
getElementById
(
'
wait
'
);
width
:
1
wait
.
textContent
=
'
Fetching data.json (2.07MB)....
'
;
}
fetch
(
'
data/large-data.json
'
)
}
.
then
(
r
=>
r
.
json
())
},
.
then
(
packed
=>
{
]
wait
.
textContent
=
'
Rendering...
'
;
};
let
data
=
prepData
(
packed
);
setTimeout
(()
=>
makeChart
(
data
),
200
);
myChart
.
setOption
(
opts
,
true
);
});
wait
.
textContent
=
"
Done!
"
;
console
.
timeEnd
(
'
chart
'
);
}
let
wait
=
document
.
getElementById
(
"
wait
"
);
wait
.
textContent
=
"
Fetching data.json (2.07MB)....
"
;
fetch
(
"
./data/large-data.json
"
).
then
(
r
=>
r
.
json
()).
then
(
packed
=>
{
wait
.
textContent
=
"
Rendering...
"
;
let
data
=
prepData
(
packed
);
setTimeout
(()
=>
makeChart
(
data
),
0
);
});
});
});
</script>
</script>
</body>
</body>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录