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

First version of separating memory and runtime measurements #204

Merged
merged 9 commits into from
Apr 11, 2018

Conversation

PragTob
Copy link
Member

@PragTob PragTob commented Apr 2, 2018

Introduces a new Measurer behaviour that can be adopted by Time
and Memory and in the future Reduction. Also switches over to
memory having its own configuration for time. Like how this ended up
in general.

This isn't merge ready yet, it's a first implementation already
up for review and CI.

Fixes #202

Ah yeah, most important proof - it actually fixes the problem - memory measurements don't seem to be impacting runtime measurements anymore:

tobi@speedy ~/github/benchee $ mix run samples/run.exs 
Name                  ips        average  deviation         median         99th %
flat_map           2.16 K      463.52 μs    ±10.62%         452 μs         762 μs
map.flatten        1.16 K      863.87 μs    ±16.60%         822 μs     1301.96 μs

Comparison: 
flat_map           2.16 K
map.flatten        1.16 K - 1.86x slower
# include memory measurements in the run.exs
tobi@speedy ~/github/benchee $ mix run samples/run.exs 
Name                  ips        average  deviation         median         99th %
flat_map           2.30 K      435.69 μs     ±9.88%         429 μs         720 μs
map.flatten        1.19 K      838.40 μs    ±16.38%         803 μs        1223 μs

Comparison: 
flat_map           2.30 K
map.flatten        1.19 K - 1.92x slower

Memory usage statistics:

Name           Memory usage
flat_map          625.54 KB
map.flatten       781.85 KB - 1.25x memory usage

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

TODO:

  • README/documentation adjustments
  • respect memory_time during calculation of estimated total runtime
  • allow measurer to return nil when a a measurement "failed" and handle it appropriately (memory measurement is less than 0 case)

This also sort of sets up putting do_benchmark/3 into its own module as it is responsible for running the benchmarking with a given measurer. The rest of the module deals with setting up the measurements according to our different measurer.

PragTob added 2 commits April 2, 2018 18:15
Introduces a new `Measurer` behaviour that can be adopted by `Time`
and `Memory` and in the future `Reduction`. Also switches over to
memory having its own configuration for time. Like how this ended up
in general.

This isn't merge ready yet, it's a first implementation already
up for review and CI.

TODO:
- [ ] README/documentation adjustments
- [ ] respect `memory_time` during calculation of estimated total runtime
- [ ] allow measurer to return `nil` when a a measurement "failed" and handle it appropriately (memory measurement is less than 0 case)
@@ -146,7 +146,7 @@ defmodule Benchee.Benchmark.RunnerTest do
assert [memory_consumption] = Enum.uniq(memory_usages)
assert memory_consumption >= 1
# depending on the number iterations determined, there can be spikes/changes
assert memory_consumption <= 100
assert memory_consumption <= 1_000
Copy link
Member Author

Choose a reason for hiding this comment

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

woopsie I added a 0 here! ;)

Before this was run in the repeated code with a num_iterations of 100 or 10. It seems that we have some small basic overhead that now shows up again more. We can have a look at removing this some time later.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just wrote a small script and memory usage for an empty function without anything seems to be 616 Byte on my machine.

Copy link
Collaborator

@wasnotrice wasnotrice left a comment

Choose a reason for hiding this comment

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

This turned out nicely! What do you think about changing the behavior name to Measure? I think it would improve readability. Like when you pass Measure.Time as an argument. And it is easier to type/pronounce! :)

alias Benchee.Benchmark.{Scenario, ScenarioContext}
alias Benchee.Utility.{RepeatN, Parallel}
alias Benchee.Configuration
alias Benchee.Benchmark.Measurer
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe place this alias with the others from Benchmark

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 damn me, totally - thanks Eric!

@PragTob
Copy link
Member Author

PragTob commented Apr 3, 2018

Good idea on the naming too, thanks! I think that was my OOP mindset leaking through as in the thing that measures but just Measure is nicer I think - thanks!

Copy link
Collaborator

@devonestes devonestes left a comment

Choose a reason for hiding this comment

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

This is a great start!

My only concern at the moment is with naming. In our configuration, the keys time and memory_time don't make it clear that they're exclusive of one another. I could see people thinking that memory_time is how much of the time we want to devote to measuring memory.

That said, this might be able to be cleared up just with documentation. It would be awesome if we could come up with something that didn't require documentation to make that clear, but nothing is coming to me yet this morning. I'll keep thinking about it, though 😄

@PragTob
Copy link
Member Author

PragTob commented Apr 4, 2018

Yeah nothing comes to my mind as well. That was one of my concerns with these... we could rename time to run_time but I don't think that really helps or what do you think? (it'd be breaking though, could be a fall back but I dunno I still see it as the main use case)

@devonestes
Copy link
Collaborator

Since we're still pre-1.0, now would be the time to make those breaking changes if we feel they're the best way to go 😉

However, run_time is similarly confusing, so I don't think that's a great change to go with. What about memory_duration, run_time_duration, and (later on) reductions_duration? That's the best synonym for time I've been able to come up with, but I'm not really in love with it.

@PragTob
Copy link
Member Author

PragTob commented Apr 5, 2018

but what good would chaning it from time to duration be?

I think it's better if the times always just add to each other - because if they sort of divide the time amongst each other all sorts of edge cases pop up... :(

@PragTob
Copy link
Member Author

PragTob commented Apr 7, 2018

Ok this should be done now from me... but one of the builds failed (edit: ok was just a hex hiccup) I'll see real quick what that is about hopefully.

Reviews appreciated 🙏

🐰 🐰 🐰

whatsapp image 2018-04-06 at 22 29 03

* Fixes #206
* Also make sure that we're not printing that we are "benchmarking"
@PragTob
Copy link
Member Author

PragTob commented Apr 8, 2018

Includes a fix for #206 now :)

edit: as it includes another fix now but they are all relatively separate, reviewing commits separately might help :)

Copy link
Collaborator

@devonestes devonestes left a comment

Choose a reason for hiding this comment

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

This looks great! Just one little documentation thing that I think might be helpful, and then it's ready to go.

IO.puts "Available memory: #{available_memory}"
IO.puts "Elixir #{elixir_version}"
IO.puts "Erlang #{erlang_version}"
IO.puts """
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thank you 👍

README.md Outdated
@@ -143,6 +145,7 @@ The available options are the following (also documented in [hexdocs](https://he

* `warmup` - the time in seconds for which a benchmarking job should be run without measuring times before real measurements start. This simulates a _"warm"_ running system. Defaults to 2.
* `time` - the time in seconds for how long each individual benchmarking job should be run and measured. Defaults to 5.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe this should be updated to make it super clear that this is only to measure the runtime performance?

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 definitely, good catch - thank you! I'll do that tomorrow :)

```

Memory time can be specified separately as it will often be constant - so it might not need as much measuring time.
Copy link
Collaborator

Choose a reason for hiding this comment

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

🙏

@PragTob
Copy link
Member Author

PragTob commented Apr 11, 2018

Assuming that this is good now --> (devon's comments where documentation related I think I fixed those and if not we can always adjust again)

@PragTob PragTob merged commit 2bdb361 into master Apr 11, 2018
@PragTob PragTob deleted the separate-memory-and-runtime-measurement branch April 11, 2018 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Memory measurements do influence runtime measurements
3 participants