我创建了一个简单的应用程序,它从video
标记中获取照片并将其设置为灰色,在此处完整提供:Canvas WebWorker PoC:
const photoParams = [
0, //x
0, //y
320, //width
240, //height
];
async function startVideo () {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: true,
});
const video = document.querySelector('#video');
video.srcObject = stream;
video.play();
return video;
}
function takePhoto () {
const video = document.querySelector('#video');
const canvas = document.querySelector('#canvas');
canvas.width = 320;
canvas.height = 240;
const context = canvas.getContext('2d');
context.drawImage(video, ...photoParams);
const imageData = applyFilter({imageData:
context.getImageData(...photoParams)});
context.putImageData(imageData, 0, 0);
return canvas.toDataURL("image/png")
}
function setPhoto () {
const photo = takePhoto();
const image = document.querySelector('#image');
image.src = photo;
}
startVideo();
const button = document.querySelector('#button');
button.addEventListener('click', setPhoto);
在其中一个函数中,我放置了一个很长的,不必要的for
循环来使它变得非常慢:
function transformPixels ({data}) {
let rawPixels;
const array = new Array(2000);
for (element of array) {
rawPixels = [];
const pixels = getPixels({
data,
});
const filteredPixels = [];
for (const pixel of pixels) {
const average = getAverage(pixel);
filteredPixels.push(new Pixel({
red: average,
green: average,
blue: average,
alpha: pixel.alpha,
}));
}
for (const pixel of filteredPixels) {
rawPixels.push(...pixel.toRawPixels());
}
}
return rawPixels;
};
我创建了Web Worker版本,正如我所说,它应该更快,因为它不会破坏主线程:
function setPhotoWorker () {
const video = document.querySelector('#video');
const canvas = document.querySelector('#canvas');
canvas.width = 320;
canvas.height = 240;
const context = canvas.getContext('2d');
context.drawImage(video, ...photoParams);
const imageData = context.getImageData(...photoParams);
const worker = new Worker('filter-worker.js');
worker.onmessage = (event) => {
const rawPixelsArray = [...JSON.parse(event.data)];
rawPixelsArray.forEach((element, index) => {
imageData.data[index] = element;
});
context.putImageData(imageData, 0, 0);
const image = document.querySelector('#image');
image.src = canvas.toDataURL("image/png");
}
worker.postMessage(JSON.stringify([...imageData.data]));
}
可以这样运行:
button.addEventListener('click', setPhotoWorker);
工作人员代码与单线程版本几乎完全相同,除了一件事 - 提高消息传递性能,string is sent instead of array of numbers:
worker.onmessage = (event) => {
const rawPixelsArray = [...JSON.parse(event.data)];
};
//...
worker.postMessage(JSON.stringify([...imageData.data]));
在filter-worker.js
内:
onmessage = (data) => {
const rawPixels = transformPixels({data: JSON.parse(data.data)});
postMessage(JSON.stringify(rawPixels));
};
问题是 worker版本总是比主线程版本慢20-25%。首先,我认为这可能是消息的大小,但在我的笔记本电脑中,我有640 x 480相机,它提供了307 200件物品 - 我认为这些物品价格不够昂贵,因为2000 for
迭代,导致结果:主线程:约160秒,工人约200秒。您可以从Github repo下载应用并自行查看。这里的模式非常相似 - 工人总是慢20-25%。如果不使用JSON API
,工人需要220秒才能完成工作。我认为唯一的一个原因是工作线程的优先级非常低,而在我的应用程序中,主线程没有太多的事情要做,它只是速度慢 - 而在实际应用中,主线程可能比较繁忙,工人会赢。你有什么想法为什么工人这么慢?谢谢你的每一个答案。