寻找一种逐像素绘制的快速方法,为什么这段代码比java慢1000倍?

rao*_*oof 5 performance awt clojure

这是一个简单的代码,只对窗口着色4次.

也许有一些我看不到的明显事物.

我的目标是从头开始学习计算机图形,我想逐个像素地绘制以完全控制.我正在寻找一种快速的方法来做到这一点.

是完整的代码.

相关的clojure部分:

(defmacro for-loop [[sym init check change :as params] & steps]
  `(loop [~sym ~init value# nil]
     (if ~check
       (let [new-value# (do ~@steps)]
         (recur ~change new-value#))
       value#)))
(time
 (for-loop
  [k 0 (< k 2) (inc k)]
  (for-loop
   [c 0 (< c 2) (inc c)]
   (for-loop
    [i 0 (< i width) (inc i)]
    (for-loop
     [j 0 (< j height) (inc j)]
     (aset ^ints @pixels (+ i (* j width)) (get cs c))))
   (.repaint canvas))))
Run Code Online (Sandbox Code Playgroud)

java中的相同代码:

long t = System.currentTimeMillis();
for (int k = 0 ; k < 2; k++) {
  for (int c = 0; c < 2; c++) {
    for (int i = 0 ; i < width; i++) {
      for (int j = 0; j < height; j++) {
        pixels[i + j * width] = cs[c];
      }
    }
    repaint();
  }
}
System.out.println(System.currentTimeMillis() - t);
Run Code Online (Sandbox Code Playgroud)

Car*_*ate 7

有几个问题:

  • 如果你跑lein check,你会看到反射警告.你在运行时强制反射会降低速度.我将canvas创建更改为:

    (defonce canvas (doto (proxy [Frame] []
                            (update [g] (.paint this g))
                            (paint [^Graphics2D g]
                              (.drawImage g, ^BufferedImage image, 0, 0 nil)))
                      (.setSize width height)
                      (.setBackground Color/black)
                      (.setFocusableWindowState false)
                      (.setVisible true)))
    
    Run Code Online (Sandbox Code Playgroud)

    注意我正在使用的类型提示.它不知道使用哪个重载drawImage,并且完全找不到该paint方法.

  • 然而,主要问题是使用aset.来自aset的文档:

    设置索引/索引处的值.适用于引用类型的Java数组.返回val.

    强调我的.

    问题是aset不适用于基元.它强制将每个数字包装为a Integer,然后在图像中使用时再次打开.当在图像的每个像素上相乘时,这是非常昂贵的.

    更改asetaset-int使用int基元.这将执行时间从大约20秒缩短到半秒.


老实说,我不能低估它.它比它快得多,但仍然比Java版本慢约20倍.我现在已经工作了将近2个小时,而且我已经撞墙了.希望其他人可以挤出最后一点时间.

  • @raoof我仍然会在Clojure中编写它,但是用Java编写代码的这些部分.在Leiningen项目的project.clj文件中,您可以使用:java-source-paths选项指出Java源代码的位置.要使Clojure和Java代码的混合使用REPL,请查看将自动编译和重新加载Java类的lein-virgil项目. (2认同)