If you really care about performance then I would use a macro for code 
generation and do something the following:

(defmacro appendfunc 
     ([m keys sb]
       `(do
          (.append ~sb (~m ~(first keys)))
          ~@(for [k (next keys)]
              `(do 
                 (.append ~sb \,)
                 (.append ~sb (~m ~k))))
          (.append ~sb \n))))

(let [sb (StringBuilder.)
         f (fn [^StringBuilder sb m] (appendfunc m [:one :two :three :four] 
sb))
         maps (repeat 250000 {:one 1 :two 2 :three 3 :four 4})]
     (time (let [res (str (reduce f sb maps))] (count res))))

=> "Elapsed time: 118.105355 msecs"

Could probably optimise a bit more but that's under 400ns per row... pretty 
decent I think.


On Thursday, 12 February 2015 09:25:12 UTC+8, Mark Watson wrote:
>
> I'm looking for the most performant way to transform a huge seq (size 
> 250000) of maps into a single CSV.
>
> The data structure looks something like:
>
> (def data-struct
>
>   (repeat 250000 {:one 1 :two 2 :three 3 :four 4}))
>
>
> A naive implementation would be:
>
> (let [f #(->> % (map (comp str val)) (clojure.string/join ","))]
>
>   (->> data-struct
>
>     (map f)
>
>     (clojure.string/join "\n")))
>
>
> However, this takes far too long for my application (an the order of 10s 
> of seconds).
>
> Another attempt using reducers:
>
> (require '[clojure.core.reducers :as r])
>
>  
>
> (let [f #(->> % (map (comp str val)) (clojure.string/join ","))
>
>       r-join (fn
>
>                ([] nil)
>
>                  ([x y]
>
>                   (if (and x y) (str x "\n" y)
>
>                     (if x (str x)
>
>                       (if y (str y))))))]
>
>   (->> data-struct
>
>     (r/map f)
>
>     (r/fold r-join)))
>
>
> Still not great.
>
> But, Looking at the sources of clojure.string/join and clojure.core/str, 
> it becomes apparent that the both implementations create an instance of 
> java.lang.StringBuilder 
> for each element in the sequence. (I have to imagine this is the main 
> issue, even though GC seems to only be ~5% of the runtime)
>
> Would it make sense to instantiate one java.lang.StringBuilder for all of 
> the concatenation (and call java.lang.StringBuilder append)?
>
> What's the best way to do this with idiomatic Clojure?
>
> Thanks a lot!
>

-- 
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