Oops -- something was wrong with my benchmarks, and my "improvements on the 
order of 1/3" was wrong. I still see improvements with r/fold as compared to my 
agent-based approach, but the difference now appears to be only something like 
1/20.

 -Lee

On Jun 5, 2014, at 7:19 PM, Lee Spector <lspec...@hampshire.edu> wrote:

> 
> A followup on these issues, after experimenting a bit with reducers, just for 
> anyone who may be interested:
> 
> - Naive use of reducers in place of seq functions, throughout my program, got 
> messy and led to some new problems with memory management, I think (or at 
> least to jerkier execution over time). I realized that reducers probably 
> shouldn't be used so indiscriminately.
> 
> - With more moderate use of reducers, and using fold, I do indeed see 
> performance improvements relative to sequential code and also relative to the 
> previous version of my code that used agents for concurrency. I don't 
> actually seem to be getting much benefit from the way that reducers avoid 
> building intermediate results -- all of the benefit appears to be due to 
> using a combination of r/fold and one r/map to process collection elements 
> concurrently. I've only done small numbers of tests and they all involve 
> randomness, but I do see improvements on the order of 1/3.
> 
> - The best behaving/looking approach for me seems to be to stick with lazy 
> sequence operations and aggressive de-lazifying for the bulk of my code 
> (possibly cleaning some things up by defining my own forv, repeatv, etc., or 
> possibly rewriting some of the core lazy sequence functions to not be lazy in 
> the first place), but to use r/fold and r/map for concurrency for my 
> top-level tasks. I'm getting the best results by using reducers only in one 
> place, in my definition of a concurrent mapping function, which I then use 
> only for top-level functions (never nested):
> 
> (defn pmapall
>   [f coll]
>   (if single-thread-mode
>     (doall (map f coll))
>     (r/fold 1 r/cat r/append! (r/map f coll))))
> 
> This replaces my previous agent-based version:
> 
> (defn pmapall
>   [f coll]
>   (if single-thread-mode
>     (doall (map f coll))
>     (let [agents (map #(agent % :error-handler 
>                               (fn [agnt except] (clojure.repl/pst except 
> 1000) (System/exit 0))) 
>                       coll)]
>       (dorun (map #(send % f) agents))
>       (apply await agents)
>       (doall (map deref agents)))))
> 
> The one that uses reducers is neater and seems to make my program run faster, 
> although I'm not really sure why.
> 
> In any event, I'm using reducers now only as an alternative concurrency 
> mechanism and solving my original problems by de-lazification.
> 
> -Lee
> 
> 
> 
> On Jun 4, 2014, at 7:21 PM, Lee Spector <lspec...@hampshire.edu> wrote:
> 
>> 
>> On Jun 4, 2014, at 1:29 PM, Timothy Baldridge <tbaldri...@gmail.com> wrote:
>> 
>>> Although your original complaint was about clojure seqs being lazy. It 
>>> should be noted that reducers are also lazy down to the point of a fold or 
>>> reduce, so I'm not sure what you're really getting there. It wouldn't be 
>>> hard at all to write map, filter, remove, etc. in terms of list operations. 
>>> Perhaps that's what you're looking for? If I understand your original 
>>> complaint, you want map, filter, etc, to be eager.
>>> 
>> 
>> True, my original concern was (and my main concern still is) to avoid 
>> crashes (in this case, stack overflow errors) that stem from unexpected (to 
>> me) consequences of laziness.
>> 
>> Those problems could indeed be solved by rewriting map and filter etc, or 
>> (easier) by "wringing out the laziness" wherever it arises, e.g. by forcing 
>> everything to be a vector (or a list, as you suggest). It's a little 
>> cumbersome to do this everywhere, though, and I was wondering if there was 
>> an existing library or approach for this issue.
>> 
>> Then, however, it was suggested that I could have my cake and another kind 
>> of cake too, by using reducers. The suggestion was that this could banish 
>> the laziness-related issues while also providing significant performance 
>> improvements. Sounds good to me! I'm still working on getting my current 
>> project to behave well using reducers, but it seems promising.
>> 
>>> 
>>> On the other hand, time spent learning how lazy seqs work, and the caveats 
>>> involved will make it easier for you to understand the code produced by the 
>>> rest of the community. So perhaps that's the better option. 
>> 
>> I agree that it's important to understand lazy sequences, and I do think I 
>> understand the core concepts reasonably well (and I've implemented lazy 
>> evaluation in other languages, etc.). But I've also traced some really 
>> annoying and hard to find bugs to unexpected (and sometimes never fully 
>> explained) consequences of laziness, so I'd like to find the best ways to 
>> avoid it when I don't really want it. If that best way turns out to make 
>> things run faster too then that'd be fantastic.
>> 
>> -Lee

--
Lee Spector, Professor of Computer Science
Cognitive Science, Hampshire College
893 West Street, Amherst, MA 01002-3359
lspec...@hampshire.edu, http://hampshire.edu/lspector/
Phone: 413-559-5352, Fax: 413-559-5438

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