Size: a a a

2020 May 17

Е

Евгений in pro.elixir
V
на больших мапах показывает скорость на 33% выше чем Enum.reduce
@pham_nuwen FYI
это понятно, меньше аллокаций, и матчинг проще
источник

Е

Евгений in pro.elixir
впрочем, насчет матчинга не уверен
источник

Е

Евгений in pro.elixir
Я еще побенчил reduce_while, который вроде как использует уже протоколы, ожидал большой разницы, но на удивление разница не так уж и велика:
1кк элементов в мапе
iter           18.67
elixir         13.08 - 1.43x slower +22.88 ms
источник

Е

Евгений in pro.elixir
Мой бенч.
Код говно или сойдет?
источник

Е

Евгений in pro.elixir
только не говорите, что параметр командной строки надо валидировать и выдавать ошибку в случае чего :)))))
источник

Е

Евгений in pro.elixir
Впрочем, всем похуй скорее всего :D
источник

a

arikai in pro.elixir
Евгений
Это вы кому? :)
Уже все посмотрели. И судя по всему Enum.reduce_while для мап использует как раз протокольный Enumerable.reduce
Я о том, что все операции в Enum над коллекциями реализованы через Enumerable.reduce
Больше инфы: http://blog.plataformatec.com.br/2015/05/introducing-reducees/

Это в последнее время стали расширять протокол для перформанса: напр. Возможность сделать count, который в случае чего фоллбэчнеться на реализацию через reduce
источник

LL

Lama Lover in pro.elixir
arikai
Я о том, что все операции в Enum над коллекциями реализованы через Enumerable.reduce
Больше инфы: http://blog.plataformatec.com.br/2015/05/introducing-reducees/

Это в последнее время стали расширять протокол для перформанса: напр. Возможность сделать count, который в случае чего фоллбэчнеться на реализацию через reduce
источник

Е

Евгений in pro.elixir
arikai
Я о том, что все операции в Enum над коллекциями реализованы через Enumerable.reduce
Больше инфы: http://blog.plataformatec.com.br/2015/05/introducing-reducees/

Это в последнее время стали расширять протокол для перформанса: напр. Возможность сделать count, который в случае чего фоллбэчнеться на реализацию через reduce
Как выясняется не все. Enum.reduce для мап и списков специализирован и не обращается к протоколу Enumerable для мап и списков.
источник

a

arikai in pro.elixir
Оптимизация же
источник

a

arikai in pro.elixir
Сами видели, как редьюс реализован для мап :)
источник

LL

Lama Lover in pro.elixir
arikai
Сами видели, как редьюс реализован для мап :)
Так я и скинул редьюс для мап...
источник

SK

Simon Khaskelberg in pro.elixir
У меня на тестах Enumerable.reduce работает быстрее Enum.reduce для всех размеров map
источник

a

arikai in pro.elixir
Евгений
Господа, что за говно?
Так уж получилось, полез сегодня смотреть как реализован в эликисире протокол Enumerable для Map и как-то жестко прихуел:
Enumerable.List.reduce(:maps.to_list(map), acc, fun)
https://github.com/elixir-lang/elixir/blob/c592d1300d3dba34bf8005cb9e124c9004ac45ea/lib/elixir/lib/enum.ex#L3779
Что это за херня? Они превращают мапу в список??? А если в мапе миллион записей?
Полез в эрланг, а там оказывается итераторы есть, чтобы бегать по мапе.
По-моему - это эпик фейл, нет?
Или это я тупой?
Я скорее про этот код
Тут в коммитах лучше глянуть
источник

LL

Lama Lover in pro.elixir
Simon Khaskelberg
У меня на тестах Enumerable.reduce работает быстрее Enum.reduce для всех размеров map
Интересно, а как тестируешь?
источник

SK

Simon Khaskelberg in pro.elixir
Lama Lover
Интересно, а как тестируешь?
inputs = %{
 "100_000 elements" => Enum.map(1..100_000, fn el -> {to_string(el), el} end) |> Enum.into(%{}),
 "1_000_000 elements" =>
   Enum.map(1..1_000_000, fn el -> {to_string(el), el} end) |> Enum.into(%{}),
 "10_000_000 elements" =>
   Enum.map(1..10_000_000, fn el -> {to_string(el), el} end) |> Enum.into(%{})
}

Benchee.run(
 %{
   "Enumerable.reduce" => fn map ->
     Enumerable.reduce(map, {:cont, 0}, fn {_, v}, acc -> {:cont, acc + v} end)
   end,
   "Enum.reduce" => fn map ->
     Enum.reduce(map, 0, fn {_, v}, acc -> acc + v end)
   end,
   ":maps.fold" => fn map ->
     :maps.fold(fn _k, v, acc -> acc + v end, 0, map)
   end
 },
 inputs: inputs,
 warmup: 10,
 time: 20,
 memory_time: 5
)
источник

LL

Lama Lover in pro.elixir
Simon Khaskelberg
inputs = %{
 "100_000 elements" => Enum.map(1..100_000, fn el -> {to_string(el), el} end) |> Enum.into(%{}),
 "1_000_000 elements" =>
   Enum.map(1..1_000_000, fn el -> {to_string(el), el} end) |> Enum.into(%{}),
 "10_000_000 elements" =>
   Enum.map(1..10_000_000, fn el -> {to_string(el), el} end) |> Enum.into(%{})
}

Benchee.run(
 %{
   "Enumerable.reduce" => fn map ->
     Enumerable.reduce(map, {:cont, 0}, fn {_, v}, acc -> {:cont, acc + v} end)
   end,
   "Enum.reduce" => fn map ->
     Enum.reduce(map, 0, fn {_, v}, acc -> acc + v end)
   end,
   ":maps.fold" => fn map ->
     :maps.fold(fn _k, v, acc -> acc + v end, 0, map)
   end
 },
 inputs: inputs,
 warmup: 10,
 time: 20,
 memory_time: 5
)
А результаты какие?
источник

SK

Simon Khaskelberg in pro.elixir
##### With input 100_000 elements #####
Name                        ips        average  deviation         median         99th %
:maps.fold               208.10        4.81 ms    ±94.18%        4.49 ms        8.86 ms
Enumerable.reduce        180.48        5.54 ms   ±139.45%        5.20 ms       10.77 ms
Enum.reduce              156.41        6.39 ms    ±82.14%        6.35 ms        9.85 ms

Comparison:
:maps.fold               208.10
Enumerable.reduce        180.48 - 1.15x slower +0.74 ms
Enum.reduce              156.41 - 1.33x slower +1.59 ms

Memory usage statistics:

Name                 Memory usage
:maps.fold                3.00 MB
Enumerable.reduce         6.07 MB - 2.02x memory usage +3.07 MB
Enum.reduce               5.31 MB - 1.77x memory usage +2.31 MB

**All measurements for memory usage were the same**

##### With input 10_000_000 elements #####
Name                        ips        average  deviation         median         99th %
:maps.fold                 2.23      448.57 ms    ±19.78%      438.49 ms      998.63 ms
Enumerable.reduce          1.74      575.91 ms    ±33.01%      552.81 ms     1530.11 ms
Enum.reduce                1.65      604.37 ms    ±17.60%      588.14 ms     1135.32 ms

Comparison:
:maps.fold                 2.23
Enumerable.reduce          1.74 - 1.28x slower +127.33 ms
Enum.reduce                1.65 - 1.35x slower +155.79 ms

Memory usage statistics:

Name                 Memory usage
:maps.fold              305.35 MB
Enumerable.reduce       610.89 MB - 2.00x memory usage +305.54 MB
Enum.reduce             534.26 MB - 1.75x memory usage +228.92 MB

**All measurements for memory usage were the same**

##### With input 1_000_000 elements #####
Name                        ips        average  deviation         median         99th %
:maps.fold                20.69       48.32 ms    ±34.23%       47.37 ms       64.95 ms
Enumerable.reduce         15.72       63.60 ms    ±33.28%       62.84 ms       80.84 ms
Enum.reduce               15.70       63.71 ms    ±29.81%       63.63 ms       70.67 ms

Comparison:
:maps.fold                20.69
Enumerable.reduce         15.72 - 1.32x slower +15.28 ms
Enum.reduce               15.70 - 1.32x slower +15.39 ms

Memory usage statistics:

Name                 Memory usage
:maps.fold               30.45 MB
Enumerable.reduce        60.98 MB - 2.00x memory usage +30.53 MB
Enum.reduce              53.38 MB - 1.75x memory usage +22.93 MB

**All measurements for memory usage were the same**
источник

SK

Simon Khaskelberg in pro.elixir
Т.е разница небольшая, но все же есть
источник

SK

Simon Khaskelberg in pro.elixir
Как по мне самое интересное это разница в памяти. Enum.reduce стабильно в 1.75 раза памяти ест больше чем :maps.fold
источник