Thanks for the detailed suggestions. Implementing them did bring the 
execution time down to around 250secs. Though that value is still much 
longer than 45secs. Could you please verify if I have implemented them 
correctly?

Code - 
https://github.com/amithgeorge/reddit-dailyprogrammer-clojure/blob/4ec02600e025d257a59d76fee0ad0eb01f4785ff/src/rdp/214_intermediate_arr.clj

project.clj contains the line 

    :jvm-opts ^:replace ["-Xms1024m" "-Xmx1g" "-server"]

  > 0) Switch to Clojure 1.7.0-beta3 - it's faster and some things below 
are dependent on it for best performance. And use Java 1.8.

Done. 

  > 1) Parse the lines you're reading directly into longs (Clojure focuses 
on 64-bit primitives - longs and doubles)

    (defn- read-line-nums
      ([] (read-line-nums (read-line) #(Long/parseLong %1)))
      ([input-line parse-fn]
       (if-let [line input-line]
         (->> line
              (#(clojure.string/split %1 #" "))
              (map parse-fn)))))

  > 2) Put the longs first into a data structure that preserves the 
primitive type. The two best options for that here are records (which can 
have primitive fields) and arrays. I would create a Canvas defrecord with 
^long width and height and a Paper defrecord with all ^long fields for 
example.

    (defrecord Paper [^long color 
                      ^long x1  ^long y1
                      ^long x2 ^long y2])

    (defrecord Canvas [^long width ^long height])

    (defn- make-paper
      ([^long w ^long h] (make-paper [0 0 0 w h]))
      ([^longs [color x y w h]]
       (Paper. color x y (+ x w -1) (+ y h -1))))

I had to make the second arity accept a vector, as it seems impossible to 
create a function accepting more than 4 primitive arguments. Though I 
suppose I could grouped the args into a `record` of its own?

  > 3) Store the papers in a vector (using transient to create it)

The `read-input-file` function which creates and stores the papers, it 
finishes within 20ms. So I didn't bother using a transient. 

  > 4) I suspect visible-color and covered? could probably be tightened up 
into a reduce over papers or a single loop-recur over papers - can't say I 
totally get what's happening there.

The papers vector represents a sheets of papers that have been stacked on 
top of each other. ie, the last element in the vector is the canvas, and 
the first element is the topmost sheet. Each sheet is of different 
dimensions. Depending on the coordinates where they are placed, they may 
cover a part of the canvas. Different sheets may overlap in the areas they 
cover and thus we only want to consider the topmost sheet for that area. 

The `visible-color` function accepts a canvas coordinate and the stack 
papers and goes through the stack to find the first sheet that covers said 
coordinate. That papers color will be visible color for that coordinate.

  > 5) In visible-color-frequencies, you could use "update" instead of get 
+ transient assoc! on the acc map, but this is never going to be terribly 
fast. Another option here would be to create an array with the max color 
(you could track that while reading if it's not a well-known answer) and 
bash the array. That can retain int or long counters and will be *way* 
faster.

    (defn- visible-color-frequencies-arr
      [{:keys [colors canvas papers]}]
      (let [colorCounts (long-array (count colors))] 
        (reduce 
         (fn [_ ^longs coord]
           (if-let [color (visible-color coord papers)]
             (aset-long colorCounts color (+ 1 (aget colorCounts color)))
             _))
         -1
         (for [^long y (range (:height canvas))
               ^long x (range (:width canvas))]
           [x y]))
         (zipmap (range) colorCounts)))

It's a bit messy with me abusing reduce and totally ignoring the 
accumulator, but the version using arrays is atleast 10 seconds faster than 
the one using transient maps. That said, even the hashmap version took 
around 260-270secs. So the bulk of the time savings is caused by some other 
change. 

  > 6) You can use (set! *unchecked-math* :warn-on-boxed) to get faster 
math (no overflow checks) and also issue warnings (added in 1.7) if you 
happened to use boxed math by accident. 

I added these to both the old code (which didn't use type hints) and the 
new one. I ran the `-main` function of both from within the repl and using 
lein run. I didn't see any warnings in any of the executions. How am I 
supposed to use these?

    (defn -main 
      ([] (-main "0" "false"))
      ([index use-arrays] 
       (time 
        (binding [*unchecked-math* :warn-on-boxed
                  *warn-on-reflection* true] 
          (if-not (Boolean/parseBoolean use-arrays) 
            (solve (input-files (Integer/parseInt index)))
            (solve-arr (input-files (Integer/parseInt index))))))))

  > Use: ^:jvm-opts ^:replace ["-server"] 

In my case there isn't any noticable difference between explicitly using 
'-server' and not using it. Which brings me to my final question, 

  > The major problem here is that you are using boxed math for everything 
instead of primitives.

>From what I understand of the changes I made, a reduction of almost 250 
seconds came about from simply using type hints. Is that normal? The 
incrementing of color counts in the original code, was that the boxed math 
you were referring to? 

In C#, using generic types consistently meant I never had to worry about 
boxing. I tried using `no.disassemble` to verify if the type hints really 
changed anything, but I only got more confused. 

Consider this example code where my-val is supposed to be a long and each 
of the functions is supposed to accept a long and return a long.

    (-> my-val
        (func-a)
        (func-b)
        (func-c))

I will have to add typehints to original value as well to each function's 
input and output, right? Can typehints be added to denote a 
collection/sequence of longs? What about a collection/sequence of records? 


Finally, can the performance be improved any more? 250secs still feels too 
long compared to the C# version.

-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to