2013-04-13 38 views
1

我想使用webworkers渲染動畫mandelbrot zoomer的幀的部分,因爲涉及到很多計算,並且因爲這可以很容易地在塊中拆分,所以應該是理想的並行處理的情況。Webworker帆布性能可怕

但是無論我嘗試什麼,我都沒有得到任何性能,以換取額外的CPU工人使用。與非工作者版本相比,Chrome中的基準測試速度稍慢,但在Firefox中速度要慢得多。

我的猜測是,將圖像數據傳輸給網絡工作人員非常昂貴,我嘗試接收原始數據並使用它來渲染幀,但結果非常相似。我不認爲這是向工作人員發送和接收圖像數據的理想方式(事實上,我只需要接收它,但我無法在工作人員中創建可直接用於畫布的緩衝區) 。因此,在發送任何大量數據的過程中都會產生真正的瓶頸。

親愛的stackoverflow,請幫我回答這兩個問題:我在這裏做錯了什麼,什麼可以改善?

演示可以找到here工作人員,並參考jsfiddle非工人版本。

代碼如下:

"use strict"; 

/*global $*/ 

$(function() { 

    var mandelbrot = new Mandelbrot(); 

}); 

var Mandelbrot = function() { 

    // set some values 
    this.width = 500; 
    this.height = 500; 

    this.x_center = -1.407566731001088; 
    this.y_center = 2.741525895538953e-10; 

    this.iterations = 250; 
    this.escape = 4, 
    this.zoom = 10; 
    this.count = 0; 
    this.worker_size = 10; 
    this.received = 0; 
    this.refresh = true; 

    //let's go - create canvas, image data and workers 
    this.init(); 
    //start animation loop 
    this.animate(); 

}; 

Mandelbrot.prototype = { 

    init: function() { 

     var self = this; 

     //create main canvas and append it to div 
     var container = $("#content"); 

     this.canvas = document.createElement("canvas"); 
     this.canvas.width = this.width; 
     this.canvas.height = this.height; 

     container.append(this.canvas); 

     //create imagedata 
     this.context = this.canvas.getContext("2d"); 
     this.image = this.context.getImageData(0, 0, this.width, this.height); 
     this.data = new Int32Array(this.image.data.buffer); 

     //create imagedata for webworkers 
     this.worker_data = this.context.getImageData(0, 0, this.width, this.height/this.worker_size); 

     //create webworkers drop them in array 
     this.pool = []; 

     for (var i = 0; i < this.worker_size; i++) { 

      this.pool[i] = new Worker("js/worker.js"); 
      this.pool[i].idle = true; 
      this.pool[i].id = i; 

      //on webworker finished 
      this.pool[i].onmessage = function(e) { 

       self.context.putImageData(e.data, 0, self.height/self.worker_size * e.target.id); 
       self.received++; 

      }; 

     } 
    }, 

    iterate: function() { 

     for (var i = 0; i < this.pool.length; i++) { 

      this.pool[i].postMessage({ 

       image: this.worker_data, 
       id: this.pool[i].id, 
       worker_size: this.worker_size, 
       width: this.width, 
       height: this.height, 
       x_center: this.x_center, 
       y_center: this.y_center, 
       iterations: this.iterations, 
       escape: this.escape, 
       zoom: this.zoom 

      }); 
     } 
    }, 

    animate: function() { 

     requestAnimationFrame(this.animate.bind(this)); 

     //poor man's benchmark over 250 frames 
     if (this.count === 0) { 
      console.time("timer"); 
     } 

     if (this.count === 250) { 
      console.timeEnd("timer"); 
     } 

     //refresh at init, then refresh when all webworkers are done and reset 
     if (this.received === this.worker_size | this.refresh) { 

      this.received = 0; 
      this.refresh = false; 
      this.count++; 
      this.zoom *= 0.95; 
      this.iterate(); 

     } 
    } 
}; 

和worker.js:

self.onmessage = function(e) { 

    "use strict"; 

    var x_step = e.data.zoom/e.data.width; 
    var y_step = e.data.zoom/e.data.height; 

    var y_start = e.data.height/e.data.worker_size * e.data.id; 
    var y_end = e.data.height/e.data.worker_size; 

    var data = new Int32Array(e.data.image.data.buffer); 

    for (var y = 0; y < y_end; y++) { 

     var iy = e.data.y_center - e.data.zoom/2 + (y + y_start) * y_step; 

     for (var x = 0; x < e.data.width; x++) { 

      var rx = e.data.x_center - e.data.zoom/2 + x * x_step; 

      var zx = rx; 
      var zy = iy; 
      var zx2 = 0; 
      var zy2 = 0; 

      for (var i = 0; zx2 + zy2 < e.data.escape && i < e.data.iterations; ++i) { 

       zx2 = zx * zx; 
       zy2 = zy * zy; 
       zy = (zx + zx) * zy + iy; 
       zx = zx2 - zy2 + rx; 
      } 

      data[y * e.data.width + x] = (255 << 24) | (i << 16) | (i << 8) | i; 

     } 
    } 

    self.postMessage(e.data.image); 

}; 

回答

0

我真的試圖在這個實驗同樣的事情,這是一個置換過濾器:

http://www.soundstep.com/blog/experiments/displacement-js/heart/ http://www.soundstep.com/blog/2012/04/25/javascript-displacement-mapping/

I在過濾器中創建了一名工作人員,然後將這些像素計算在一起,然後再將其發佈回主應用程序。基本上迭代一個worker內的所有像素。

在工作人員之前,我有一個循環4 getImageData,這不能在worker中完成。無論如何,它在Chrome上佔用大約15%的CPU。

因此,總體而言,如果沒有工作人員,我可以獲得70%的CPU,並且我可以獲得工作人員90%的CPU。

我想工人無法完成的操作,比如「getImageData」和「putImageData」,再加上工人本身的事實,需要比沒有工人更多的CPU。

如果我們能夠發送其他類型的數據,可能會更好,所以我們可以在worker中執行getImageData和putImageData。

不確定還有另一種發送和接收字節的方式來處理和重構畫布內容。

http://typedarray.org/concurrency-in-javascript/

1

的問題是,你是在母圖像的每個像素迭代。如果將迭代限制爲兩幅圖像中較小的一幅,事情會更快。此外,如果您平鋪圖紙,則每個圖塊都可以在單獨的網絡工作人員處理,從而增加圖像每個部分的拼裝。我寫這個:http://robertleeplummerjr.github.io/CanvasWorker/這正是你想要的。