Skip to content

Instantly share code, notes, and snippets.

@cr0t
Created June 19, 2023 16:50
Show Gist options
  • Save cr0t/299280930808b67b6db7742d6900d368 to your computer and use it in GitHub Desktop.
Save cr0t/299280930808b67b6db7742d6900d368 to your computer and use it in GitHub Desktop.
An Elixir benchmark idea of which born during discussion with one of my mentees on Exercism.io
Mix.install([:benchee])
defmodule Proteins do
@stop "STOP"
@err_msg "invalid codon"
@codons %{
"UGU" => "Cysteine",
"UGG" => "Tryptophan",
"UUU" => "Phenylalanine",
"UAA" => @stop
}
def rna_normal(rna) do
rna
|> parse()
|> Stream.map(&of_codon/1)
|> Stream.take_while(&(elem(&1, 1) not in [@stop, @err_msg]))
|> Enum.to_list()
end
def rna_throw(rna) do
try do
rna
|> parse()
|> Stream.transform([], fn codon, acc ->
case of_codon(codon) do
{:ok, @stop} -> throw(acc)
{:error, _} -> throw(acc)
protein -> {[protein], [protein | acc]}
end
end)
|> Enum.to_list()
catch
results -> results
end
end
# Helpers
defp parse(rna), do: Regex.scan(~r/.{3}/, rna) |> List.flatten()
defp of_codon(codon) when is_map_key(@codons, codon), do: {:ok, @codons[codon]}
defp of_codon(_), do: {:error, @err_msg}
end
# Preparations
# A long and correct strand of codons can be made by diplication
long_strand = String.duplicate("UGUUGGUUU", 33_000)
short_strand = String.duplicate("UGUUGGUUU", 1)
long_mid = "#{long_strand}UAA#{long_strand}"
long_err = "#{long_strand}ERR#{long_strand}"
short_mid = "#{short_strand}UAA#{short_strand}"
short_err = "#{short_strand}ERR#{short_strand}"
IO.puts("""
Length of the short strand is #{String.length(short_mid)} chars, it contains #{div(String.length(short_mid), 3)} codons
Length of the long strand is #{String.length(long_mid)} chars, it contains #{div(String.length(long_mid), 3)} codons
""")
# Just to check that our functions return the same output, we can compare it:
IO.inspect(
{
Proteins.rna_normal(long_mid) == Proteins.rna_throw(long_mid),
Proteins.rna_normal(long_err) == Proteins.rna_throw(long_err),
Proteins.rna_normal(short_mid) == Proteins.rna_throw(short_mid),
Proteins.rna_normal(short_err) == Proteins.rna_throw(short_err)
},
label: "rna_normal/1 == rna_throw/2"
)
# Benchmarks
Benchee.run(
%{
"mid_short_normal" => fn -> Proteins.rna_normal(short_mid) end,
"mid_short_throw" => fn -> Proteins.rna_throw(short_mid) end
},
memory_time: 2
)
# Benchmarking mid_short_normal ...
# Benchmarking mid_short_throw ...
# Name ips average deviation median 99th %
# mid_short_normal 133.82 K 7.47 μs ±161.36% 6.94 μs 14.33 μs
# mid_short_throw 130.04 K 7.69 μs ±208.18% 7.11 μs 18.98 μs
# Comparison:
# mid_short_normal 133.82 K
# mid_short_throw 130.04 K - 1.03x slower +0.22 μs
# Memory usage statistics:
# Name Memory usage
# mid_short_normal 3.24 KB
# mid_short_throw 3.86 KB - 1.19x memory usage +0.62 KB
Benchee.run(
%{
"mid_long_normal" => fn -> Proteins.rna_normal(long_mid) end,
"mid_long_throw" => fn -> Proteins.rna_throw(long_mid) end
},
memory_time: 2
)
# Benchmarking mid_long_normal ...
# Benchmarking mid_long_throw ...
# Name ips average deviation median 99th %
# mid_long_normal 4.56 219.16 ms ±14.19% 213.24 ms 332.82 ms
# mid_long_throw 3.44 291.10 ms ±12.84% 284.57 ms 356.07 ms
# Comparison:
# mid_long_normal 4.56
# mid_long_throw 3.44 - 1.33x slower +71.95 ms
# Memory usage statistics:
# Name Memory usage
# mid_long_normal 59.67 MB
# mid_long_throw 81.57 MB - 1.37x memory usage +21.90 MB
Benchee.run(
%{
"err_short_normal" => fn -> Proteins.rna_normal(short_err) end,
"err_short_throw" => fn -> Proteins.rna_throw(short_err) end
},
memory_time: 2
)
# Benchmarking err_short_normal ...
# Benchmarking err_short_throw ...
# Name ips average deviation median 99th %
# err_short_normal 131.65 K 7.60 μs ±377.34% 7.06 μs 13.76 μs
# err_short_throw 129.20 K 7.74 μs ±148.64% 7.21 μs 15.04 μs
# Comparison:
# err_short_normal 131.65 K
# err_short_throw 129.20 K - 1.02x slower +0.144 μs
# Memory usage statistics:
# Name Memory usage
# err_short_normal 3.23 KB
# err_short_throw 3.84 KB - 1.19x memory usage +0.60 KB
Benchee.run(
%{
"err_long_normal" => fn -> Proteins.rna_normal(long_err) end,
"err_long_throw" => fn -> Proteins.rna_throw(long_err) end
},
memory_time: 2
)
# Benchmarking err_long_normal ...
# Benchmarking err_long_throw ...
# Name ips average deviation median 99th %
# err_long_normal 4.48 223.34 ms ±13.81% 218.28 ms 337.31 ms
# err_long_throw 3.34 299.57 ms ±12.38% 303.89 ms 349.05 ms
# Comparison:
# err_long_normal 4.48
# err_long_throw 3.34 - 1.34x slower +76.23 ms
# Memory usage statistics:
# Name Memory usage
# err_long_normal 59.67 MB
# err_long_throw 81.57 MB - 1.37x memory usage +21.90 MB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment