Repository navigation
Fix complexity calculation when range includes 0 (#410) - #2346
Conversation
Differentiate between uncalled SetComplexityN and SetComplexityN(0) by initializing State::complexity_n_ and ThreadManager::Result::complexity_n to -1, and relaxing the check in ComputeBigO to BM_CHECK_GE(run.complexity_n, 0). Add guards against n <= 1 in logarithmic curves and sigma_gn_squared == 0 in MinimalLeastSq. Add unit tests for zero-starting ranges and a death test for missing SetComplexityN calls.
- Use ComplexityN instead of IterationCount for complexity curves, Result::complexity_n, tests, and documentation. - Update complexity_n accumulation logic in RunInThread.
LebedevRI
left a comment
There was a problem hiding this comment.
The more i stare at this -1 the more i don't like it.
Please just do std::Optional<ComplexityN> complexity_n = None;
LebedevRI
left a comment
There was a problem hiding this comment.
Please update all complexity_n/complexity_n_ fields to all be Optional<>.
There should not be any == -1/<0/>=0 checks.
This reverts commit 172972c.
Thanks for the review. I ran into an issue making everything std::optional, so I’d like to ask for your input before going further. ThreadManager::Result is internal, so std::optional works fine there. The problem is State::complexity_n_ and BenchmarkReporter::Run::complexity_n, which are in public headers. cxx11_test builds those headers in C++11/14 mode, where doesn’t exist, so it breaks (MSVC: 'optional': is not a member of 'std'). Everything else builds fine. Also, changing the type of the public Run::complexity_n would break any custom reporter that reads it as an integer. I see three options: 1.Bump cxx11_test to require C++17, and use std::optional everywhere, including the public headers. Since this is more of a policy decision, I’d rather not pick one myself. Which would you prefer? |
Oh well, i forgot about that.
So here's the thing, this diff already breaks that. They most probably expect
I think it shouldn't be |
Got it, I've implemented One quick question before I push: since
|
|
Code around would desugar the new type into a valid |
Thanks! I've pushed this. The strict type is a separate Please let me know if you'd like a different name. The Python bindings ( |
LebedevRI
left a comment
There was a problem hiding this comment.
Nice, this looks somewhat reasonable.
I'll need to sleep on this, but i do believe this will be fine overall.
Thank you.
| ComplexityN OptionalComplexityN::value() const { | ||
| BM_CHECK(has_value()) << "Did you forget to call SetComplexityN?"; | ||
| return n_; | ||
| } |
There was a problem hiding this comment.
Can these be in the header?
There was a problem hiding this comment.
include/benchmark/statistics.h is a public installed header (and compiled under C++11 in cxx11_test), whereas BM_CHECK lives in internal src/check.h (which isn't installed and uses C++17 std::string_view), so BM_CHECK can only be used from .cc files under src/.
There was a problem hiding this comment.
Right. No idea why i said BM_CHECK. These should just be assert(), like in #2274.
There was a problem hiding this comment.
I've updated OptionalComplexityN to be header-only using assert() just like PR #2274, removed the definitions from src/statistics.cc, and switched to ASSERT_DEBUG_DEATH for the tests. Tested it locally and all 87 tests pass smoothly in both debug and release.
Thanks for pointing that out and bearing with me! Let me know if it looks good now.
|
Never seen that bazel failure before: The actual source file seems to vary between runs. |
|
Hi @LebedevRI just a follow up - any other change you want me to do on my side? thx! |
LebedevRI
left a comment
There was a problem hiding this comment.
I do not understand why the CI check is failing here, but not in other PR's/branches.
|
Could you re-run the earlier green bazel.ubuntu-latest job from this PR (commit f079722)? If it fails now, it’s the runner i think? |
|
@dmah42 halp. That workflow is marked as required and github does not allow me to merge this without it passing. |
|
i've been working on this, sorry. it's because there's a rollout changing "-latest" between two versions and our bazel cache is version-agnostic. |
|
Thank you @LebedevRI and @dmah42, I really appreciate the detailed reviews and your patience with the CI issues. I learned a lot from this one. |
|
@amoralpha thank you! @dmah42 thanks! |
Fixes #410.
complexity_n now defaults to -1 (“not set”) instead of 0, so SetComplexityN(0) is distinguishable from never calling it, as suggested in the issue. The check in ComputeBigO is relaxed to >= 0. Since n == 0 is now reachable, I added guards for log2(0) in oLogN/oNLogN and for a zero denominator in MinimalLeastSq.
Tests: zero-starting ranges for oN, auto, lambda, and oLogN with Range(0, 1), plus a death test for the missing SetComplexityN case. Without the fix, complexity_benchmark fails with the exact error from the issue (“Did you forget to call SetComplexityN?”).
Design note: n == 0 carries no asymptotic information, so the motivation is only that Range(0, ...) is a natural parameterization and currently aborts with a misleading message. If you’d prefer, I can instead keep n == 0 runs in the report but exclude them from the BigO fit.