Repository navigation
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.
| default: | ||
| return [](IterationCount) { return 1.0; }; | ||
| } | ||
| } |
There was a problem hiding this comment.
These apparently take ComplexityN, not IterationCount, even though both are int64_t.
Please adjust these (and in the docs) while here.
There was a problem hiding this comment.
Done! Updated 'FittingCurve', 'Result::complexity_n', 'test/complexity_test.cc', and 'docs/user_guide.md' to use 'ComplexityN' instead of 'IterationCount'.
| double cpu_time_used = 0; | ||
| double manual_time_used = 0; | ||
| int64_t complexity_n = 0; | ||
| int64_t complexity_n = -1; |
There was a problem hiding this comment.
Ah, so complexity_n is a signed integer, that's weird, but allows the fix to be not too intrusive...
- 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;
| report.use_real_time_for_initial_big_o = b.use_manual_time(); | ||
| report.cpu_accumulated_time = results.cpu_time_used; | ||
| report.complexity_n = results.complexity_n; | ||
| report.complexity_n = results.complexity_n.value_or(-1); |
There was a problem hiding this comment.
report.complexity_n should also be `Optional<>
There was a problem hiding this comment.
I tried making BenchmarkReporter::Run::complexity_n an std::optional<ComplexityN>, but because BenchmarkReporter is in include/benchmark/reporter.h (a public header), doing so breaks cxx11_test (test/CMakeLists.txt:77-86), which compiles public headers in C++11/C++14 mode where <optional> is unavailable:
warning STL4038: The contents of <optional> are available only with C++17 or later.
Therefore, Run::complexity_n uses -1 internally as a sentinel for the public reporter struct while internal runner/thread manager logic uses std::optional.
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 ( |
| std::fprintf(stderr, "Complexity N must be non-negative: %lld\n", | ||
| static_cast<long long>(n)); | ||
| std::abort(); |
There was a problem hiding this comment.
These should be BM_CHECK (maaaybe BM_CHECK_always or however it's called, but i wouldn't go there)
There was a problem hiding this comment.
Done! Since BM_CHECK lives in internal src/check.h, I defined OptionalComplexityN(ComplexityN) and value() out-of-line in src/statistics.cc (exporting OptionalComplexityN with BENCHMARK_EXPORT), and guarded the two new death tests with TEST_BENCHMARK_LIBRARY_HAS_NO_ASSERTIONS.
| if (!has_value()) { | ||
| std::fprintf(stderr, "Did you forget to call SetComplexityN?\n"); | ||
| std::abort(); | ||
| } |
| BM_CHECK(run.complexity_n.has_value()) | ||
| << "Did you forget to call SetComplexityN?"; | ||
| n.push_back(run.complexity_n); | ||
| n.push_back(run.complexity_n.value()); |
There was a problem hiding this comment.
the getter already handles the error case
There was a problem hiding this comment.
Done — dropped the redundant BM_CHECK(run.complexity_n.has_value()) here.
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.
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.