getImageData-网络工作者-如何减少垃圾收集? [英] getImageData - Web workers - How can I reduce garbage collection?

查看:47
本文介绍了getImageData-网络工作者-如何减少垃圾收集?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在运行一个示例Web画布更新脚本,但是我注意到它每隔几秒钟就会停顿大约200毫秒。通常循环时间约为15毫秒。

I've got a sample web-worker canvas update script running, but I've noticed it stalls every few seconds for about 200ms. The usual loop time is around 15ms.

我猜这是垃圾回收-看起来像分析器一样。

I'm guessing this is garbage collection - it looks like it from the profiler.

http://codepen.io/SarahC/pen/bgBoMM

我认为它在此函数中:

function nextFrame(){
  timeChart.start();
  workersWorking = workerCount;
  var stripHeight = ~~( h / workerCount );
  for(var i = 0; i < workerCount; i++){
    var localImageData = ctx.getImageData(0, stripHeight * i, w, stripHeight); /// This needs putting in constant memory.... GC takes ages here.
    workers[i].postMessage({imageData: localImageData, YPosition: stripHeight * i, threadNumber: i});
  }
}

如果正是这一点使所有垃圾存储器,我不知道如何为这些数据块继续使用相同的内存区域。

If it is this bit that's making all the garbage memory, I don't know what I can do to keep using the same area of memory for these blocks of data.

推荐答案

传递imageData的缓冲区而不是imageData本身。



这样,您的缓冲区已已转移 (使用 zero-copy 操作),并且不会再污染主线程的内存。

否则,当您不进行传输时,对象将被结构化克隆(就像您是否执行了 JSON.parse(JSON.stringify(yourObject)); 一样),这意味着您的计算机在其中保存了三个相同数据的副本从工作程序发送回主线程时内存。

Pass the buffer of your imageData instead of the imageData itself.

This way, your buffer is transferred (with a zero-copy operation), and doesn't pollute main thread's memory anymore.
Otherwise, when you don't transfer it your object is structured cloned (just like if you did JSON.parse(JSON.stringify(yourObject));), which means that you computer holds three copies of the same data in memory when you send back from the worker to the main thread.

请注意,当在工作程序中传递时,imageData的数据在主线程中不再可用(它如果尝试将putImageData()抛出错误,将抛出错误。
不幸的是,我不知道一种更改ImageData缓冲区的好方法,但是由于 ImageData() 构造函数(显然IE中仍不支持。 ..),实际上只会创建一个指向arrayBuffer的指针。

Note that when being passed in the worker, the imageData's data is not available anymore in the main thread (it will throw an error if i.e you try to putImageData() it). I unfortunately don't know of a good way to change the buffer of an ImageData, but you can set a buffer at the creation thanks to the ImageData() constructor (apparently still not supported in IE...), which will indeed just create a pointer to the arrayBuffer.

因此,当所有这些都受支持时,只有ImageData结构(基本上是对象 {width:XXX,height:XXX} )而不是重的缓冲区被创建。其他所有东西都只是移动了,不会污染内存。

So when all this is supported, only the ImageData structure (basically an object {width:XXX, height:XXX}) is created instead of an heavy buffer. Everything else is just moved, and doesn't pollute the memory.

let workerURL = URL.createObjectURL(new Blob([workerScript.textContent], {
  type: 'application/javascript'
}));

const worker = new Worker(workerURL);
worker.onmessage = e => {
  let buf = e.data,
    arr = new Uint8ClampedArray(buf),
    processedImageData;
  try {
    processedImageData = new ImageData(arr, imageData.width, imageData.height);
  } catch (e) {
    processedImageData = ctx.createImageData(imageData.width, imageData.height);
    processedImageData.data.set(arr);
  }
  // checks that we didn't created an useless buffer in this last step
  // IE will because it doesn't support new ImageData(buf)
  console.log('Does our TypedArray share the same buffer as the one we received ? ',
              arr.buffer === buf);
  console.log('Does our new imageData share the same buffer as the one we received ? ',
              processedImageData.data.buffer === buf);
  // Note that here a check for the original imageData's buffer has no sense
  //       since it has been emptied
  ctx.putImageData(processedImageData, 0, 0);
}

const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(20, 20, 60, 80);
let imageData = ctx.getImageData(0, 0, 300, 150);
// pass it as transferable
worker.postMessage(imageData.data.buffer, [imageData.data.buffer]);
console.log(imageData.data.length, 'now empty')

<script type="worker-script" id="workerScript">
	self.onmessage = e => {
		let buf = e.data,
		arr = new Uint8Array(buf);
		console.log('worker received', buf);
		for(let i =0; i<arr.length; i+=4){
			arr[i] = (arr[i] + 128) % 255;
			arr[i+1] = (arr[i+1] + 128) % 255;
			arr[i+2] = (arr[i+2] + 128) % 255;			
			}
		self.postMessage(buf, [buf]);
		// this won't print in stacksnippet's console
		// you have to check your dev tools' one
		console.log('worker now holds', buf.byteLength, 'empty');

		};
</script>
<canvas id="canvas"></canvas>

以及使用结构克隆的反例:

And a counter example using structure cloning :

let workerURL = URL.createObjectURL(new Blob([workerScript.textContent], {
  type: 'application/javascript'
}));

const worker = new Worker(workerURL);
worker.onmessage = e => {
  let buf = e.data;
  // so our original imageData's arrayBuffer is still available
  imageData.data.set(buf);
  // Here we can check for equality with the first arrayBuffer
  console.log('Is the first bufferArray the same as the one we received ?', imageData.data.buffer === buf);  
  ctx.putImageData(imageData, 0, 0);
}

const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(20, 20, 60, 80);
let imageData = ctx.getImageData(0, 0, 300, 150);
// pass it as transferable
worker.postMessage(imageData.data.buffer);
console.log(imageData.data.length, 'not empty')

<script type="worker-script" id="workerScript">
	self.onmessage = e => {
		let buf = e.data,
		arr = new Uint8Array(buf);
		console.log('worker received', buf);
		for(let i =0; i<arr.length; i+=4){
			arr[i] = (arr[i] + 128) % 255;
			arr[i+1] = (arr[i+1] + 128) % 255;
			arr[i+2] = (arr[i+2] + 128) % 255;			
			}
		console.log(arr);
		self.postMessage(buf);
		// this won't print in stacksnippet's console
		// you have to check your dev tools' one
		console.log('worker now holds', buf.byteLength, 'full');
		};
</script>
<canvas id="canvas"></canvas>

这篇关于getImageData-网络工作者-如何减少垃圾收集?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆