Re: ANN: Specter 0.11.0, performance without the tradeoffs

2016-06-02 Thread 'Gunnar Völkel' via Clojure
Following the convention not to use `def` in non-top-level positions, you 
should use `intern` instead of `def`.

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


Re: ANN: Specter 0.11.0, performance without the tradeoffs

2016-05-31 Thread Mark Engelberg
I'm glad it helped!

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


Re: ANN: Specter 0.11.0, performance without the tradeoffs

2016-05-31 Thread Nathan Marz
Great idea on interning a var at macro-time and then using it later! I did 
something similar for the ClojureScript implementation of inline caching 
but for some odd reason didn't think of doing it for the Clojure version. 
I'm seeing a big performance improvement for the [:a :b :c] benchmark from 
my post - about 15% faster than before and now very close to full manual 
precompilation.

Here's the 
commit: 
https://github.com/nathanmarz/specter/commit/fbca7ab99c84d93a28f7773f1c56be12e1a939a3


On Tuesday, May 31, 2016 at 9:33:29 PM UTC-4, puzzler wrote:
>
> I think this is an interesting problem, so here are some additional 
> brainstorms on the issue that may or may not be useful...
>
> One strategy to create a global mutable variable from inside the function 
> would be to use def at macroexpansion time to create a var, and then just 
> refer to it in the expansion, like this:
>
> (defmacro expand-to-something-with-mutable-global []
>   ; At macroexpansion time, create a (private) var containing a volatile.
>   (let [global-name (gensym "cache")]
> (eval `(def ^:private ~global-name (volatile! nil)))
> ; Now macro can refer to this var, can use as a cache, etc.
> `(if-let [cache-contents# (deref ~global-name)] 
>(do-something-with cache-contents#)
>(reset! ~global-name init-value-for-cache
>
> Not sure if this would be any faster than ConcurrentHashMap, but I would 
> guess that it would be if Clojure resolves the location of the var in 
> memory once, at compile-time.
>
> A related technique would be for Specter to maintain an ArrayList of 
> volatiles.  At macroexpansion time, you add a fresh volatile to the end of 
> the ArrayList, noting the index of its location, and then inside the macro 
> you hardcode a lookup in the ArrayList at the specific index to retrieve 
> the volatile containing the cache for this particular expansion.  I would 
> expect this to be faster than looking up in a hash map, although there'd be 
> some additional concurrency/locking details to worry about that I assume 
> ConcurrentHashMap handles for you.
>
> Or just use an ArrayList's slot directly as the cache (rather than storing 
> a volatile to add a level of indirection), but then the concurrency 
> logistics would be even more complicated.
>
> On Tue, May 31, 2016 at 12:50 PM, Nathan Marz  > wrote:
>
>> No, because that global mutable variable would need to be specifiable by 
>> Specter on usage of the library. For example, if you wrote:
>>
>> (defn foo []
>>   (select [:a :b] {:a {:b 1}}))
>>
>> `select` has no ability to control anything outside the context of its 
>> form. It certainly can't wrap `foo` to put a volatile field in its closure. 
>> So for the static-field idea, it would expand to something like:
>>
>> (defn foo []
>>   (static-field [pathcache]
>>  (if-not pathcache (set! pathcache ...))
>>  ...
>>  ))
>>
>>
>>
>> On Tuesday, May 31, 2016 at 3:15:26 PM UTC-4, puzzler wrote:
>>>
>>> In your writeup, you say that there would be further speed benefits if 
>>> you could have a global mutable variable within the context of a function 
>>> (like a static field).
>>>
>>> Can't you effectively accomplish that already in Clojure like this?:
>>>
>>> (let [mycache (volatile! nil)]
>>>   (defn foo []
>>>   ...)))
>>>
>> -- 
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com .
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

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


Re: ANN: Specter 0.11.0, performance without the tradeoffs

2016-05-31 Thread Mark Engelberg
I think this is an interesting problem, so here are some additional
brainstorms on the issue that may or may not be useful...

One strategy to create a global mutable variable from inside the function
would be to use def at macroexpansion time to create a var, and then just
refer to it in the expansion, like this:

(defmacro expand-to-something-with-mutable-global []
  ; At macroexpansion time, create a (private) var containing a volatile.
  (let [global-name (gensym "cache")]
(eval `(def ^:private ~global-name (volatile! nil)))
; Now macro can refer to this var, can use as a cache, etc.
`(if-let [cache-contents# (deref ~global-name)]
   (do-something-with cache-contents#)
   (reset! ~global-name init-value-for-cache

Not sure if this would be any faster than ConcurrentHashMap, but I would
guess that it would be if Clojure resolves the location of the var in
memory once, at compile-time.

A related technique would be for Specter to maintain an ArrayList of
volatiles.  At macroexpansion time, you add a fresh volatile to the end of
the ArrayList, noting the index of its location, and then inside the macro
you hardcode a lookup in the ArrayList at the specific index to retrieve
the volatile containing the cache for this particular expansion.  I would
expect this to be faster than looking up in a hash map, although there'd be
some additional concurrency/locking details to worry about that I assume
ConcurrentHashMap handles for you.

Or just use an ArrayList's slot directly as the cache (rather than storing
a volatile to add a level of indirection), but then the concurrency
logistics would be even more complicated.

On Tue, May 31, 2016 at 12:50 PM, Nathan Marz  wrote:

> No, because that global mutable variable would need to be specifiable by
> Specter on usage of the library. For example, if you wrote:
>
> (defn foo []
>   (select [:a :b] {:a {:b 1}}))
>
> `select` has no ability to control anything outside the context of its
> form. It certainly can't wrap `foo` to put a volatile field in its closure.
> So for the static-field idea, it would expand to something like:
>
> (defn foo []
>   (static-field [pathcache]
>  (if-not pathcache (set! pathcache ...))
>  ...
>  ))
>
>
>
> On Tuesday, May 31, 2016 at 3:15:26 PM UTC-4, puzzler wrote:
>>
>> In your writeup, you say that there would be further speed benefits if
>> you could have a global mutable variable within the context of a function
>> (like a static field).
>>
>> Can't you effectively accomplish that already in Clojure like this?:
>>
>> (let [mycache (volatile! nil)]
>>   (defn foo []
>>   ...)))
>>
> --
> 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.
>

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


Re: ANN: Specter 0.11.0, performance without the tradeoffs

2016-05-31 Thread Nathan Marz
No, because that global mutable variable would need to be specifiable by 
Specter on usage of the library. For example, if you wrote:

(defn foo []
  (select [:a :b] {:a {:b 1}}))

`select` has no ability to control anything outside the context of its 
form. It certainly can't wrap `foo` to put a volatile field in its closure. 
So for the static-field idea, it would expand to something like:

(defn foo []
  (static-field [pathcache]
 (if-not pathcache (set! pathcache ...))
 ...
 ))



On Tuesday, May 31, 2016 at 3:15:26 PM UTC-4, puzzler wrote:
>
> In your writeup, you say that there would be further speed benefits if you 
> could have a global mutable variable within the context of a function (like 
> a static field).
>
> Can't you effectively accomplish that already in Clojure like this?:
>
> (let [mycache (volatile! nil)]
>   (defn foo []
>   ...)))
>

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


Re: ANN: Specter 0.11.0, performance without the tradeoffs

2016-05-31 Thread Mark Engelberg
In your writeup, you say that there would be further speed benefits if you
could have a global mutable variable within the context of a function (like
a static field).

Can't you effectively accomplish that already in Clojure like this?:

(let [mycache (volatile! nil)]
  (defn foo []
  ...)))

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


ANN: Specter 0.11.0, performance without the tradeoffs

2016-05-31 Thread Nathan Marz
Specter is a library for querying and transforming nested data structures 
concisely and efficiently. The 0.11.0 release is a huge milestone that 
implements something which for the better part of the past year I wasn't 
sure was possible. In summary: now you don't have to do anything special to 
make Specter run fast. The prior work of manually precompiling your paths 
for performance is now seamlessly automated.

As part of the release, wrote up a detailed guide to how Specter achieves 
its high performance. Should also be an interesting read for anyone curious 
about the design of a very powerful Clojure library. 
https://github.com/nathanmarz/specter/wiki/Specter-0.11.0:-Performance-without-the-tradeoffs

Note that there are some backwards incompatible changes in this release, so 
please read the changelog below. The core select/transform/etc. functions 
have changed to macros, and there have been some name updates to clean up 
the terminology. Updating your projects to the new changes should be very 
easy.



Changes:

* New `path` macro does intelligent inline caching of the provided path. 
The path is factored into a static portion and into params which may change 
on each usage of the path (e.g. local parameters). The static part is 
factored and compiled on the first run-through, and then re-used for all 
subsequent invocations. As an example, `[ALL (keypath k)]` is factored into 
`[ALL keypath]`, which is compiled and cached, and `[k]`, which is provided 
on each execution. If it is not possible to precompile the path (e.g. [ALL 
some-local-variable]), nothing is cached and the path will be compiled on 
each run-through.

* BREAKING CHANGE: all `select/transform/setval/replace-in` functions 
changed to macros and moved to com.rpl.specter.macros namespace. The new 
macros now automatically wrap the provided path in `path` to enable inline 
caching. Expect up to a 100x performance improvement without using explicit 
precompilation, and to be within 2% to 15% of the performance of explicitly 
precompiled usage.

* Added `select*/transform*/setval*/replace-in*/etc.` functions that have 
the same functionality as the old `select/transform/setval/replace-in` 
functions.

* Added `must-cache-paths!` function to throw an error if it is not 
possible to factor a path into a static portion and dynamic parameters.

* BREAKING CHANGE: `defpath` renamed to `defnav`

* BREAKING CHANGE: `path` renamed to `nav`

* BREAKING CHANGE: `fixed-pathed-path` and `variable-pathed-path` renamed 
to `fixed-pathed-nav` and `variabled-pathed-nav`

* Added `must` navigator to navigate to a key if and only if it exists in 
the structure

* Added `continous-subseqs` navigator

* Added `ATOM` navigator (thanks @rakeshp)

* Added "navigator constructors" that can be defined via 
`defnavconstructor`. These allow defining a flexible function to 
parameterize a defnav, and the function integrates with inline caching for 
high performance.

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