Skip to content

Commit

Permalink
Merge c3accbd into 995be92
Browse files Browse the repository at this point in the history
  • Loading branch information
elpikel committed Oct 7, 2017
2 parents 995be92 + c3accbd commit e5cfcd2
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 10 deletions.
199 changes: 199 additions & 0 deletions lib/benchee/conversion/memory.ex
@@ -0,0 +1,199 @@
defmodule Benchee.Conversion.Memory do
@moduledoc """
Unit scaling for memory converting from bytes to kilobytes and others.
"""

alias Benchee.Conversion.{Format, Scale, Unit}

@behaviour Scale
@behaviour Format

@bytes_per_kilobyte 1024
@bytes_per_megabyte @bytes_per_kilobyte * @bytes_per_kilobyte
@bytes_per_gigabyte @bytes_per_megabyte * @bytes_per_kilobyte
@bytes_per_terabyte @bytes_per_gigabyte * @bytes_per_kilobyte

@units %{
terabyte: %Unit{
name: :terabyte,
magnitude: @bytes_per_terabyte,
label: "TB",
long: "Terabytes"
},
gigabyte: %Unit{
name: :gigabyte,
magnitude: @bytes_per_gigabyte,
label: "GB",
long: "Gigabytes"
},
megabyte: %Unit{
name: :megabyte,
magnitude: @bytes_per_megabyte,
label: "MB",
long: "Megabytes"
},
kilobyte: %Unit{
name: :kilobyte,
magnitude: @bytes_per_kilobyte,
label: "KB",
long: "Kilobytes"
},
byte: %Unit{
name: :byte,
magnitude: 1,
label: "B",
long: "Bytes"
}
}

@doc """
Scales a memory value in bytes into a larger unit if appropriate
## Examples
iex> {value, unit} = Benchee.Conversion.Memory.scale(1)
iex> value
1.0
iex> unit.name
:byte
iex> {value, unit} = Benchee.Conversion.Memory.scale(1_234)
iex> value
1.205078125
iex> unit.name
:kilobyte
iex> {value, unit} = Benchee.Conversion.Memory.scale(11_234_567_890.123)
iex> value
10.463006692121736
iex> unit.name
:gigabyte
iex> {value, unit} = Benchee.Conversion.Memory.scale(1_111_234_567_890.123)
iex> value
1.0106619519229962
iex> unit.name
:terabyte
"""
def scale(memory) when memory >= @bytes_per_terabyte do
scale_with_unit memory, :terabyte
end
def scale(memory) when memory >= @bytes_per_gigabyte do
scale_with_unit memory, :gigabyte
end
def scale(memory) when memory >= @bytes_per_megabyte do
scale_with_unit memory, :megabyte
end
def scale(memory) when memory >= @bytes_per_kilobyte do
scale_with_unit memory, :kilobyte
end
def scale(memory) do
scale_with_unit memory, :byte
end

# Helper function for returning a tuple of {value, unit}
defp scale_with_unit(memory, unit) do
{scale(memory, unit), unit_for(unit)}
end

@doc """
Get a unit by its atom representation.
## Examples
iex> Benchee.Conversion.Memory.unit_for :gigabyte
%Benchee.Conversion.Unit{
name: :gigabyte,
magnitude: 1_073_741_824,
label: "GB",
long: "Gigabytes"
}
"""
def unit_for(unit) do
Scale.unit_for @units, unit
end

@doc """
Scales a memory value in bytes into a value in the specified unit
## Examples
iex> Benchee.Conversion.Memory.scale(12345, :kilobyte)
12.0556640625
iex> Benchee.Conversion.Memory.scale(12345, :megabyte)
0.011773109436035156
iex> Benchee.Conversion.Memory.scale(123_456_789, :gigabyte)
0.11497809458523989
"""
def scale(count, unit) do
Scale.scale count, unit, __MODULE__
end

@doc """
Finds the best unit for a list of memory units. By default, chooses the most common
unit. In case of tie, chooses the largest of the most common units.
Pass `[strategy: :smallest]` to always return the smallest unit in the list.
Pass `[strategy: :largest]` to always return the largest unit in the list.
Pass `[strategy: :best]` to always return the most frequent unit in the list.
Pass `[strategy: :none]` to always return :byte.
## Examples
iex> Benchee.Conversion.Memory.best([23, 23_000, 34_000, 2_340_000]).name
:kilobyte
iex> Benchee.Conversion.Memory.best([23, 23_000, 34_000, 2_340_000, 3_450_000]).name
:megabyte
iex> Benchee.Conversion.Memory.best([23, 23_000, 34_000, 2_340_000], strategy: :smallest).name
:byte
iex> Benchee.Conversion.Memory.best([23, 23_000, 34_000, 2_340_000], strategy: :largest).name
:megabyte
"""
def best(list, opts \\ [strategy: :best])
def best(list, opts) do
Scale.best_unit(list, __MODULE__, opts)
end

@doc """
The most basic unit in which memory occur, byte.
## Examples
iex> Benchee.Conversion.Memory.base_unit.name
:byte
"""
def base_unit, do: unit_for(:byte)

@doc """
Formats a number as a string, with a unit label. To specify the unit, pass
a tuple of `{value, unit_atom}` like `{1_234, :kilobyte}`
## Examples
iex> Benchee.Conversion.Memory.format(45_678.9)
"44.61 KB"
iex> Benchee.Conversion.Memory.format(45.6789)
"45.68 B"
iex> Benchee.Conversion.Memory.format({45.6789, :kilobyte})
"45.68 KB"
iex> Benchee.Conversion.Memory.format {45.6789,
...> %Benchee.Conversion.Unit{
...> long: "Kilobytes", magnitude: 1024, label: "KB"}
...> }
"45.68 KB"
"""
def format(memory) do
Format.format(memory, __MODULE__)
end
end
17 changes: 7 additions & 10 deletions lib/benchee/system.ex
Expand Up @@ -4,6 +4,7 @@ defmodule Benchee.System do
"""

alias Benchee.Suite
alias Benchee.Conversion.Memory

@doc """
Adds system information to the suite (currently elixir and erlang versions).
Expand Down Expand Up @@ -108,30 +109,26 @@ defmodule Benchee.System do
parse_memory_for(:Linux, system_cmd("cat", ["/proc/meminfo"]))
end

@kilobyte_to_gigabyte 1024 * 1024
@byte_to_gigabyte 1024 * @kilobyte_to_gigabyte

defp parse_memory_for(_, "N/A"), do: "N/A"
defp parse_memory_for(:Windows, raw_output) do
[memory] = Regex.run(~r/\d+/, raw_output)
{memory, _} = Integer.parse(memory)
format_memory(memory, @byte_to_gigabyte)
Memory.format(memory)
end
defp parse_memory_for(:macOS, raw_output) do
{memory, _} = Integer.parse(raw_output)
format_memory(memory, @byte_to_gigabyte)
Memory.format(memory)
end
defp parse_memory_for(:Linux, raw_output) do
["MemTotal:" <> memory] = Regex.run(~r/MemTotal.*kB/, raw_output)
{memory, _} = memory
["MemTotal:" <> memory_info] = Regex.run(~r/MemTotal.*kB/, raw_output)
{memory_in_kilobytes, _} = memory_info
|> String.trim()
|> String.trim_trailing(" kB")
|> Integer.parse
format_memory(memory, @kilobyte_to_gigabyte)
memory_in_bytes = memory_in_kilobytes * 1024
Memory.format(memory_in_bytes)
end

defp format_memory(memory, coefficient), do: "#{Float.round(memory / coefficient, 2)} GB"

def system_cmd(cmd, args, system_func \\ &System.cmd/2) do
{output, exit_code} = system_func.(cmd, args)
if exit_code > 0 do
Expand Down
55 changes: 55 additions & 0 deletions test/benchee/conversion/memory_test.exs
@@ -0,0 +1,55 @@
defmodule Benchee.Conversion.MemoryTest do
use ExUnit.Case, async: true
import Benchee.Conversion.Memory
doctest Benchee.Conversion.Memory

describe ".format" do
test ".format(1_023)" do
assert format(1_023) == "1023 B"
end

test ".format(1025)" do
assert format(1_025) == "1.00 KB"
end

test ".format(876_543_219.8765)" do
assert format(876_543_219.8765) == "835.94 MB"
end

test ".format(9_876_543_219.8765)" do
assert format(9_876_543_219.8765) == "9.20 GB"
end

test ".format(14_569_876_543_219.8765)" do
assert format(14_569_876_543_219.8765) == "13.25 TB"
end

test ".format(523.0)" do
assert format(523.0) == "523 B"
end

test ".format(0)" do
assert format(0) == "0 B"
end
end

@list_with_mostly_megabytes [1, 200, 3_000_000, 4_000_000, 50_000_000, 50_000_000, 77_000_000_000]

describe ".best" do
test "when list is mostly megabytes" do
assert best(@list_with_mostly_megabytes) == unit_for(:megabyte)
end

test "when list is mostly megabytes, strategy: :smallest" do
assert best(@list_with_mostly_megabytes, strategy: :smallest) == unit_for(:byte)
end

test "when list is mostly megabytes, strategy: :largest" do
assert best(@list_with_mostly_megabytes, strategy: :largest) == unit_for(:gigabyte)
end

test "when list is mostly megabytes, strategy: :none" do
assert best(@list_with_mostly_megabytes, strategy: :none) == unit_for(:byte)
end
end
end

0 comments on commit e5cfcd2

Please sign in to comment.