Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable tprof #441

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/benchee/profile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ defmodule Benchee.Profile do

@default_profiler :eprof
@builtin_profilers [:cprof, :eprof, :fprof]
# https://hexdocs.pm/mix/1.17.0/Mix.Tasks.Profile.Tprof.html
# Was introduced in 1.17.0, it also requires OTP 27 but I trust it to error fine for
# that case itself
if Version.match?(System.version(), ">= 1.17.0") do
@builtin_profilers [:tprof | @builtin_profilers]
end

# we run the function a bunch already, no need for further warmup
@default_profiler_opts [warmup: false]

Expand Down
83 changes: 0 additions & 83 deletions samples/fast_with_profiling.ex

This file was deleted.

157 changes: 157 additions & 0 deletions samples/fast_with_profiling.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
list = Enum.to_list(1..10_000)
map_fun = fn i -> [i, i * i] end

Benchee.run(
%{
"flat_map" => fn -> Enum.flat_map(list, map_fun) end,
"map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end
},
warmup: 0.1,
time: 0.3,
memory_time: 0.3,
profile_after: true
)

Benchee.run(
%{
"flat_map" => fn -> Enum.flat_map(list, map_fun) end,
"map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end
},
warmup: 0.1,
time: 0.3,
memory_time: 0.3,
profile_after: :tprof
)

# tobi@qiqi:~/github/benchee(main)$ mix run samples/fast_with_profiling.exs
# Compiling 2 files (.ex)
# Operating System: Linux
# CPU Information: AMD Ryzen 9 5900X 12-Core Processor
# Number of Available Cores: 24
# Available memory: 31.26 GB
# Elixir 1.17.3
# Erlang 27.1
# JIT enabled: true

# Benchmark suite executing with the following configuration:
# warmup: 100 ms
# time: 300 ms
# memory time: 300 ms
# reduction time: 0 ns
# parallel: 1
# inputs: none specified
# Estimated total run time: 1 s 400 ms

# Benchmarking flat_map ...
# Benchmarking map.flatten ...
# Calculating statistics...
# Formatting results...

# Name ips average deviation median 99th %
# flat_map 2.24 K 447.25 μs ±53.59% 322.59 μs 1096.32 μs
# map.flatten 1.61 K 620.76 μs ±38.00% 689.02 μs 963.44 μs

# Comparison:
# flat_map 2.24 K
# map.flatten 1.61 K - 1.39x slower +173.51 μs

# Memory usage statistics:

# Name Memory usage
# flat_map 625 KB
# map.flatten 781.25 KB - 1.25x memory usage +156.25 KB

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

# Profiling flat_map with eprof...

# Profile results of #PID<0.1617.0>
# # CALLS % TIME µS/CALL
# Total 30004 100.0 3779 0.13
# Enum.flat_map/2 1 0.00 0 0.00
# anonymous fn/2 in :elixir_compiler_2.__FILE__/1 1 0.00 0 0.00
# :erlang.apply/2 1 0.03 1 1.00
# :erlang.++/2 10000 14.58 551 0.06
# Enum.flat_map_list/2 10001 42.13 1592 0.16
# anonymous fn/1 in :elixir_compiler_2.__FILE__/1 10000 43.27 1635 0.16

# Profile done over 6 matching functions

# Profiling map.flatten with eprof...

# Profile results of #PID<0.1619.0>
# # CALLS % TIME µS/CALL
# Total 60007 100.0 5281 0.09
# List.flatten/1 1 0.00 0 0.00
# Enum.map/2 1 0.00 0 0.00
# :lists.flatten/1 1 0.00 0 0.00
# :erlang.apply/2 1 0.04 2 2.00
# anonymous fn/2 in :elixir_compiler_2.__FILE__/1 1 0.04 2 2.00
# anonymous fn/1 in :elixir_compiler_2.__FILE__/1 10000 19.90 1051 0.11
# Enum."-map/2-lists^map/1-1-"/2 10001 26.60 1405 0.14
# :lists.do_flatten/2 40001 53.42 2821 0.07

# Profile done over 8 matching functions
# Operating System: Linux
# CPU Information: AMD Ryzen 9 5900X 12-Core Processor
# Number of Available Cores: 24
# Available memory: 31.26 GB
# Elixir 1.17.3
# Erlang 27.1
# JIT enabled: true

# Benchmark suite executing with the following configuration:
# warmup: 100 ms
# time: 300 ms
# memory time: 300 ms
# reduction time: 0 ns
# parallel: 1
# inputs: none specified
# Estimated total run time: 1 s 400 ms

# Benchmarking flat_map ...
# Benchmarking map.flatten ...
# Calculating statistics...
# Formatting results...

# Name ips average deviation median 99th %
# flat_map 2.42 K 412.87 μs ±48.32% 323.59 μs 737.84 μs
# map.flatten 1.72 K 582.68 μs ±35.48% 673.13 μs 987.99 μs

# Comparison:
# flat_map 2.42 K
# map.flatten 1.72 K - 1.41x slower +169.81 μs

# Memory usage statistics:

# Name Memory usage
# flat_map 625 KB
# map.flatten 781.25 KB - 1.25x memory usage +156.25 KB

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

# Profiling flat_map with tprof...

# Profile results of #PID<0.3062.0>
# # CALLS % TIME µS/CALL
# Total 30003 100.00 3686 0.12
# Enum.flat_map/2 1 0.00 0 0.00
# anonymous fn/2 in :elixir_compiler_2.__FILE__/1 1 0.05 2 2.00
# :erlang.++/2 10000 14.46 533 0.05
# anonymous fn/1 in :elixir_compiler_2.__FILE__/1 10000 33.67 1241 0.12
# Enum.flat_map_list/2 10001 51.82 1910 0.19
Copy link

@ruslandoga ruslandoga Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there is a way to pass opts to the profilers, like call_memory to tprof to get heap allocs?

https://hexdocs.pm/mix/Mix.Tasks.Profile.Tprof.html#profile/2-options

Copy link

@ruslandoga ruslandoga Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchee already supports profile_after: {profiler, opts}

🤦

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! See https://github.com/bencheeorg/benchee?tab=readme-ov-file#configuration for profile_after

a profiler - either as a tuple of {profiler, opts} (e.g., {:fprof, [sort: :own]}) or just the profiler (e.g., :fprof), which is equivalent to {profiler, []}. The accepted built-in profilers are :cprof, :eprof and :fprof.

Should make that clearer/include it in the profiler section of the README though!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah didn't see your response, sometimes github auto fetches sometimes it doesn't - but yeah :)


# Profile done over 5 matching functions

# Profiling map.flatten with tprof...

# Profile results of #PID<0.3064.0>
# # CALLS % TIME µS/CALL
# Total 60006 100.00 5316 0.09
# List.flatten/1 1 0.00 0 0.00
# Enum.map/2 1 0.00 0 0.00
# :lists.flatten/1 1 0.00 0 0.00
# anonymous fn/2 in :elixir_compiler_2.__FILE__/1 1 0.06 3 3.00
# anonymous fn/1 in :elixir_compiler_2.__FILE__/1 10000 24.72 1314 0.13
# Enum."-map/2-lists^map/1-1-"/2 10001 25.81 1372 0.14
# :lists.do_flatten/2 40001 49.42 2627 0.07
3 changes: 1 addition & 2 deletions test/benchee/profile_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@
assert_receive {:profiling, ^name, ^profiler}
end

@profilers Profile.builtin_profilers()
for profiler <- @profilers do
for profiler <- Profile.builtin_profilers() do
@profiler profiler
# can't say warmup as some profilers will have it in the profile messing with the test
describe "warming up behavior with #{@profiler}" do
Expand All @@ -103,7 +102,7 @@
end

# can't say warmup in the test description as eprof picks it up and then it matches
test "the function will be called exactly once by default for profiling" do

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 25.3)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 25.3)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 26.2)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 26.2)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 25.3)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 25.3)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 26.2)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)

Check failure on line 105 in test/benchee/profile_test.exs

View workflow job for this annotation

GitHub Actions / Test on Ubuntu (Elixir 1.17.3, OTP 26.2)

test warming up behavior with tprof the function will be called exactly once by default for profiling (Benchee.ProfileTest)
retrying(fn ->
output = capture_profile_io_and_message_with_config(@profiler)

Expand Down
Loading