I naively tried to load n canvas to the page, and compared the time needed to plot against the time taken shifting array.
(我天真地尝试将n个画布加载到页面,并将绘制所需的时间与移动数组所花费的时间进行了比较。)
tldr: Whichever method is used to shift the points, the method is negligible against the time needed to plot the points.
(tldr:无论使用哪种方法移动点,该方法与绘制点所需的时间都可以忽略不计。)
(~ 3%) ((?3%))
I only run the code in ff70 with casual js.
(我只在带有休闲js的ff70中运行代码。)
(For instance I am stocking an array of objects even though optimization may be available if I stock only floats) ((例如,即使我仅存储浮动对象,即使可以进行优化,我也要存储一组对象))
There are two kind of measures: push and refresh.
(有两种措施:推送和刷新。)
- push measures the time needed to shift a point and add a new one
(推式测量移动一个点并添加一个新点所需的时间)
- refresh measures the time needed to replot the canvas
(刷新度量重新绘制画布所需的时间)
Below three approaches for pushing: either push (Array.prototype.(shift|push), tail to a queue (and move the head), or nopole's approach
(以下三种推入方法:推入(Array.prototype。(shift | push),拖尾到队列(并移动头部)或nopole的方法)
Every 10ms I plot the time spent in the push
method.
(我每隔10ms绘制一次push
方法中的时间。)
On top of the picture, the cumulative time spent. (在图片顶部,花费了累计时间。)
I stop once a run has reached 100 points and reload the page for another run. (一旦运行达到100点,我就会停止,然后重新加载页面以进行另一次运行。)
The y axis is the same accross all runs
(y轴在所有行程中都相同)
- Push
(推)
push avg: (838+886+864)/3 = 862ms
(推平均:(838 + 886 + 864)/ 3 = 862ms)
- Queue
(队列)
push avg: (625+760+825)/3 = 736ms
(推平均(625 + 760 + 825)/ 3 = 736ms)
refresh avg: (40554+39934+40915+39194+39264+30480)/6 = 38390ms
(刷新平均:(40554 + 39934 + 40915 + 39194 + 39264 + 30480)/ 6 = 38390ms)
- Nopole
(诺波尔)
push avg: (792+861+871)/3 = 841ms
(推平均:(792 + 861 + 871)/ 3 = 841ms)
Notice that for one sample: (625/30480) we seem to have benefited from some cpu willing to work.
(请注意,对于一个示例:(625/30480),我们似乎已经从一些愿意工作的CPU中受益。)
So the shifting method feels even more irrelevant. (因此,移位方法变得更加无关紧要。)
It is hard to tell which approach is better, because of the few samples drought for each kind of methods and it is likely more an issue of cpu's overall workload rather than the page itself
(很难确定哪种方法更好,因为每种方法的样本干旱少,这很可能是cpu整体工作量的问题,而不是页面本身的问题)
To reproduce
(重现)
let timerPush = 0 let timerRefresh = 0 class Canvas { constructor (f, el, period) { this.w = 300 this.h = 300 this.points = [] const canvas = document.createElement('canvas') canvas.style.cssText = 'background:#eeeeee; margin:10px;' canvas.width = this.w canvas.height = this.h this.ctx = canvas.getContext('2d') this.ctx.transform(1, 0, 0, -1, 0, this.h / 2) this.ctx.lineWidth = 1 this.dw = this.w / this.MAX_POINTS this.dh = this.h / 2 el.appendChild(canvas) let x = 0 this.timer = setInterval(_ => { x += period this.push({ x, y: f(x) }) this.refresh() }, period * 1000) } refresh () { const now = performance.now() this.ctx.clearRect(0, -this.h / 2, this.w, this.h) this.ctx.beginPath() this._plot() this.ctx.stroke() this.ctx.closePath() timerRefresh += performance.now() - now } push (p) { const now = performance.now() this._push(p) timerPush += performance.now() - now } _plot () { if (!this.points.length) { return } this.ctx.moveTo(0 * this.dw, this.points[0].y * this.dh) for (let i = 1; i < this.points.length; ++i) { const p = this.points[i] this.ctx.lineTo(i * this.dw, py * this.dh) } } _push (p) { if (this.points.length == this.MAX_POINTS) { this.points.shift() } this.points.push(p) } MAX_POINTS = 100 } class CanvasQueue extends Canvas { constructor () { super(...arguments) this.tail = {} this.head = this.tail this.n = 0 } _plot () { if (!this.head.next.p) return let node = this.head.next this.ctx.moveTo(0 * this.dw, node.py * this.dh) let i = 1 node = node.next while (node) { this.ctx.lineTo(i * this.dw, node.py * this.dh) ++i node = node.next } } _push (p) { if (this.n === this.MAX_POINTS) { this.head = this.head.next } else { this.n++ } const node = { p } this.tail.next = node this.tail = node } } class CanvasNopole extends Canvas { constructor () { super(...arguments) this.start = 0 } _plot () { if (!this.points.length) { return } const s = this.start let z = 1 let startBack = 0 if (this.points[s]){ this.ctx.moveTo(0 * this.dw, this.points[s].y * this.dh) for (let i = s+1; i < this.points.length; ++i) { const p = this.points[i] this.ctx.lineTo(z++ * this.dw, py * this.dh) } }else{ this.ctx.moveTo(0 * this.dw, this.points[0].y * this.dh) startBack = 1 } for (let i = startBack; i < s; ++i) { const p = this.points[i] this.ctx.lineTo(z++ * this.dw, py * this.dh) } } _push (p) { this.points[this.start] = p this.start = (this.start + 1) % this.MAX_POINTS } } class CanvasSummary extends Canvas { constructor () { super(...arguments) this.ctx.resetTransform() this.ctx.transform(1, 0, 0, -1, 0, this.h) // we know beforehand that timer should not grow bigger const deltaTimer = 50 this.dh = this.h / deltaTimer this.old = timerPush } refresh () { this.ctx.clearRect(0, 0, this.w, this.h) this.ctx.beginPath() this.ctx.resetTransform() this.ctx.fillText(`push: ${timerPush} plot: ${timerRefresh}`, 5, 20) this.ctx.transform(1, 0, 0, -1, 0, this.h) this._plot() this.ctx.stroke() this.ctx.closePath() } push (p) { this._push(p) } } function run () { const $summary = document.querySelector('.summary') const $bench = document.querySelector('.bench') const cs = new CanvasSummary(x => { if (cs.points.length === cs.MAX_POINTS) { clearInterval(cs.timer) } const y = timerPush - cs.old cs.old = timerPush return y }, $summary, 1) //const canvas = Array(30).fill(0).map(x => new Canvas(Math.sin, $bench, 0.01)) //const canvas = Array(30).fill(0).map(x => new CanvasQueue(Math.sin, $bench, 0.01)) const canvas = Array(30).fill(0).map(x => new CanvasNopole(Math.sin, $bench, 0.01)) } run()
<section class="summary"></section> <hr/> <div class="bench"></div>