2012-12-22 73 views
1

我正在嘗試Java ForkJoin框架並編寫了一個簡單的測試程序,將圖像的像素設置爲隨機顏色。例如。它會產生僞噪聲。Java ForkJoin多線程比單線程慢

但是在測試性能時,我發現運行單線程比運行多線程更快。我通過傳遞高門檻讓它運行單線程。

這是類工人類:

public class Noise extends RecursiveAction { 

    private BufferedImage image; 
    private int xMin; 
    private int yMin; 
    private int xMax; 
    private int yMax; 
    private int threshold = 2000000; // max pixels per thread 

    public Noise(BufferedImage image, int xMin, int yMin, int xMax, int yMax, int threshold) { 
     this.image = image; 
     this.xMin = xMin; 
     this.yMin = yMin; 
     this.xMax = xMax; 
     this.yMax = yMax; 
     this.threshold = threshold; 
    } 

    public Noise(BufferedImage image, int xMin, int yMin, int xMax, int yMax) { 
     this.image = image; 
     this.xMin = xMin; 
     this.yMin = yMin; 
     this.xMax = xMax; 
     this.yMax = yMax; 
    } 

    @Override 
    protected void compute() { 
     int ppt = (xMax - xMin) * (yMax - yMin); // pixels pet thread 
     if(ppt > threshold) { 
      // split 
      int verdeling = ((xMax - xMin)/2) + xMin; 
      invokeAll(new Noise(image, xMin, yMin, verdeling, yMax), 
        new Noise(image, verdeling+1, yMin, xMax, yMax)); 
     } 
     else { 
      // execute! 
      computeDirectly(xMin, yMin, xMax, yMax); 
     } 
    } 

    private void computeDirectly(int xMin, int yMin, int xMax, int yMax) { 
     Random generator = new Random(); 
     for (int x = xMin; x < xMax; x++) { 
      for (int y = yMin; y < yMax; y++) { 
       //image.setPaint(new Color(generator.nextInt())); 
       int rgb = generator.nextInt(); 
       int red = (rgb >> 16) & 0xFF; 
       int green = (rgb >> 8) & 0xFF; 
       int blue = rgb & 0xFF; 

       red = (int) Math.round((Math.log(255L)/Math.log((double) red)) * 255); 
       green = (int) Math.round((Math.log(255L)/Math.log((double) green)) * 255); 
       blue = (int) Math.round((Math.log(255L)/Math.log((double) blue)) * 255); 

       int rgbSat = red; 
       rgbSat = (rgbSat << 8) + green; 
       rgbSat = (rgbSat << 8) + blue; 

       image.setRGB(x, y, rgbSat); 
      } 

     } 
     Graphics2D g2D = image.createGraphics(); 
     g2D.setPaint(Color.RED); 
     g2D.drawRect(xMin, yMin, xMax-xMin, yMax-yMin); 
    } 
} 

當生成6000 * 6000圖像的結果是:
單線程:9.4sec @ 25%的CPU負載
多線程:16.5sec @ 80 %-90%CPU負載
(Core2quad Q9450)

爲什麼多線程版本較慢?
我該如何解決這個問題?

+4

你試過用y而不是x分裂嗎?我認爲BufferedImage很可能在內存中存儲行 - 主要,這可能會有所作爲。 – yiding

+1

更大的問題可能是BufferedImage和Graphics對象不是線程安全的。您可能想要在數組中生成噪聲,並直接從數據創建一個BufferedImage(這也將比使用setRGB更快)。 – yiding

+0

至於你的第一個評論:我已經轉向Y分裂,但它沒有區別。 – Paul

回答

2

首先,F/J是一個利基產品。如果你沒有一個巨大的數組並將其作爲DAG處理,那麼你使用的是錯誤的產品。當然,F/J可以使用多個處理器,但是也可以使用簡單的多線程方法,而不需要F/J的所有開銷。

嘗試使用四個線程,並直接給每個工作的四分之一。

這是路F /Ĵ本來是用於:

Sum left = new Sum(array, low, mid); 
Sum right = new Sum(array, mid, high); 
left.fork(); 
long rightAns = right.compute(); 
long leftAns = left.join(); 
return leftAns + rightAns; 

當你不走結構樹的葉子,那麼所有的賭注都關閉。

+0

你可能是對的,但這個程序的重點是測試ForkJoin的運作(如上所述)。所以,這仍然讓我想知道爲什麼這樣發生? – Paul

+0

如何用fork(),join()替換inokeAll()這種遞歸分解框架的設計工作方式。 – edharned

0

由於開銷?分叉和連接也需要時間。也許你需要一個更大的測試集?或者在線索本身做更多的工作?

+0

在四核上執行時間約爲16秒,因此64sec核心時間。在6000 * 6000圖像上有2M限制,這樣就可以產生32個線程,即每個線程執行時間大約爲2秒。所以創建線程的開銷應該相當低,我認爲呢?另一方面,單線程執行需要9,5秒的核心時間。但是那些55sec核心時間開銷在哪裏呢? – Paul