This one is more than 10x faster

(defn sum-fields3 [^"[[D" arr1 ^"[[D" arr2 ^"[[D" result]
  (let [L (int (alength arr1))]
    (dotimes [i L]
      (let [^doubles a1 (aget arr1 i)
            ^doubles a2 (aget arr2 i)
            ^doubles r  (aget result i)]
        (dotimes [j L]
          (aset-double r j (+ (double (aget a1 j)) (double (aget a2
j)))))))))

Not sure what the problem was exactly with yours.  Maybe the multi-
indices versions of aget.

On Sep 21, 7:43 pm, Ranjit <rjcha...@gmail.com> wrote:
> I was thinking I needed the Java arrays for interop. At one step in my
> simulation I need to take an FFT of a 2d array, then multiply that
> array with another array, and then inverse FFT. Java array operations
> in Clojure seem so slow though that it will actually be better to
> convert from either a built in data structure or an incanter matrix to
> an array for the fft, then back for the multiplication, then back for
> the IFFT, and then back.
>
> The incanter matrices seem like a pretty good choice. This function
> using incanter is about as fast as the imperative type hinted function
> you came up with:
>
> (defn gaussian-matrix [L mean std] (matrix (map #(sample-normal %1
> mean std) (repeat L L))))
>
> ,and adding matrices is fast. Converting from an incanter matrix to an
> array like this:
>
> (def B (into-array (map double-array (to-list A))))
>
> takes ~100 msec, and converting back takes a similar amount of time.
> So unfortunately all the converting back and forth means almost a half
> second extra per loop.
>
> Not that I want to use this approach anymore, but what other type
> hints could I add to my Java array addition function? The only
> unhinted variables I see left are the indices.
>
> Thanks for all your help,
>
> -Ranjit
>
> On Sep 21, 5:48 pm, Jason Wolfe <jawo...@berkeley.edu> wrote:
>
> > I think you're still missing some type hints.  I think there are
> > varying degrees of reflective code the compiler can emit, and it
> > doesn't always warn for the intermediate cases.
>
> > Do you need to do all this array business for Java interop, or just
> > because you believe it will give you maximum performance?  If the
> > latter, I'd recommend trying it with built-in data structures first --
> > you might be surprised at how good the performance can be.  Or, if you
> > want to do lots of matrix-and-vector stuff, perhaps try out the
> > Incanter matrix library or similar Java libraries.
>
> > I find writing correctly-hinted Clojure code for dealing with arrays
> > to be clumsier and more difficult than just writing the equivalent
> > Java (one of very few areas where this is the case), but fortunately I
> > only very rarely find the need to do so.  But, if that's really what
> > you want to do, I think you should always be able to get speed on par
> > with raw Java.
>
> > -Jason
>
> > On Sep 21, 1:04 pm, Ranjit <rjcha...@gmail.com> wrote:
>
> > > Thanks, I just tried using the random number generator in Incanter as
> > > a replacement and it shaves another ~100 msec off the runtime of the
> > > function. That's better but still noticeably slower than python.
>
> > > I also tried applying what I've learned here to writing a function
> > > that adds two arrays which is imperative and I used type hints to get
> > > rid of the reflection warnings:
>
> > > (defn sum-fields [^"[[D" arr1 ^"[[D" arr2 ^"[[D" result]
> > >   (let [L (alength arr1)]
> > >     (dotimes [i L]
> > >       (dotimes [j L]
> > >         (aset-double ^doubles (aget result i) j (+ (aget arr1 i j)
> > >                              (aget arr2 i j)))))))
>
> > > but it's surprisingly slow compared to numpy again.
>
> > > On Sep 21, 2:26 pm, Jason Wolfe <jawo...@berkeley.edu> wrote:
>
> > > > FYI I fired up a profiler and more than 2/3 of the runtime of gaussian-
> > > > matrix5 is going to the .nextGaussian method of java.util.Random.  I
> > > > think there are faster (and higher-quality) drop-in replacements for
> > > > java.util.Random, if that is really important to you.  I seem to
> > > > recall there being a good Mersenne Twister implementation around,
> > > > which might fit your bill.
>
> > > > -Jason
>
> > > > On Sep 21, 6:34 am, Ranjit <rjcha...@gmail.com> wrote:
>
> > > > > Yeah, I spoke too soon. All the rows are identical. This is what I
> > > > > meant to do:
>
> > > > > (defn gaussian-matrix-for-the-nth-time [L]
> > > > >   (into-array (map double-array (repeatedly L #(repeatedly L next-
> > > > > gaussian)))))
>
> > > > > and on my computer takes ~800 msecs vs. ~400 msecs for gaussian-
> > > > > matrix5 to make an array of 1000^2 gaussians.
>
> > > > > But numpy only takes about 100 msecs to do the same thing on my
> > > > > computer. I'm surprised we can't beat that or at least get close. But
> > > > > maybe next-gaussian is the bottleneck as you say.
>
> > > > > On Sep 21, 12:20 am, Jason Wolfe <jawo...@berkeley.edu> wrote:
>
> > > > > > On Sep 20, 4:43 pm, Ranjit <rjcha...@gmail.com> wrote:
>
> > > > > > > I'm glad you think partition is the problem, because that was my 
> > > > > > > guess
> > > > > > > too. But I think I have the answer. This is the fastest version 
> > > > > > > I've
> > > > > > > seen so far:
>
> > > > > > > (defn gaussian-matrix-final [L]
> > > > > > >   (into-array ^doubles (map double-array (repeat L (repeatedly L 
> > > > > > > next-
> > > > > > > gaussian)))))
>
> > > > > > The ^doubles type hint is wrong (it means array of doubles, not seq 
> > > > > > of
> > > > > > arrays of doubles); the compiler is just ignoring it.
>
> > > > > > And the reason it's so fast is probably since you're repeating the
> > > > > > same L gaussian values L times here.  Doesn't matter how fast it 
> > > > > > runs
> > > > > > if it returns the wrong answer :-).   Anyway, that might indicate 
> > > > > > that
> > > > > > the .nextGaussian is actually the bottleneck in the fastest 
> > > > > > versions.
>
> > > > > > -Jason
>
> > > > > > > If I understand what's going on now, then it looks like the only 
> > > > > > > way
> > > > > > > to make this any faster is if next-gaussian could return 
> > > > > > > primitives.
>
> > > > > > > The for, and doseq macros seems like they're pretty slow.
>
> > > > > > > -Ranjit
>
> > > > > > > On Sep 20, 3:30 pm, Jason Wolfe <jawo...@berkeley.edu> wrote:
>
> > > > > > > > I think partition is slowing you down (but haven't profiled to
> > > > > > > > verify).  Here's a functional version that's about 70% as fast 
> > > > > > > > as my
> > > > > > > > "5":
>
> > > > > > > > (defn gaussian-matrix6 [L]
> > > > > > > >      (to-array (for [i (range L)] (into-array Double/TYPE (for 
> > > > > > > > [j
> > > > > > > > (range L)] (next-gaussian))))))
>
> > > > > > > > and I'd guess that's about as good as you're going to get, 
> > > > > > > > given that
> > > > > > > > this approach is necessarily going to box and unbox the 
> > > > > > > > doubles, and
> > > > > > > > create intermediate sequences, rather than stuffing the 
> > > > > > > > primitive
> > > > > > > > doubles directly into the result array.
>
> > > > > > > > -Jason
>
> > > > > > > > On Sep 20, 12:00 pm, Ranjit <rjcha...@gmail.com> wrote:
>
> > > > > > > > > Replacing the doseq's with dotimes speeds it up a little more:
>
> > > > > > > > > (defn gaussian-matrix5 [^"[[D" arr]
> > > > > > > > >   (dotimes [x (alength arr)]
> > > > > > > > >     (dotimes [y (alength (first arr))]
> > > > > > > > >       (aset-double ^doubles (aget arr (int x)) (int y) (next-
> > > > > > > > > gaussian)))))
>
> > > > > > > > > but I'm getting reflection warnings on alength. I guess it 
> > > > > > > > > doesn't
> > > > > > > > > cause a problem because they're only called once?
>
> > > > > > > > > Also adding type hints to the more functional version of my 
> > > > > > > > > first
> > > > > > > > > attempt speeds things up quite a bit:
>
> > > > > > > > > (defn gaussian-matrix2 [L]
> > > > > > > > >      (into-array ^doubles
> > > > > > > > >           (map double-array (partition L (repeatedly (* L L) 
> > > > > > > > > next-
> > > > > > > > > gaussian)))))
>
> > > > > > > > > But it's still about 4x slower than gaussian-matrix5 above. 
> > > > > > > > > There must
> > > > > > > > > be a way to improve on the inner loop here that doesn't 
> > > > > > > > > require using
> > > > > > > > > indices, right?
>
> > > > > > > > > On Sep 20, 12:32 pm, Jason Wolfe <jawo...@berkeley.edu> wrote:
>
> > > > > > > > > > Oops, I found aset-double2 with tab completion and figured 
> > > > > > > > > > it was
> > > > > > > > > > build-in.  Forgot it was a utility I built some time ago, a 
> > > > > > > > > > stub for a
> > > > > > > > > > Java method that does the setting.
>
> > > > > > > > > > Also, I got the type hint for the "arr" arg wrong, although 
> > > > > > > > > > it didn't
> > > > > > > > > > seem to matter.
>
> > > > > > > > > > Here's a fixed version in standard Clojure that's basically 
> > > > > > > > > > as fast:
>
> > > > > > > > > > user> (defn gaussian-matrix4 [^"[[D" arr ^int L]
> > > > > > > > > >             (doseq [x (range L) y (range L)] (aset-double 
> > > > > > > > > > ^doubles
> > > > > > > > > > (aget arr (int x)) (int y) (.nextGaussian ^Random r))))
> > > > > > > > > > #'user/gaussian-matrix4
> > > > > > > > > > user> (do   (microbench (gaussian-matrix3 (make-array 
> > > > > > > > > > Double/TYPE 10
> > > > > > > > > > 10) 10)) (microbench (gaussian-matrix4 (make-array 
> > > > > > > > > > Double/TYPE 10 10)
> > > > > > > > > > 10)) )
> > > > > > > > > > min; avg; max ms:  0.000 ; 0.033 ; 8.837    ( 56828  
> > > > > > > > > > iterations)
> > > > > > > > > > min; avg; max ms:  0.009 ; 0.038 ; 7.132    ( 50579  
> > > > > > > > > > iterations)
>
> > > > > > > > > > It seems like you should be able to just use aset-double 
> > > > > > > > > > with multiple
> > > > > > > > > > indices (in place of aset-double2), but I can't seem to get 
> > > > > > > > > > the type
> > > > > > > > > > hints right.
>
> > > > > > > > > > -Jason
>
> > > > > > > > > > On Sep 20, 7:36 am, Ranjit <rjcha...@gmail.com> wrote:
>
> > > > > > > > > > > Thanks Jason, this is great.
>
> > > > > > > > > > > I was confused earlier because I wasn't seeing reflection 
> > > > > > > > > > > warnings,
> > > > > > > > > > > but it turns out that was only because I was evaluating 
> > > > > > > > > > > the function
> > > > > > > > > > > definitions in the emacs buffer, and the warnings weren't 
> > > > > > > > > > > visible.
>
> > > > > > > > > > > I have a question about gaussian-matrix3 though. What is 
> > > > > > > > > > > "aset-
> > > > > > > > > > > double2"? Is that a macro that has a type hint for an 
> > > > > > > > > > > array of
> > > > > > > > > > > doubles?
>
> > > > > > > > > > > Thanks,
>
> > > > > > > > > > > -Ranjit
> > > > > > > > > > > On Sep 19, 5:37 pm, Jason Wolfe <jawo...@berkeley.edu> 
> > > > > > > > > > > wrote:
>
> > > > > > > > > > > > Hi Ranjit,
>
> > > > > > > > > > > > The big perf differences you're seeing are due to 
> > > > > > > > > > > > reflective calls.
> > > > > > > > > > > > Getting the Java array bits properly type-hinted is 
> > > > > > > > > > > > especially tricky,
> > > > > > > > > > > > since you don't always get good reflection warnings.
>
> ...
>
> read more »

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to