Mix.install([
{:benchee, "~> 1.0"},
{:kino_benchee, "~> 0.1.0"},
{:faker, "~> 0.18"}
])
defmodule StringExt do
defguardp is_ending_with(str, pat) when binary_part(str, byte_size(str), -byte_size(pat)) == pat
def rsplit(string, pattern), do: do_rsplit(string, pattern, "")
defp do_rsplit("", _pat, acc), do: ["", acc]
defp do_rsplit(str, pat, acc) when is_ending_with(str, pat), do: [binary_part(str, 0, byte_size(str)-byte_size(pat)), acc]
defp do_rsplit(str, pat, acc) do
<<front::binary-size(byte_size(str) - 1), c>> = str
do_rsplit(front, pat, <<c>> <> acc)
end
end
defmodule Benchmarks do
def string_split_list_last(input) do
input |> String.split("_") |> List.last()
end
@pattern ~r/([^_]*)$/
def regex(input) do
case Regex.run(@pattern, input, capture: :first) do
[a] -> a
_ -> ""
end
end
def split_trailing(input) do
case :string.split(input, "_", :trailing) do
[a] -> a
[_, a] -> a
end
end
def rsplit(input) do
[_, a] = StringExt.rsplit(input, "_")
a
end
end
Benchee.run(
%{
"String.split |> List.last" => &Benchmarks.string_split_list_last/1,
"Regex.run |> List.first" => &Benchmarks.regex/1,
":string.split(_, _, :trailing)" => &Benchmarks.split_trailing/1,
"StringExt.rsplit()" => &Benchmarks.rsplit/1
},
inputs: %{
"empty" => "",
"none" => Faker.Lorem.word(),
"small" => Enum.map_join(1..10, "_", fn _ -> Faker.Lorem.word() end),
"medium" => Enum.map_join(1..1000, "_", fn _ -> Faker.Lorem.word() end),
"large" => Enum.map_join(1..100000, "_", fn _ -> Faker.Lorem.word() end)
}
)