2014-02-12 225 views
1

您好我是一個新手,並試圖代碼的renderScript計算數組中值的總和。我想知道如何使用渲染腳本在數組中執行元素的總和。有沒有一種方法可以將輸出傳遞迴腳本以進行順序添加?我的問題的說法是: 矢量和使用的renderScript

描述:計算數組中的值的總和。

輸入:整數數組

輸出:整數

任何幫助,將不勝感激!

回答

4

這恐怕是更復雜一點似乎比,但我會盡我所能解釋這裏,你可以採取實現這一可能的途徑。

你所要求的是並行簡化算法,它可以實現或者是你的情況下的數組求和,或者是任何其他交換+關聯運算符,當迭代應用到數組上時,它將「減少」它到一個單一的數字。其他示例是查找大數組的最大值或最小值。在CUDA和OpenCL中,這種計算有一個衆所周知的模式,它能夠最大限度地利用並行線程,例如,如果谷歌「CUDA減少」,您將獲得大量有用的信息。

,這是實現的途徑是通過反覆減少一半的陣列,一遍又一遍,直到你結束了一個單一的元素。每次減少它時,每個新元素都是前兩個元素的總和。下面是更好的描述了這個算法的圖片:

Parallel Reduction

因此,例如,你以一個16個元素的數組。你只運行一次算法,最後得到一個8個元素的數組 - 這8個元素中的每一個都是來自原始數組的兩個數字的總和。

你再次運行它,你將得到4種元素 - 其中每一項都是兩個數字從上一步驟的總和。您的總和 - 等等......

你不停,直到你最終只有一個號碼這樣做。

中的renderScript實施,這將是的低效方式:

的Java:

int[] ints; // Your data is held here. 

Allocation data = Allocation.createSized(rs, Element.I32(rs), ints.length, Allocation.USAGE_SCRIPT); 
data.copy1DRangeFrom(0, ints.length, ints); 

ScriptC_Reduce script = new ScriptC_Reduce(rs); 
script.bind_data(data); 

for (int stride = ints.length/2; stride > 0; stride /= 2) { 
    script.set_stride(stride); 
    script.forEach_root(input, output); 
} 

data.copyTo(ints); 
int totalsum = ints[0]; 

的renderScript:

#pragma version(1) 
#pragma rs java_package_name(...[your package here]...) 

int stride; 
int * data; 

void root(const int32_t *v_in, int32_t *v_out, uint32_t x) { 
    if (x < stride) data[x] += data[x + stride]; 
} 

如果你用RS工作過,你可能注意幾件奇怪的事情:

  1. 請注意,RS內核中的「v_in」和「v_out」完全不用,因爲它們僅限於讀寫當前線程索引對應的數據元素,而reduce算法需要訪問數據元素其他職位。因此,存在一個int數組指針「data」,它是從具有相同名稱的分配中綁定到Java的,這就是內核直接處理的內容。
  2. 從Java中的循環中調用內核多次,而不是在內核中執行該循環。這是因爲在每次迭代時,previuos步驟中的所有數據都必須準備好在其預期位置,否則「data [x + stride]」將不同步。在RS中,內核調用會被鎖定,這意味着在內核完成整個數據處理之前不會執行任何操作。這與__syncthreads()在CUDA內核中的作用類似,如果您熟悉這一點的話。

但是,我上面提到,這是一個效率低下的實現。但它應該指向正確的方向。爲了使效率更高,可能需要將數據拆分爲更小的塊,以便分別計算,因爲如此處所述,此算法將在每個迭代步驟運行ints.length線程數,並在非常大的數組上運行,這將導致批次的步驟,以及在每個步驟的空閒線程的批號。

此外,這假定你的數組的長度正好是2的冪,所以多重對分只會導致一個元素。對於其他大小的陣列,您可能需要填充陣列。再次,在處理非常大的數組時,0填充將需要大量浪費的內存。

因此,要解決這些問題,您可能需要將您的數組分成多個,例如每個64個元素。因此,如果你沒有一個確切的數組長度,填充「最後」塊到64將不需要那麼多的內存。此外,您將需要更少的迭代步驟(以及更少的空閒線程)來減少64個元素。當然,64是我剛剛製作的一個神奇數字。嘗試2的其他權力來查看他們的結果,您可能會看到更好的結果與其他塊大小,如16或32.我懷疑性能與塊大小將非常依賴硬件。

編輯:這假定RenderScript可以使用它的GPU上運行的設備驅動程序,以便它可以實際啓動大量的並行線程。否則,像這樣的僅執行CPU的內核可能會比處理數組lineary更慢。

+0

這種方法應該適用於非常大的數組。對於更小的陣列,數以千計的元素,簡單的循環可能會更快。 –

+0

看完這篇文章後我有點困惑。您將方法「root」稱爲內核,但是從Android文檔中可以看出內核具有標識符__attribute __((kernel)),因此這是一個可調用的函數。因此它是一個單線程函數。我錯過了什麼嗎? – eldjon

+0

'root(...)'是RenderScript文件中一個特殊的保留方法名稱,它始終充當該類的主要內核。雖然可以像上面提到的那樣通過用內核屬性指定內核屬性來將_additional_內核添加到同一個類中,但內核始終是「默認」內核。然後,從java端,它被稱爲'script.forEach_root(...)'。 – monoeci

3

不要。除非你有比1增加更奇特的東西。別。只有在該陣列中至少有400萬個整數時,代碼纔會更快。

RenderScript: Entries:1 Total: 3 Time: 0.067ms 
Simple Loop : Entries:1 Total: 3 Time: 0.001ms 
RenderScript: Entries:2 Total: 97 Time: 0.614ms 
Simple Loop : Entries:2 Total: 97 Time: 0.001ms 
RenderScript: Entries:4 Total: 227 Time: 0.28ms 
Simple Loop : Entries:4 Total: 227 Time: 0.002ms 
RenderScript: Entries:8 Total: 320 Time: 0.445ms 
Simple Loop : Entries:8 Total: 320 Time: 0.002ms 
RenderScript: Entries:16 Total: 700 Time: 0.486ms 
Simple Loop : Entries:16 Total: 700 Time: 0.002ms 
RenderScript: Entries:32 Total: 1807 Time: 0.595ms 
Simple Loop : Entries:32 Total: 1807 Time: 0.002ms 
RenderScript: Entries:64 Total: 3218 Time: 0.624ms 
Simple Loop : Entries:64 Total: 3218 Time: 0.002ms 
RenderScript: Entries:128 Total: 6230 Time: 0.737ms 
Simple Loop : Entries:128 Total: 6230 Time: 0.003ms 
RenderScript: Entries:256 Total: 12968 Time: 0.769ms 
Simple Loop : Entries:256 Total: 12968 Time: 0.005ms 
RenderScript: Entries:512 Total: 26253 Time: 0.895ms 
Simple Loop : Entries:512 Total: 26253 Time: 0.01ms 
RenderScript: Entries:1024 Total: 52345 Time: 0.987001ms 
Simple Loop : Entries:1024 Total: 52345 Time: 0.017ms 
RenderScript: Entries:2048 Total: 100223 Time: 1.715ms 
Simple Loop : Entries:2048 Total: 100223 Time: 0.034ms 
RenderScript: Entries:4096 Total: 200375 Time: 1.213ms 
Simple Loop : Entries:4096 Total: 200375 Time: 0.065ms 
RenderScript: Entries:8192 Total: 403713 Time: 1.196ms 
Simple Loop : Entries:8192 Total: 403713 Time: 0.163001ms 
RenderScript: Entries:16384 Total: 812411 Time: 1.929ms 
Simple Loop : Entries:16384 Total: 812411 Time: 0.41ms 
RenderScript: Entries:32768 Total: 1620542 Time: 1.822ms 
Simple Loop : Entries:32768 Total: 1620542 Time: 0.617ms 
RenderScript: Entries:65536 Total: 3250733 Time: 5.955ms 
Simple Loop : Entries:65536 Total: 3250733 Time: 1.384ms 
RenderScript: Entries:131072 Total: 6478866 Time: 2.622ms 
Simple Loop : Entries:131072 Total: 6478866 Time: 2.008ms 
RenderScript: Entries:262144 Total: 12980832 Time: 3.979999ms 
Simple Loop : Entries:262144 Total: 12980832 Time: 4.377001ms 
RenderScript: Entries:524288 Total: 25956676 Time: 10.163ms 
Simple Loop : Entries:524288 Total: 25956676 Time: 8.326ms 
RenderScript: Entries:1048576 Total: 51897168 Time: 12.723001ms 
Simple Loop : Entries:1048576 Total: 51897168 Time: 15.871999ms 
RenderScript: Entries:2097152 Total: 103867356 Time: 32.229001ms 
Simple Loop : Entries:2097152 Total: 103867356 Time: 31.367ms 
RenderScript: Entries:4194304 Total: 207646704 Time: 61.628999ms 
Simple Loop : Entries:4194304 Total: 207646704 Time: 63.378ms 
RenderScript: Entries:8388608 Total: 415058480 Time: 103.734999ms 
Simple Loop : Entries:8388608 Total: 415058480 Time: 140.088ms 

這是切割所有在renderscript的青睞。就像假設所有的分配將在主循環之外完成,並且不需要將數據陣列複製出去(我簡單地稱爲rs.finish()來確保渲染完成)。

#pragma version(1) 
#pragma rs java_package_name(com.photoembroidery.tat.olsennoise) 

int stride; 
int * data; 

void root(const int32_t *v_in, int32_t *v_out, uint32_t x) { 
    data[x] += data[x + stride]; 
} 

請注意啓動選項。你做第一次裁減,將數組調整到正確的2倍。因此,你可以在之前的大小和兩個因子之間採取任何餘數,然後處理這些條目,這樣它們就會以2的因子減少。然後你處理兩個因素。

//int[] array = //array of your data//; 

     ScriptC_reduce script = new ScriptC_reduce(mRS); 
     Allocation data = Allocation.createSized(mRS, Element.I32(mRS), array.length, Allocation.USAGE_SCRIPT); 
     data.copy1DRangeFrom(0, array.length, array); 
     script.bind_data(data); 

     int smallest2ExpBiggerThanLength = 1; 
     for (int length = arraysize; length != 0; length >>= 1,smallest2ExpBiggerThanLength <<= 1); 

     int end = smallest2ExpBiggerThanLength/2; 
     int start = smallest2ExpBiggerThanLength - arraysize; 
     if (start == end) { 
      start = 0; 
      end = end/2; 
     } 

     while (end > 0) { 
      launchOptions.setX(start, end); 
      script.set_stride(end - start); 
      script.forEach_root(data, data, launchOptions); 
      script.forEach_root(data,data); 
      end = end >> 1; 
      start = 0; 
     } 
     data.copyTo(array); 
     int total = array[0]; 

其他答案中最大的低效率是啓動選項。從限制範圍開始,而不是檢查範圍的有效性,你會更好。你輸了4倍的速度。一個簡單的循環將會更快更通用,而不會引發渲染錯誤。 - 你需要做一些比1增加更難的事情。

2

我們實際上正在努力支持「reduce」作爲下一個Android版本的新內核類型。這將允許您在分配的單元格上運行關聯操作(如添加),並獲得單個減少的結果。代碼已經存在於AOSP中,但我們正試圖使其更靈活/一般。當前窗體已經允許你指定一個2輸入 - > 1輸出內核,它可以應用於所有單元。在此期間,您可以通過在可調參數中依次運行並使用rsGetElementAt _ *()來遍歷單元格來近似減少內核。這比Java要快得多,在這種情況下你不斷付出不必要的邊界檢查(以及其他開銷)。