diff --git a/bench/combine_binary.exs b/bench/combine_binary.exs new file mode 100644 index 00000000..638c5730 --- /dev/null +++ b/bench/combine_binary.exs @@ -0,0 +1,37 @@ +defmodule CombineBinary do + def binary_join(blocks) do + Enum.reduce(blocks, "", fn block, acc -> acc <> block end) + end + + # def iolist([block]) do + # block + # end + + def iolist(blocks) do + blocks + |> Enum.reduce([], fn block, acc -> [acc, block] end) + |> IO.iodata_to_binary + end + + def reverse_join(blocks) do + blocks + |> Enum.reverse + |> Enum.join("") + end +end + +block = String.duplicate("block", 100) +inputs = %{ + "1" => [:binary.copy(block)], + "10" => for(_i <- 0..10, do: :binary.copy(block)), + "100" => for(_i <- 0..100, do: :binary.copy(block)) +} + +Benchee.run(%{ + #"reverse_join": &CombineBinary.reverse_join/1, + "binary_join": &CombineBinary.binary_join/1, + "iolist": &CombineBinary.iolist/1, + }, + inputs: inputs, + print: [fast_warning: false] +) diff --git a/bench/date_time_compare.exs b/bench/date_time_compare.exs new file mode 100644 index 00000000..4da75e10 --- /dev/null +++ b/bench/date_time_compare.exs @@ -0,0 +1,20 @@ +smaller = DateTime.from_unix!(1_234_567) +bigger = DateTime.from_unix!(1_234_568) +much_bigger = DateTime.from_unix!(9_867_543) + +inputs = %{ + "close": {smaller, bigger}, + "close reversed": {bigger, smaller}, + "far": {smaller, much_bigger}, + "far reversed": {much_bigger, smaller} +} +Benchee.run( + %{ + "enum": fn {smaller, bigger} -> Enum.min_by([smaller, bigger], &DateTime.to_unix/1) end, + "apply": fn {smaller, bigger} -> apply(Enum, :min_by, [[smaller, bigger], &DateTime.to_unix/1]) end, + "compare": fn {smaller, bigger} -> if DateTime.compare(smaller, bigger) == :lt, do: smaller, else: bigger end + }, + time: 5, + inputs: inputs, + print: [fast_warning: false] +) diff --git a/bench/drop_nil_values.exs b/bench/drop_nil_values.exs new file mode 100644 index 00000000..e6d1e50b --- /dev/null +++ b/bench/drop_nil_values.exs @@ -0,0 +1,54 @@ +defmodule DropNilValues do + def reordered(map) do + :maps.fold( + fn + _k, v, acc when not is_nil(v) -> acc + k, nil, acc -> Map.delete(acc, k) + end, + map, + map + ) + end + + def delete(map) do + :maps.fold( + fn + k, nil, acc -> Map.delete(acc, k) + _k, _v, acc -> acc + end, + map, + map + ) + end + + def original(map) do + :maps.fold( + fn + _k, nil, acc -> acc + k, v, acc -> Map.put(acc, k, v) + end, + %{}, + map + ) + end +end + +Benchee.run( + %{ + reordered: &DropNilValues.reordered/1, + delete: &DropNilValues.delete/1, + original: &DropNilValues.original/1 + }, + print: [fast_warning: false], + inputs: %{ + third_nils_big: Map.new(0..10, fn i -> + if div(i, 3) == 0, do: {i, i}, else: {i, nil} + end), + quarter_nils_big: Map.new(0..10, fn i -> + if div(i, 4) == 0, do: {i, i}, else: {i, nil} + end), + half_nils_big: Map.new(0..10, fn i -> + if div(i, 2) == 0, do: {i, i}, else: {i, nil} + end) + } +) diff --git a/bench/group.exs b/bench/group.exs new file mode 100644 index 00000000..a20a23e2 --- /dev/null +++ b/bench/group.exs @@ -0,0 +1,14 @@ +data = Concentrate.Parser.GTFSRealtime.parse(File.read!("/tmp/TripDescriptors.pb"), []) + +Benchee.run( + %{ + "original": &Concentrate.Encoder.GTFSRealtimeHelpers.group/1, + "new": &Concentrate.Encoder.GTFSRealtimeHelpers.group_new/1 + #"enum": fn -> Concentrate.Merge.merge(Enum.flat_map(data, &elem(&1, 1))) end, + ##"stream": fn -> Concentrate.Merge.merge(Stream.flat_map(data, &elem(&1, 1))) end, + #"flatten": fn -> Concentrate.Merge.merge(List.flatten(Map.values(data))) end, + #"new": fn -> Concentrate.Merge.merge_new(Enum.flat_map(data, &elem(&1, 1))) end + #"ets": fn -> Concentrate.Merge.merge_ets(data) end + }, + inputs: %{data: data}, + time: 10) diff --git a/bench/map_reduce.exs b/bench/map_reduce.exs new file mode 100644 index 00000000..9c9aa372 --- /dev/null +++ b/bench/map_reduce.exs @@ -0,0 +1,50 @@ +defmodule MapReduce do + def original(map) do + acc = {:cont, 0} + Enumerable.List.reduce(:maps.to_list(map), acc, &sum/2) + end + + def maybe_iterator(map) when map_size(map) <= 131_072 do # 2 ** 9 + original(map) + end + def maybe_iterator(map) do + iterator(map) + end + + def iterator(map) do + acc = {:cont, 0} + iter = :maps.iterator(map) + reduce_iter(iter, acc, &sum/2) + end + + defp reduce_iter(_iter, {:halt, acc}, _fun), do: {:halted, acc} + defp reduce_iter(iter, {:suspend, acc}, fun), do: {:suspended, acc, &reduce_iter(iter, &1, fun)} + defp reduce_iter(iter, {:cont, acc}, fun) do + case :maps.next(iter) do + {key, value, iter} -> + reduce_iter(iter, fun.({key, value}, acc), fun) + :none -> + {:done, acc} + end + end + + defp sum(_, b), do: {:cont, b} +end + +Benchee.run( + %{ + original: &MapReduce.original/1, + maybe_iterator: &MapReduce.iterator/1, + iterator: &MapReduce.iterator/1, + }, + warmup: 1, + memory_time: 2, + print: [fast_warning: false], + inputs: %{ + small: Map.new(0..31, fn i -> {i, i} end), + medium: Map.new(0..1023, fn i -> {i, i} end), + large: Map.new(0..32_767, fn i -> {i, i} end), + one_thirty_two: Map.new(0..(div(1_048_576, 8) - 1), fn i -> {i, i} end), + xlarge: Map.new(0..1_048_575, fn i -> {i, i} end) + } +) diff --git a/bench/merge_mod.exs b/bench/merge_mod.exs new file mode 100644 index 00000000..eddd31fd --- /dev/null +++ b/bench/merge_mod.exs @@ -0,0 +1,26 @@ +alias Concentrate.{TripDescriptor, VehiclePosition, StopTimeUpdate} +alias Concentrate.Merge.{Table, TableOrig} + +data = for file <- Path.wildcard("/tmp/*.pb") do + Concentrate.Parser.GTFSRealtime.parse(File.read!(file), []) +end |> List.flatten +sort_fn = fn x -> + case x do + %TripDescriptor{} -> 0 + %VehiclePosition{} -> 1 + %StopTimeUpdate{} -> 2 + _ -> 4 + end +end + +Benchee.run( + %{ + "original": fn -> TableOrig.new() |> TableOrig.add(:source) |> TableOrig.update(:source, data) |> TableOrig.items |> Enum.sort_by(&Concentrate.Mergeable.sort_key/1) end, + # "enum": fn -> Concentrate.Merge.merge(Enum.flat_map(data, &elem(&1, 1))) end, + # "stream": fn -> Concentrate.Merge.merge(Stream.flat_map(data, &elem(&1, 1))) end, + # "flatten": fn -> Concentrate.Merge.merge(List.flatten(Map.values(data))) end, + "new": fn -> Table.new() |> Table.add(:source) |> Table.update(:source, data) |> Table.items end + #proto": fn -> Concentrate.MergeProto.merge(data) end, + #"ets": fn -> Concentrate.Merge.merge_ets(data) end + }, + time: 10) diff --git a/bench/recent.exs b/bench/recent.exs new file mode 100644 index 00000000..82f068b9 --- /dev/null +++ b/bench/recent.exs @@ -0,0 +1,33 @@ +defmodule BenchRecent do + @moduledoc false + @data Enum.to_list(0..1000) + @values (for key <- ~w(a b c b c a)a do + {key, @data} + end) + + @selector {:_, :"$1"} + + def map_flat(_) do + state = %{} + {[_ | _], _} = Enum.reduce(@values, {nil, state}, fn {key, value}, {_, acc} -> + acc = Map.put(acc, key, value) + {Enum.flat_map(acc, &elem(&1, 1)), acc} + end) + end + + def ets(state) do + [_ | _] = Enum.reduce(@values, nil, fn {key, value}, _ -> + :ets.delete(state, key) + inserts = for v <- value, do: {key, v} + _ = :ets.insert(state, inserts) + :ets.match(state, @selector) + end) + end +end + +Benchee.run(%{ + "map_flat": &BenchRecent.map_flat/1, + "ets": &BenchRecent.ets/1, + }, + before_each: fn input -> :ets.new(:tab, [:bag]) end +) diff --git a/bench/skipped_departures.exs b/bench/skipped_departures.exs new file mode 100644 index 00000000..cd50b387 --- /dev/null +++ b/bench/skipped_departures.exs @@ -0,0 +1,30 @@ +alias Concentrate.{TripDescriptor, StopTimeUpdate} + +td = TripDescriptor.new([]) +five_skipped = for _ <- 0..4 do + StopTimeUpdate.new(schedule_relationship: :SKIPPED) +end +one_skipped = Enum.take(five_skipped, 1) +ten_normal = for _ <- 0..9 do + StopTimeUpdate.new(departure_time: 5) +end + +Benchee.run( + %{ + "original": &Concentrate.GroupFilter.SkippedDepartures.filter/1, + "new": &Concentrate.GroupFilter.SkippedDeparturesNew.filter/1 + #"enum": fn -> Concentrate.Merge.merge(Enum.flat_map(data, &elem(&1, 1))) end, + ##"stream": fn -> Concentrate.Merge.merge(Stream.flat_map(data, &elem(&1, 1))) end, + #"flatten": fn -> Concentrate.Merge.merge(List.flatten(Map.values(data))) end, + #"new": fn -> Concentrate.Merge.merge_new(Enum.flat_map(data, &elem(&1, 1))) end + #"ets": fn -> Concentrate.Merge.merge_ets(data) end + }, + inputs: %{ + no_skipped: {td, [], ten_normal}, + one_skipped_end: {td, [], ten_normal ++ one_skipped}, + one_skipped: {td, [], ten_normal ++ one_skipped ++ ten_normal}, + five_skipped_end: {td, [], ten_normal ++ five_skipped}, + five_skipped: {td, [], ten_normal ++ five_skipped ++ ten_normal} + }, + print: [fast_warning: false], + time: 5)