Thank you for sharing!

Can you please share the implementations too? But generally speaking, I
don’t think the performance difference is relevant enough to  justify the
inclusion.

On Fri, Dec 31, 2021 at 21:40 Paul Alexander <paul.aj.mans...@gmail.com>
wrote:

> I believe this is easier to read/follow:
>
> Name                            ips        average  deviation
> median         99th %
>
> map replace_many             1.93 M      517.77 ns  ±9182.17%           0
> ns        1000 ns
>
> map reduce + replace!        1.58 M      631.47 ns  ±7266.46%           0
> ns        2000 ns
>
> map for + replace            1.58 M      634.43 ns  ±8194.35%           0
> ns        2000 ns
>
> Comparison:
>
> map replace_many             1.93 M
>
> map reduce + replace!        1.58 M - 1.22x slower +113.70 ns
>
> map for + replace            1.58 M - 1.23x slower +116.66 ns
>
>
> Name                           ips        average  deviation
> median         99th %
>
> map update_many             1.43 M      701.44 ns  ±7768.55%           0
> ns        2000 ns
>
> map for + update!           1.20 M      832.54 ns  ±5683.86%        1000
> ns        2000 ns
>
> map reduce + update!        1.17 M      851.53 ns  ±4816.16%        1000
> ns        2000 ns
>
> Comparison:
>
> map update_many             1.43 M
>
> map for + update!           1.20 M - 1.19x slower +131.09 ns
>
> map reduce + update!        1.17 M - 1.21x slower +150.08 ns
>
>
> Name                                 ips        average  deviation
> median         99th %
>
> keywords replace_many             1.49 M      669.04 ns  ±5836.31%
> 980 ns        1980 ns
>
> keywords reduce + replace!        1.41 M      710.24 ns  ±5503.07%
> 980 ns        1980 ns
>
> keywords for + replace!           1.40 M      714.06 ns  ±5791.74%
> 980 ns        1980 ns
>
> Comparison:
>
> keywords replace_many             1.49 M
>
> keywords reduce + replace!        1.41 M - 1.06x slower +41.19 ns
>
> keywords for + replace!           1.40 M - 1.07x slower +45.02 ns
>
>
> Name                                ips        average  deviation
> median         99th %
>
> keywords update_many             1.35 M      742.22 ns  ±5371.88%
> 1000 ns        2000 ns
>
> keywords reduce + update!        1.02 M      976.57 ns  ±5363.65%
> 1000 ns        2000 ns
>
> keywords for + update!           1.01 M      986.09 ns  ±5456.16%
> 1000 ns        2000 ns
>
> Comparison:
>
> keywords update_many             1.35 M
>
> keywords reduce + update!        1.02 M - 1.32x slower +234.34 ns
>
> keywords for + update!           1.01 M - 1.33x slower +243.87 ns
>
>
> Does this sway your opinion on the proposal, José?
> On Friday, December 31, 2021 at 12:29:00 PM UTC-5 Paul Alexander wrote:
>
>> Thank you, Marten, for the suggestion. And Happy New Year!
>> I actually ended up doing that before your reply, except I had used a
>> "homebrew benchmarking" implementation. I also did it with Benchee, and the
>> results really weren't all that different but I've included only the
>> results from Benchee below.
>>
>> Name                                 ips        average  deviation
>>   median         99th %
>>
>> map replace_many                  2.69 M      371.13 ns ±10368.06%
>>     0 ns         990 ns
>>
>> map for + replace                 2.32 M      430.38 ns  ±8111.30%
>>     0 ns         990 ns
>>
>> map reduce + replace!             2.32 M      431.63 ns  ±8167.48%
>>     0 ns         990 ns
>>
>> map update_many                   2.05 M      488.47 ns  ±9258.19%
>>     0 ns         990 ns
>>
>> map for + update!                 1.64 M      609.13 ns  ±5804.57%
>>     0 ns         990 ns
>>
>> map reduce + update!              1.66 M      600.64 ns  ±5698.26%
>>     0 ns         990 ns
>>
>> keywords replace_many             1.91 M      523.22 ns  ±6849.81%
>>     0 ns         990 ns
>>
>> keywords for + replace!           1.84 M      544.19 ns  ±6432.10%
>>     0 ns         990 ns
>>
>> keywords reduce + replace!        1.82 M      550.39 ns  ±6488.68%
>>     0 ns         990 ns
>>
>> keywords update_many              1.78 M      561.74 ns  ±6508.52%
>>     0 ns         990 ns
>>
>> keywords for + update!            1.49 M      670.96 ns  ±6347.76%
>>     0 ns         990 ns
>>
>> keywords reduce + update!         1.46 M      686.43 ns  ±6250.71%
>>     0 ns         990 ns
>>
>>
>> On Friday, December 31, 2021 at 2:05:17 AM UTC-5 w...@resilia.nl wrote:
>>
>>> Putting them in a module in any file (with e.g. a `.ex` or `.exs`
>>> extension) and running them from there will work.
>>> You might also like to look into libraries such as `Benchee` which make
>>> benchmarking easier and prevent some of the pitfalls which a homebrew
>>> benchmarking implementation might have.
>>>
>>> Hope this helps, and happy old year/new year :-),
>>>
>>> ~Marten
>>> On 31-12-2021 01:01, Paul Alexander wrote:
>>>
>>> Sorry. Would putting them in a test case be better practice? If not, do
>>> you mind telling me of the correct way which would produce the most
>>> indicative results?
>>>
>>> On Thursday, December 30, 2021 at 6:24:08 PM UTC-5 José Valim wrote:
>>>
>>>> Don’t benchmark in the shell. Code in the shell is evaluated and not
>>>> compiled.
>>>>
>>>> On Fri, Dec 31, 2021 at 00:14 Paul Alexander <paul.aj...@gmail.com>
>>>> wrote:
>>>>
>>>>> Thanks for offering your opinion, José. I very much understand where
>>>>> you're coming in regards to using Enum.reduce/3 for such an operation, but
>>>>> I have found it to cause a fair amount of needless overhead especially 
>>>>> when
>>>>> there are other operations going on and the updating should be the most
>>>>> trivial. Since you brought up the efficiency tradeoffs, I've put together 
>>>>> a
>>>>> few simple benchmarks below for the Map functions where the results are
>>>>> from averaging 10k iterations. As you can see the performance improvement
>>>>> is quite drastic, with both *_many functions being 130%+ .
>>>>>
>>>>> iex> map
>>>>>
>>>>> %{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10}
>>>>>
>>>>> iex> kv
>>>>>
>>>>> [a: 11, d: 22, g: 33, j: 44]
>>>>>
>>>>> iex> keys
>>>>>
>>>>> [:a, :d, :g, :j]
>>>>>
>>>>> iex(32)> Benchmark.measure(10_000, "reduce + replace!", fn ->
>>>>> Enum.reduce(kv, map, fn {k, v}, acc -> Map.replace!(acc, k, v) end) end)
>>>>>
>>>>> reduce + replace! avg: 18.2396
>>>>>
>>>>> iex(33)> Benchmark.measure(10_000, "replace_many", fn ->
>>>>> Map.replace_many(map, kv) end)
>>>>>
>>>>> replace_many avg: 1.0979
>>>>>
>>>>> iex(34)> Benchmark.measure(10_000, "reduce + update!", fn ->
>>>>> Enum.reduce(keys, map, fn k, acc -> Map.update!(acc, k, &(&1*2)) end) end)
>>>>>
>>>>> reduce + update! avg: 48.284
>>>>>
>>>>> iex(35)> Benchmark.measure(10_000, "update_many", fn ->
>>>>> Map.update_many(map, keys, &(&1*2)) end)
>>>>>
>>>>> update_many avg: 9.8719
>>>>> On Thursday, December 30, 2021 at 5:13:10 PM UTC-5 José Valim wrote:
>>>>>
>>>>>> Hi Paul,
>>>>>>
>>>>>> Thanks for the proposal. My personal take is that a Enum.reduce/3 +
>>>>>> the relevant Map operation should be the way to go, because this can 
>>>>>> easily
>>>>>> lead to a combination of replace_many, update_many, put_new_many, etc.
>>>>>> Especially because the many operations likely wouldn't be any more
>>>>>> efficient than the Enum.reduce/3 call.
>>>>>>
>>>>>>
>>>>>> On Thu, Dec 30, 2021 at 10:42 PM Paul Alexander <paul.aj...@gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>> Hi everyone,
>>>>>>>
>>>>>>> I would like to discuss the possibility of adding two new functions
>>>>>>> to each of the Map and Keyword modules; replace_many/2 and 
>>>>>>> update_many/3.
>>>>>>> Their purpose is to update maps and keyword lists providing either a
>>>>>>> keyword list of the key-value pairs which need to updated, or a list of
>>>>>>> keys and a function which operates on the existing values of those keys.
>>>>>>>
>>>>>>> Far too often I find myself needing to call replace!/3 or update!/3
>>>>>>> several times from within a pipeline, or even needing to use a
>>>>>>> for-comprehension or Enum.reduce/3 to update a map in "one shot", when 
>>>>>>> it
>>>>>>> feels like there should be a function for this.
>>>>>>>
>>>>>>> There are a number of reasons as to why I think these functions
>>>>>>> should be considered, but I'll provide only two for now:
>>>>>>>
>>>>>>>    1. There is already a way of updating multiple key-value pairs
>>>>>>>    simultaneously for maps using %{map | k1 => v1, k2 => v2}. But this
>>>>>>>    unfortunately does not support passing a literal keyword list after 
>>>>>>> the
>>>>>>>    cons operator.
>>>>>>>       - My first instinct was to see if I could expand the special
>>>>>>>       update syntax to handle keyword lists, but I wasn't able to find 
>>>>>>> where it
>>>>>>>       is in the codebase. If someone could point that out for me 
>>>>>>> because I'd like
>>>>>>>       to learn how it works, I'd greatly appreciate it.
>>>>>>>    2. It would be somewhat analogous to Kernel.struct!/2, where
>>>>>>>    keyword lists can be passed as the second argument to update several 
>>>>>>> fields
>>>>>>>    within a struct. Seeing as how structs are maps, it only makes sense 
>>>>>>> there
>>>>>>>    should be a way that maps could be updated in a similar manner from 
>>>>>>> within
>>>>>>>    the Map module.
>>>>>>>
>>>>>>>
>>>>>>> I have already implemented the four functions, complete with docs,
>>>>>>> examples, and passing tests. But I wanted confirmation from the core 
>>>>>>> team
>>>>>>> if a PR is welcome for this addition. Any opinions?
>>>>>>>
>>>>>> --
>>>>>>> You received this message because you are subscribed to the Google
>>>>>>> Groups "elixir-lang-core" group.
>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>> send an email to elixir-lang-co...@googlegroups.com.
>>>>>>> To view this discussion on the web visit
>>>>>>> https://groups.google.com/d/msgid/elixir-lang-core/7c79a2b8-3a3c-48f9-a4d0-7d2e07c851e4n%40googlegroups.com
>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/7c79a2b8-3a3c-48f9-a4d0-7d2e07c851e4n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>> .
>>>>>>>
>>>>>> --
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "elixir-lang-core" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>>> an email to elixir-lang-co...@googlegroups.com.
>>>>>
>>>> To view this discussion on the web visit
>>>>> https://groups.google.com/d/msgid/elixir-lang-core/63cf4231-5747-4f8e-99f7-87a68a7ab664n%40googlegroups.com
>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/63cf4231-5747-4f8e-99f7-87a68a7ab664n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>> .
>>>>
>>>>
>>>>> --
>>> You received this message because you are subscribed to the Google
>>> Groups "elixir-lang-core" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to elixir-lang-co...@googlegroups.com.
>>>
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/elixir-lang-core/cb88ccf3-ece7-47b5-a6d6-7ba5253482bfn%40googlegroups.com
>>> <https://groups.google.com/d/msgid/elixir-lang-core/cb88ccf3-ece7-47b5-a6d6-7ba5253482bfn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>> --
> You received this message because you are subscribed to the Google Groups
> "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to elixir-lang-core+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/elixir-lang-core/7e87dd44-3348-46b9-a0bf-bf4d6a806dffn%40googlegroups.com
> <https://groups.google.com/d/msgid/elixir-lang-core/7e87dd44-3348-46b9-a0bf-bf4d6a806dffn%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J9-erPjwaqqN_jmfQLfhGmMvSoKsDWm9mEgmCxy1Ea%3Dw%40mail.gmail.com.

Reply via email to