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

Print at least three significant digits for times. #701

Merged
merged 1 commit into from
Dec 14, 2018

Conversation

EricWF
Copy link
Contributor

@EricWF EricWF commented Oct 10, 2018

Some benchmarks are particularly sensitive and they run in less than
a nanosecond. In order for the console reporter to provide meaningful
output for such benchmarks it needs to be able to display the times
using more resolution than a single nanosecond.

This patch changes the console reporter to print at least three
significant digits for all results.

@LebedevRI
Copy link
Collaborator

I wonder if it would be good to introduce something like a scientific notation "time unit" (i.e. autodetect each time)

@AppVeyorBot
Copy link

Build benchmark 1514 failed (commit e2e04fa150 by @EricWF)

@@ -98,6 +98,20 @@ static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt,
va_end(args);
}

static std::string FormatTime(double time) {
// Align decimal places...
if (time < 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: 1 is int, time is double.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ack. Fixed.

}

if (!result.report_big_o && !result.report_rms) {
printer(Out, COLOR_CYAN, "%10lld", result.iterations);
printer(Out, COLOR_CYAN, "%12lld", result.iterations);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hm, why change the iteration print format?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To align it with the Iterations header.

if (time < 100) {
return FormatString("%12.1f ", time);
}
return FormatString("%10.0f ", time);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have you considered letting the format string figure out the necessary padding at least?
(see man 3 vsprintf)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you clarify a bit?

The reason for the current implementation of FormatString is to ensure columns with the same place value align. For example.

Benchmark                                                Time                CPU         Iterations
----------------------------------------------------------------------------------------------------
BM_empty                                                0.572 ns            0.572 ns     1000000000
BM_empty/threads:72                                     0.021 ns            1.50  ns      958819032
BM_spin_empty/8                                         7.47  ns            7.47  ns      180868971
BM_spin_empty/512                                     954     ns          953     ns        1466617
BM_spin_empty/8192                                  15449     ns        15447     ns          90954
BM_spin_empty/8/threads:72                              0.258 ns           18.2   ns       72560520
BM_spin_empty/512/threads:72                           22.2   ns         1564     ns         872568
BM_spin_empty/8192/threads:72                         382     ns        26233     ns          38664

I'm not too familiar with printf specifiers, but if there is a tool I can use to do that, please let me know.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'll double-check if this can be achieved with the format specifiers,
but that new extra width looks worrying :(
I don't think we can do anything about it though; i like this in general.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I don't love the extra width either. But being able to see sub-nanosecond results is worth the hit I think.

@coveralls
Copy link

coveralls commented Oct 11, 2018

Coverage Status

Coverage increased (+0.06%) to 89.347% when pulling 9ba6c8e on efcs:print-more-precision into b171791 on google:master.

@AppVeyorBot
Copy link

Build benchmark 1515 completed (commit a310dd0e40 by @EricWF)

@LebedevRI
Copy link
Collaborator

I like this, but that padding/alignment is really bothering me :/
image
image

How important is it to do that alignment of . separator? Can it be avoided?

@EricWF EricWF requested a review from dmah42 October 11, 2018 15:58
@EricWF
Copy link
Contributor Author

EricWF commented Oct 11, 2018

How important is it to do that alignment of . separator? Can it be avoided?

I think it's pretty important. It's allows you to understand the magnitude of a benchmark by glancing.

What's you're objection exactly? To readability? Do you think the space between the value and ns is ugly?

@LebedevRI
Copy link
Collaborator

LebedevRI commented Oct 11, 2018

How important is it to do that alignment of . separator? Can it be avoided?

I think it's pretty important.

It's allows you to understand the magnitude of a benchmark by glancing.

True.

What's you're objection exactly? To readability? Do you think the space between the value and ns is ugly?

Readability is good; but yes, i don't like that wasted space, and the yet-increased width of the user-counter-less line.

@EricWF
Copy link
Contributor Author

EricWF commented Oct 11, 2018

How important is it to do that alignment of . separator? Can it be avoided?

I think it's pretty important.

It's allows you to understand the magnitude of a benchmark by glancing.

True.

What's you're objection exactly? To readability? Do you think the space between the value and ns is ugly?

Readability is good; but yes, i don't like that wasted space, and the yet-increased width of the user-counter-less line.

Arguabbly the space isn't "wasted", since I think we agree it has value in representing magnitude. Otherwise we would just remove it. But it's not ideal either.

I can get rid of a little width (4 characters) if we don't care about making BigO calculations aligned with everything else, but that's ugly and doesn't help much.

Do you think this issue should block the revision?

@LebedevRI
Copy link
Collaborator

Do you think this issue should block the revision?

There are two things here as far as i can see:

  1. Always print at least 3 significant digits
  2. Align/pad .

I like 1., but i'm not quite sure about 2. yet.
So i guess i'll defer to @dominichamon.

@EricWF
Copy link
Contributor Author

EricWF commented Oct 11, 2018

A couple of alternative solutions:

  1. Add a flag to change the alignment to always be right aligned to the unit, which should waste less space. But IMO getting the old behavior back isn't worth the cost of another flag.

  2. Use the "wasted space" explicitly so it seems "less wasted". That is, fill the wasted space with three trailing decimal digits for all outputs. I don't like this either because those digits aren't significant and make it harder to see the information that is.

  3. Just right align everything all the time. This means it's a lot harder for the user to determine the magnitude of a benchmark, and ever harder yet to compare two benchmarks next to each other.

@LebedevRI
Copy link
Collaborator

A couple of alternative solutions:

  1. Add a flag to change the alignment to always be right aligned to the unit, which should waste less space. But IMO getting the old behavior back isn't worth the cost of another flag.

I agree, a flag for this does not sound good.

  1. Use the "wasted space" explicitly so it seems "less wasted". That is, fill the wasted space with three trailing decimal digits for all outputs. I don't like this either because those digits aren't significant and make it harder to see the information that is.

And what is even worse, those 'zeros' are likely straight-up a lie.

  1. Just right align everything all the time. This means it's a lot harder for the user to determine the magnitude of a benchmark, and ever harder yet to compare two benchmarks next to each other.

I would personally go with 3., but it is also possible that i'm simply too picky here.
(Though it is undeniable that the new layout is more width-consuming.)

As i said, i'll defer to @dominichamon..

@LebedevRI
Copy link
Collaborator

(Though it is undeniable that the new layout is more width-consuming.)

(I.e. if one day i/someone else "finishes" support for custom timers, and a new time column can be arbitrarily added, the width will be in short supply)

@dmah42
Copy link
Member

dmah42 commented Oct 15, 2018

What's wrong with always printing 3 decimal places instead of 3 significant digits, and not having the check for < 100?

@LebedevRI
Copy link
Collaborator

LebedevRI commented Oct 15, 2018

If the time is 52321 ms, does one care if it is 52321.000 ms or 52321.444 ms ?
I'd think that goes against the reason for setting the time units in the first place..

@dmah42
Copy link
Member

dmah42 commented Oct 15, 2018

oh you updated that from .666 so i wouldn't complain about rounding :P

I think we're hitting newer use cases as this project gets broader adoption. In general, the library was expected to be used to run benchmarks with similar timescales, so you'd get ns or ms all the way down. If we're not seeing that then we need to think a bit about how to handle it.

One option is to pick a base time scale for the entire run and use that everywhere. I'm not sure that's a great idea, but it would at least make eyeballing the runs easy. When you mix timescales you need to take care that, say, 10ms and 100ns are not being mixed up by the reader.

High-level answer: I'm not a fan of the extra whitespace between the numbers and the units.

@LebedevRI
Copy link
Collaborator

oh you updated that from .666 so i wouldn't complain about rounding :P

:P

High-level answer: I'm not a fan of the extra whitespace between the numbers and the units.

Any opinion on simply not having those whitespaces, will that also be confusing?

@dmah42
Copy link
Member

dmah42 commented Oct 15, 2018

I think it would be. You'd have

100 ns
100 us

or worse

100 ns
 10 us

and would have to take care if you're comparing by eye. Perhaps we should have something like: one time scale per benchmark family?

@LebedevRI
Copy link
Collaborator

LebedevRI commented Oct 15, 2018

That is not what i meant. I was literally talking about the current code, but without that padding with spaces.
I.e.

...
100 ms
0.1 ms
  1 ms
...

@dmah42
Copy link
Member

dmah42 commented Oct 15, 2018

If it doesn't change time unit i think that's ok.

@EricWF
Copy link
Contributor Author

EricWF commented Oct 21, 2018

I think we're hitting newer use cases as this project gets broader adoption. In general, the library was expected to be used to run benchmarks with similar timescales, so you'd get ns or ms all the way down. If we're not seeing that then we need to think a bit about how to handle it.

One option is to pick a base time scale for the entire run and use that everywhere. I'm not sure that's a great idea, but it would at least make eyeballing the runs easy. When you mix timescales you need to take care that, say, 10ms and 100ns are not being mixed up by the reader.

Unless the user explicitly changes the time unit for a benchmark, this is the behavior we have today.
So in almost all cases I suspect we won't be mixing up the reader.

That is not what i meant. I was literally talking about the current code, but without that padding with spaces.
I.e.

...
100 ms
0.1 ms
  1 ms
...

If it doesn't change time unit i think that's ok.

I personally find the other format easier to read at a glance. Especially as numbers are flying by which is often the case.

@EricWF
Copy link
Contributor Author

EricWF commented Oct 21, 2018

Test... I'm having trouble posting.

Some benchmarks are particularly sensitive and they run in less than
a nanosecond. In order for the console reporter to provide meaningful
output for such benchmarks it needs to be able to display the times
using more resolution than a single nanosecond.

This patch changes the console reporter to print at least three
significant digits for all results.

Unlike the initial attempt, this patch does not align the decimal point.
@EricWF EricWF force-pushed the print-more-precision branch from cc13eea to 9ba6c8e Compare November 21, 2018 21:35
@EricWF
Copy link
Contributor Author

EricWF commented Nov 21, 2018

I've updated the patch to no longer align the decimal point as requested.

@AppVeyorBot
Copy link

Build benchmark 1565 completed (commit 0ca09d3fe9 by @EricWF)

@AppVeyorBot
Copy link

Build benchmark 1566 completed (commit 5e0cb5180f by @EricWF)

Copy link
Collaborator

@LebedevRI LebedevRI left a comment

Choose a reason for hiding this comment

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

Can some test be updated to fully show all the spaces?
And/or, could you post a screenshot?

@@ -98,6 +98,21 @@ static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt,
va_end(args);
}


static std::string FormatTime(double time) {
// Align decimal places...
Copy link
Collaborator

Choose a reason for hiding this comment

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

Comment outdated

@@ -521,7 +521,7 @@ ADD_CASES(TC_ConsoleOut, {{"^BM_UserStats/iterations:5/repeats:3/manual_time [ "
{"^BM_UserStats/iterations:5/repeats:3/"
"manual_time_median [ ]* 150 ns %time [ ]*3$"},
{"^BM_UserStats/iterations:5/repeats:3/"
"manual_time_stddev [ ]* 0 ns %time [ ]*3$"},
"manual_time_stddev [ ]* 0.000 ns %time [ ]*3$"},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the zero be treated differently?

@dmah42
Copy link
Member

dmah42 commented Nov 22, 2018

LGTM. I ran it and master and took a diff and it looks much nicer with the extra precision.

Copy link
Collaborator

@LebedevRI LebedevRI left a comment

Choose a reason for hiding this comment

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

LG in general, i really like this.
It would be great to have better test coverage for this though.

@@ -53,7 +53,7 @@ bool ConsoleReporter::ReportContext(const Context& context) {
}

void ConsoleReporter::PrintHeader(const Run& run) {
std::string str = FormatString("%-*s %13s %13s %10s", static_cast<int>(name_field_width_),
std::string str = FormatString("%-*s %13s %15s %12s", static_cast<int>(name_field_width_),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure, is this still needed now that no alignment happens?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. There was a bug originally where we misaligned the last couple fields of the header when complexity was involved.

@EricWF EricWF merged commit 4528c76 into google:master Dec 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants