Comments 26
Оно не должно работать быстрее, потому что под капотом у воркеров живёт всё тот же js движок, который исполняется в тот же один поток, но на другом ядре. Единственное что может сказываться — области видимости. Поиск переменной в контексте window является на самом деле не дешевым удовольствием, о чём часто забывают.
Пытался использовать воркеры, но уперся в передачу ему параметров.
У меня через вебсокеты приходит большой объем данных в виде массива байт, и я хочу в воркере его разобрать в массив(прочитать там int'ы, строки, байты и прочее) и вернуть результат основному потоку.
Но вот как передать большой массив в воркер не понятно, так как просто так туда объекты не передашь.
Может кто-нибудь разъяснить, как все таки лучше передавать туда данные?
У меня через вебсокеты приходит большой объем данных в виде массива байт, и я хочу в воркере его разобрать в массив(прочитать там int'ы, строки, байты и прочее) и вернуть результат основному потоку.
Но вот как передать большой массив в воркер не понятно, так как просто так туда объекты не передашь.
Может кто-нибудь разъяснить, как все таки лучше передавать туда данные?
Это будет двойной работой, так как при передаче в воркер, ваш массив атоматом сериализуется в JSON, а там десериализуется…
С другой стороны в воркерах доступен AJAX может стоит получать данные уже в нем парсить и т.д., а результат возвращать…
С другой стороны в воркерах доступен AJAX может стоит получать данные уже в нем парсить и т.д., а результат возвращать…
Можно не клонировать структуру, а передать владение: developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
Может кто-то подсказать как сделать отрисовку в канвасе в отдельном потоке?
Если кратко — никак.
Если подробней — в воркерах нельзя обращаться к DOM модели, и вообще нету доступа к объекту window. Так что…
Ну хоть у кого-то есть реальный опыт применения воркеров и получения значительного или хоть какого-то повышения производительности?
Прошу прощения, на самом деле, это я Фома невнимательный.
Проверял разницу, раньше, только при включённом фаербаге.
Сейчас, при проверке этого примера, фаерфокс упал, а когда перезагрузился — фаербаг был отключен,
и результаты стали почти одинаковые.
Мало того, иногда, без воркера — работает быстрее!
Теперь и я присоединяюсь к Вашему вопросу, насчёт повышения производительности)
Проверял разницу, раньше, только при включённом фаербаге.
Сейчас, при проверке этого примера, фаерфокс упал, а когда перезагрузился — фаербаг был отключен,
и результаты стали почти одинаковые.
Мало того, иногда, без воркера — работает быстрее!
Теперь и я присоединяюсь к Вашему вопросу, насчёт повышения производительности)
Ну, так-то workers придуманы, чтобы не грузить GUI поток. Вот и всё. Это как в Android (образно). Все тяжеловесные задачи выполняются в фоне — и в результате картинка не прыгает, отклик на мышь нормальный. А повышения производительности не добиться. Что быстрее js или js?
Для вызовов функций внутри воркера извне, очень подошли бы Proxy.
Да, точно. Можно обернуть этот перформер в прокси:
и вызывать внутренние функции красиво:
function MyWorkerProxy(scriptUrl){
var performer = new Performer(scriptUrl);
this.firstFunction = function(params, callback){
performer.perform("firstFunction", params, callback);
}
...
}
и вызывать внутренние функции красиво:
var myWorkerProxy = new MyWorkerProxy("myscript.js");
myWorkerProxy.firstFunction({some: "some"}, function(result){console.log("result1="+result);});
Может можно автоматическое определение функций сделать? Ну, как–то так:
во внешнем коде
во внутреннем коде:
во внешнем коде
function Performer(scriptSource) {
var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;
var perform = function(functionName, params, callback) {
callbacks["request_" + (++nextRequestId)] = callback;
worker.postMessage(
{functionName: functionName, params: params, requestId: nextRequestId}
);
}
var self = this;
worker.onmessage = function(msg) {
if (msg.data.__methods__) { // получили список функций, делаем их методами Performer'a
var methods = msg.data.__methods__;
var appendMethod = function (name) {
self[name] = function (params, callback) {
perform(name, params, callback);
}
};
for (var i = 0; i < methods.length; ++i) {
appendMethod(method[i]);
}
}
else {
callbacks["request_" + msg.data.requestId](msg.data.result);
delete callbacks["request_" + msg.data.requestId];
}
}
perform('__init__');
}
во внутреннем коде:
onmessage = function (event) {
if (event.data.functionName === '__init__') {
var methods = [ // может и тут как–то можно автоматизировать?
'firstFunction',
'secondFunction'
...
];
postMessage({__methods__: methods });
}
else {
var requestId = event.data.requestId;
var workerFunction = eval(event.data.functionName);
var params = event.data.params;
var result = workerFunction(params);
postMessage({result: result, requestId: requestId});
}
}
О, так вообще классно получается! (если ещё добавить обработку ошибок)
Теперь можно с полным правом переименовать Performer в WorkerProxy:
а в воркере будет так:
и вызов:
Теперь можно с полным правом переименовать Performer в WorkerProxy:
function WorkerProxy(scriptSource) {
var self = this;
var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;
this.perform = function(functionName, params, successCallback, errorCallback) {
callbacks["request_" + (++nextRequestId)] =
{successCallback: successCallback, errorCallback: errorCallback};
worker.postMessage({functionName: functionName, params: params, requestId: nextRequestId});
}
worker.onmessage = function(msg) {
if (msg.data.__methods__) { // получили список функций, делаем их методами WorkerProxy
var methods = msg.data.__methods__;
for (var i = 0; i < methods.length; i++) {
eval('self["'+methods[i]+'"] = function (params, successCallback, errorCallback) {'
+'self.perform("'+methods[i]+'", params, successCallback, errorCallback);}');
}
return;
}
if(msg.data.error){
if( callbacks["request_" + msg.data.requestId].errorCallback){
callbacks["request_" + msg.data.requestId].errorCallback(msg.data.error);
}else{
alert(msg.data.error);
}
}else{
callbacks["request_" + msg.data.requestId].successCallback(msg.data.result);
}
delete callbacks["request_" + msg.data.requestId];
}
this.perform('__init__');
}
а в воркере будет так:
onmessage = function (event) {
if (event.data.functionName === '__init__') {
var methods = [];
for(var f in self){methods.push(f);}
postMessage({__methods__: methods });
return;
}
var requestId = event.data.requestId;
try{
var workerFunction = eval(event.data.functionName);
var params = event.data.params;
var result = workerFunction(params);
postMessage({result: result, requestId: requestId});
}catch(ex){
postMessage({error: ex + ", functionName="+event.data.functionName, requestId: requestId});
}
}
и вызов:
var myWorker = new WorkerProxy("myscript");
myWorker.firstFunction({some:"some"}, function(result){console.log("result1="+result);});
for(var f in self)
немножко настораживает — сюда попадёт вообще всё что только есть в self (честно говоря, не знаю что он содержит, но подозреваю, что немало из того, что имеется в window). Безопаснее сделать реализацию методов worker'a в объекте:
function MyWorker() {
this.firstFunction = function (params) {
// implementation
};
this.firstFunction = function (params) {
// implementation
};
// другие публичные методы, приватные методы и данные объявляются через var privateVariable, privateFunction
}
и тогда
var myWorker = new MyWorker();
onmessage = function (event) {
if (event.data.functionName === '__init__') {
var methods = [];
for(var f in myWorker) {
if (myWorker.hasOwnProperty(f) && typeof f === 'function') { // вторая проверка, возможно, избыточна
methods.push(f);
}
}
postMessage({__methods__: methods });
return;
}
var requestId = event.data.requestId;
try{
var params = event.data.params;
var result = myWorker[event.data.functionName] (params);
postMessage({result: result, requestId: requestId});
}catch(ex){
postMessage({error: ex + ", functionName="+event.data.functionName, requestId: requestId});
}
}
alert(msg.data.error);
дело вкуса, наверное, но console.log вместо alert часто оказывается удобнее.
Да, очень неплохой удобный wrapper получается.
Действительно, лишнего много тянется (и есть теоретический риск по ошибке вызвать что-то не то).
Тогда наверное, лучше, чем дописывать что-то в воркер,
сделать всё во внешнем коде и передать список публичных методов в параметрах:
внутри:
вызов:
Чтобы console.log работало — нужно же Фаербаг иметь).
Тогда наверное, лучше, чем дописывать что-то в воркер,
сделать всё во внешнем коде и передать список публичных методов в параметрах:
function WorkerProxy(scriptSource, publicWorkerFunctions) {
var self = this;
var methods = (typeof(publicWorkerFunctions) == "string") ? publicWorkerFunctions.split(",") : publicWorkerFunctions;
for (var i = 0; i < methods.length; i++) {
eval('self["'+methods[i]+'"] = function (params, successCallback, errorCallback) {'
+'self.perform("'+methods[i]+'", params, successCallback, errorCallback);}');
}
var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;
this.perform = function(functionName, params, successCallback, errorCallback) {
callbacks["request_" + (++nextRequestId)] =
{successCallback: successCallback, errorCallback: errorCallback};
worker.postMessage({functionName: functionName, params: params, requestId: nextRequestId});
}
worker.onmessage = function(msg) {
if(msg.data.error){
if( callbacks["request_" + msg.data.requestId].errorCallback){
callbacks["request_" + msg.data.requestId].errorCallback(msg.data.error);
}else{
alert(msg.data.error);
}
}else{
callbacks["request_" + msg.data.requestId].successCallback(msg.data.result);
}
delete callbacks["request_" + msg.data.requestId];
}
}
внутри:
onmessage = function (event) {
var requestId = event.data.requestId;
try{
var workerFunction = eval(event.data.functionName);
var params = event.data.params;
var result = workerFunction(params);
postMessage({result: result, requestId: requestId});
}catch(ex){
postMessage({error: ex + ", functionName="+event.data.functionName, requestId: requestId});
}
}
вызов:
var myWorker = new WorkerProxy("myscript", "firstFunction,secondFunction");
myWorker.firstFunction({some:"some"}, function(result){console.log("result1="+result);});
Чтобы console.log работало — нужно же Фаербаг иметь).
Sign up to leave a comment.
Оптимизация вызовов функций из воркеров (web-workers)