2016-03-24 5 views
1

私はBufferedImageをとり、ByteBufferを返す関数を作ろうとしています。それでOpenGLテクスチャとして使うことができます。そうするために、私は私の質問に本当に関係のないバイトシフトをしなければならないことを学んだ。 BufferedImageの値はRGBGBを必要とするARGBおよびOpenGLと関係しています。どのようにすれば、このバッファをもっと速く塗りつぶすことができますか?

私は(Javaから)を実装しようとしている機能はこの1つである:

public static ByteBuffer toByteBuffer(BufferedImage img){ 
    byte[] byteArray = new byte[img.getWidth()*img.getHeight()*4]; 
    for(int i = 0; i < img.getWidth()*img.getHeight(); i++){ 
     int value = img.getRGB(i%img.getWidth(), (i-(i%img.getWidth()))/img.getWidth()); 
     byteArray[i*4] = (byte) ((value<<8)>>24); 
     byteArray[i*4+1] = (byte) ((value<<16)>>24); 
     byteArray[i*4+2] = (byte) ((value<<24)>>24); 
     byteArray[i*4+3] = (byte) (value>>24); 
    } 
    return (ByteBuffer) ByteBuffer.allocateDirect(byteArray.length).put(byteArray).flip(); 
} 

そして、これはClojureのと私の試みです:

(defn sub-byte [^long b ^long x] 
    (unchecked-byte (-> x 
    (bit-shift-left (* 8 b)) 
    (bit-shift-right 24)))) 


(defn bufferedimage->bytebuffer [^BufferedImage img] 
    (binding [*unchecked-math* true] 
    (let [w (.getWidth img) 
      h (.getHeight img) 
      ^bytes arr (make-array Byte/TYPE (* 4 w h))] 
     (loop [i 0] 
      (let [img-i (mod i w) 
       img-j (quot i w) 
       value (.getRGB img img-i img-j)] 
      (aset arr (* i 4)  (sub-byte 1 value)) 
      (aset arr (+ 1 (* i 4)) (sub-byte 2 value)) 
      (aset arr (+ 2 (* i 4)) (sub-byte 3 value)) 
      (aset arr (+ 3 (* i 4)) (sub-byte 0 value)) 
      (when (< (+ i 1) (* w h)) (recur (+ i 1))) 
      )) 
     (cast ByteBuffer (-> (ByteBuffer/allocateDirect (count arr)) 
          (.put arr) 
          (.flip)))))) 

これは512をロードするために10秒かかります* 512タイルセット。これはまったく受け入れられません。これを1秒未満で実行しようとしています。

すべての時間を費やしている部分がループであることに注意してください。

これらの時間はREPLを使用して取得されることに言及しているかもしれません。

また、コードのパフォーマンスに重大な影響を及ぼす部分についてはJavaを使用できることをよく知っているので、これは理論的な質問のほうが多いので、クロージャコードを最適化する方法を学ぶことができます。

+0

'clojure.core/time'を使って、コードのどの部分が最も時間を費やすかを測定できます。 –

+1

私はすでにしました。私のポストから: "いつも取っている部分はループです"。 – Setzer22

+0

[Criterium](https://github.com/hugoduncan/criterium)は、ベンチマークのためのより良い方法です。一般的に、 'map'、' reduce'、 'filter'はおそらく' loop/recur'よりもスピードとイディオムの方が良い選択でしょう。 – jmargolisvt

答えて

2
機能を使用して解決の問題があなたが設定したときに明らかにされて

*warn-on-reflection*trueへ:

(set! *warn-on-reflection* true) 

あなたのコードをロードすると、コンパイラはあなたのsub-byte関数がObjectを返し、それがマッチング静的に解決できないことを教えてくれます方法。のみlongdoubleプリミティブは、戻り値の型としてサポートされているよう

Reflection warning, web_app/so.clj:26:11 - call to static method aset on clojure.lang.RT can't be resolved (argument types: [B, int, java.lang.Object). 

残念ながら、あなたはあなたの関数にbyte戻り値の型ヒントを使用することはできません。

(defn sub-byte ^byte [^long b ^long x] 
    (unchecked-byte (-> x 
         (bit-shift-left (* 8 b)) 
         (bit-shift-right 24)))) 

CompilerException java.lang.IllegalArgumentException: Only long and double primitives are supported, compiling:(web_app/so.clj:7:1) 

あなたがリターンとして^longをヒントしようとするかもしれませんヒントの結果の型は、関数本体が返すものではありません(byte):

(defn sub-byte ^long [^long b ^long x] 
    (unchecked-byte (-> x 
         (bit-shift-left (* 8 b)) 
         (bit-shift-right 24)))) 

CompilerException java.lang.IllegalArgumentException: Mismatched primitive return, expected: long, had: byte, compiling:(web_app/so.clj:7:1) 

ただしlongを返すように機能を持たせることができるが、その後、あなたはunchecked-byteでどこでもそれをラップする必要があります - この方法を使用すると、すべての反射の警告をなくす:

(defn sub-byte ^long [^long b ^long x] 
    (-> x 
     (bit-shift-left (* 8 b)) 
     (bit-shift-right 24)))) 

(unchecked-byte (sub-byte ...)) 

別の解決策は、すでにその意志が分かったようなマクロを使用することです関数呼び出しとその戻り型に関する問題は避けてください。

+0

ありがとうございます。問題は関数呼び出しではなく、オブジェクトにラップされたバイトを返す関数にありました。私はそれを念頭に置いて、その警告を反映するオプションをオンにしてください。ありがとう! – Setzer22

+0

Clojureの最近のバージョンは[バイトとバイトのヒント]をサポートしています(http://clojure.org/reference/java_interop#TypeAliases)。また、[byte casting](http://clojuredocs.org/clojure.core/bytes) – kawas44

+0

@ kawas44を使用することもできます。Clojureは 'byte'と' bytes'ヒントをサポートしていますが、Clojure関数は 'プリミティブ型の場合は「long」または「double」となります。 'int'や' byte'やその他のプリミティブな戻り値の型をサポートしていません(コンパイラのメッセージには、java.lang.IllegalArgumentException:longとdoubleのプリミティブのみがサポートされているので)。 –

0

私はマクロにサブバイトを変換することによって10Sから173msまでの時間を削減:

(defmacro sub-byte [b x] 
    `(unchecked-byte (-> ~x 
    (bit-shift-left (* 8 ~b)) 
    (bit-shift-right 24)))) 

をパフォーマンスの問題は、すべての関数呼び出しとしなければならなかったことが表示されます。

Clojureでは関数呼び出しが非常に効率的ではないと私は思っていました。また、私はコンパイラが私のためにフードの下で最適化のインライン化を実行していると思った。

私は「何」を見つけましたが、私は「なぜ」を認識していないので、私は私のものよりも何が起こっているのかを説明する答えを受け入れます。

関連する問題