Skip to content

Add support for microseconds in Stopwatch #23223

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

Closed
wants to merge 12 commits into from

Conversation

javiereguiluz
Copy link
Member

Q A
Branch? 3.4
Bug fix? no
New feature? yes
BC breaks? no?
Deprecations? no
Tests pass? yes
Fixed tickets #18756
License MIT
Doc PR -

Stopwatch component already supports microseconds (and nanoseconds, etc.) ... but the problem is that it converts the millisecond times to integers, so anything smaller to 1 millisecond is lost. This PR changes that to fix issues like the one explained in #18756.

Before

before-stopwatch

After

after-stopwatch

$this->memory = memory_get_usage(true);
}

/**
* Gets the relative time of the start of the period.
*
* @return int The time (in milliseconds)
* @return float The time (in milliseconds)
*/
public function getStartTime()
Copy link
Member

@keradus keradus Jun 19, 2017

Choose a reason for hiding this comment

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

changing output of this is BC breaker, and for one that like to write in new php in strict mode, it will crash his software.

<?php

declare(strict_types=1);

function NON_STRICTLY_TYPEHINTED_SYMFONY_METHOD___getStartTime_org() { return 123; }

function NON_STRICTLY_TYPEHINTED_SYMFONY_METHOD___getStartTime_new() { return 123.456; }

function USER_FUNCTION_THAT_CONSUMES_SYMFONY_OUTPUT(int $a) {
    echo $a;
}

USER_FUNCTION_THAT_CONSUMES_SYMFONY_OUTPUT(NON_STRICTLY_TYPEHINTED_SYMFONY_METHOD___getStartTime_org());   // OK
USER_FUNCTION_THAT_CONSUMES_SYMFONY_OUTPUT(NON_STRICTLY_TYPEHINTED_SYMFONY_METHOD___getStartTime_new()); // Fatal error: Uncaught TypeError: Argument 1 passed to USER_FUNCTION_THAT_CONSUMES_SYMFONY_OUTPUT() must be of the type integer, float given

@keradus
Copy link
Member

keradus commented Jun 19, 2017

while i do really like the idea, it's bc breaker, as proven

@javiereguiluz
Copy link
Member Author

@keradus it's a BC break ... that's why I submitted to 3.4 branch ... should I submit it instead to 4.0?

@keradus
Copy link
Member

keradus commented Jun 19, 2017

3.4 is for features, not bc breakers, isn't it ?
i would go either with clean solution for 4.0, or future-compat layer on 3.4

@keradus
Copy link
Member

keradus commented Jun 19, 2017

BC breaks? no?

vs

it's a BC break ... that's why ....

@fabpot
Copy link
Member

fabpot commented Jun 19, 2017

We cannot break BC in 4.0 without a migration path from 3.4. So target master would not change anything. What we need to do instead is to add an argument to those methods to get the value as a float and return a casted int if not.

@keradus
Copy link
Member

keradus commented Jun 19, 2017

that's why I requested future-compat layer :)

$this->start = (int) $start;
$this->end = (int) $end;
$this->start = $start;
$this->end = $end;
Copy link
Contributor

Choose a reason for hiding this comment

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

Next to the previous mentioned BC break in the issue, no type cast will also be error prone.

@javiereguiluz
Copy link
Member Author

I've made some changes. Please tell me if I'm going in the right direction. Thanks!

*/
public function __construct($start, $end)
public function __construct($start, $end /*, $useMicroPrecision = false*/)
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to keep this commented, as it's still optional since you're adding a default value. So it won't break BC. (However you could add the bool typehint commented for 4.0)

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, especially for a constructor method.

@fabpot
Copy link
Member

fabpot commented Jun 19, 2017

LGTM but as is, it does not really help as the profiling still won't get the more precise time, right?

@javiereguiluz
Copy link
Member Author

Not sure if this is what was asked for in #18756 ... but the screenshots in the PR are real: now we see details for < 0ms events.

@fabpot
Copy link
Member

fabpot commented Jun 19, 2017

How does it work? Where are you passing true to get sub-millisecond precision? Default is false.

}

$this->start = $morePrecision ? $start : (int) $start;
$this->end = $morePrecision ? $end : (int) $end;
Copy link
Member

Choose a reason for hiding this comment

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

do we care about detecting invalid inputs, eg arrays ?

Copy link
Member

Choose a reason for hiding this comment

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

no

@javiereguiluz
Copy link
Member Author

@fabpot I understand your comment now. I had forgotten to commit the change that enables more precision 😊

@@ -177,7 +177,7 @@ public function getDuration()

for ($i = 0; $i < $left; ++$i) {
$index = $stopped + $i;
$periods[] = new StopwatchPeriod($this->started[$index], $this->getNow());
$periods[] = new StopwatchPeriod($this->started[$index], $this->getNow(), true);
Copy link
Contributor

@ogizanagi ogizanagi Jun 19, 2017

Choose a reason for hiding this comment

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

This also makes the StopwatchEvent::getStartTime(), ::getEndTime() and ::getDuration() return int|float, right?
So I fear it won't be enough to ensure BC :/ You'll need to be able to configure the $morePrecision flag higher.

Or, wouldn't it be possible to simply add the flag to the three methods mentioned above, to either return an int (default) or a float (more precision): public function getStartTime(/*bool $morePrecision = false*/) instead (and always store floats in the private StopwatchPeriod properties)?

Copy link
Member

Choose a reason for hiding this comment

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

adding param to method is BC breaker.
you are thinking more about StopWatchPreciseEvent

Copy link
Contributor

@ogizanagi ogizanagi Jun 20, 2017

Choose a reason for hiding this comment

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

Indeed, except if the new arg is commented until 4.0, and implementations not having it trigger deprecations. StopWatchPreciseEvent could be a solution, but you still need a simple way to say you want to use it, higher in the Stopwatch API, without affecting existing code, as it may impact other consumers.

Copy link
Member

Choose a reason for hiding this comment

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

If I get you right - if whole param would be commented, then the way to retrieve it would be to func_get_args.
Big 👎 from my side to that practices.

Copy link
Contributor

Choose a reason for hiding this comment

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

We already use this "practice" for BC reasons in quite a lot of places in Symfony core.

Copy link
Contributor

Choose a reason for hiding this comment

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

@keradus See all func_get_arg instances in Symfony. This practice is actually pretty novel in terms of providing new APIs without causing BC breaks.

Copy link
Contributor

Choose a reason for hiding this comment

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

And those only are the remaining ones in the master branch.

Copy link
Member

Choose a reason for hiding this comment

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

@keradus Using func_get_args/func_num_args() is the only way to have proper upgrade paths when adding an argument to a method.
They can make the code less readable for a period, but that's for making it better at the end, that is what BC layers are all about.

See e.g.
https://github.com/symfony/symfony/blob/3.3/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php#L61
https://github.com/symfony/symfony/blob/3.3/src/Symfony/Component/HttpFoundation/Request.php#L597
https://github.com/symfony/symfony/blob/3.3/src/Symfony/Component/DependencyInjection/Definition.php#L805
Worst: https://github.com/symfony/symfony/blob/af4703f6f209a9abc387f81b98e00fad46b89a38/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php#L25..#L106

All of these have been removed from master already, and I guess we're all happy about what the changes involving those layers will provide.

*/
public function __construct($start, $end)
public function __construct($start, $end, /* bool */ $morePrecision = false)
Copy link
Member

Choose a reason for hiding this comment

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

bool here is no longer needed

Copy link
Member

Choose a reason for hiding this comment

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

I'm skeptical about this practice of adding commented type hints, especially when it just duplicates the docblock

$this->start = (int) $start;
$this->end = (int) $end;
$this->start = $morePrecision ? $start : (int) $start;
$this->end = $morePrecision ? $end : (int) $end;
Copy link
Member

Choose a reason for hiding this comment

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

missing cast to float IMHO

nicolas-grekas
nicolas-grekas previously approved these changes Jul 3, 2017
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

with minor comments

@nicolas-grekas nicolas-grekas dismissed their stale review July 3, 2017 10:03

too quick

@nicolas-grekas
Copy link
Member

Approval dismissed, sorry :)
I'm not sure we should really care about the "BC break". Let's return float all the time, WDYT?

IF we happen to really want to have the "BC" flag, then the implementation is partial: we cannot have these hardcoded true when instantiating StopwatchPeriod. StopwatchEvent must also have the flag, etc. transitively.

@nicolas-grekas nicolas-grekas added this to the 3.4 milestone Jul 3, 2017
@keradus
Copy link
Member

keradus commented Jul 3, 2017

I'm not sure we should really care about the "BC break". Let's return float all the time, WDYT?

and that's the bc breaker, and I show how it breaks my code. please do respect SEMVER or officially say project is no longer following it.

@robfrawley
Copy link
Contributor

@keradus The "BC break" is limited to use of declare(strict_types=1);, correct? Why can't we just change to float returns for this component for the 4.x release?

@keradus
Copy link
Member

keradus commented Jul 3, 2017

not, it's not only limited to declare(strict_types=1), one could also check the types himself in php5 as well, eg using https://github.com/webmozart/assert

Changing return type in 4.0 is fine, as 4.0 is not yet released and 4.0 is dedicated place for BC breakers.
Changing return type in non-MAJOR release is BC breaker.


http://symfony.com/doc/current/contributing/code/bc.html

Change return type No

@robfrawley
Copy link
Contributor

robfrawley commented Jul 3, 2017

Why would you be making type assertions against external (non-project) code? Anyway, as I said: I think we should change the return type for 4.x and leave as-is for the 3.x releases without any of these precision flags adding complexity to the code.

@stof
Copy link
Member

stof commented Jul 3, 2017

@robfrawley assertion could be on the argument of one of your methods, and you could be using the output of the component as input for your code.

@keradus
Copy link
Member

keradus commented Jul 3, 2017

@robfrawley , exactly as @stof said

@javiereguiluz
Copy link
Member Author

I've updated this PR to follow @ogizanagi's comments.

Copy link
Contributor

@ogizanagi ogizanagi left a comment

Choose a reason for hiding this comment

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

The collectors should be updated to use the new flag when using getters.

@@ -5,3 +5,5 @@ CHANGELOG
-----

* added the `Stopwatch::reset()` method
* allowed to measure sub-millisecond times by introducing a third argument to
Copy link
Contributor

Choose a reason for hiding this comment

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

The changelog entry needs to be updated too :)

@@ -97,7 +97,7 @@ public function stop()
throw new \LogicException('stop() called but start() has not been called before.');
}

$this->periods[] = new StopwatchPeriod(array_pop($this->started), $this->getNow());
$this->periods[] = new StopwatchPeriod(array_pop($this->started), $this->getNow(), true);
Copy link
Contributor

Choose a reason for hiding this comment

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

The third argument is not used anymore (same below)

*/
public function __construct($start, $end)
{
$this->start = (int) $start;
$this->end = (int) $end;
$this->start = $start;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't it be casted to float here to avoid issues with strict types ? (otherwise getters might return an int even with the $morePrecision flag to true)

Copy link
Member

Choose a reason for hiding this comment

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

yep. and currently, passing eg strings will cause odd working of method, while previously they would be converted in constructor already

@javiereguiluz
Copy link
Member Author

After discussing it with @nicolas-grekas, we're proposing a different implementation for this feature.

@ogizanagi
Copy link
Contributor

ogizanagi commented Jul 6, 2017

The issue I see with this new implementation over the flag in getters is that you'll probably update the debug.stopwatch service to use it, making it always return float, while someone might have reused this in it's own classes, with strict types enabled. So that's still a (edge but still) BC break to me.

@fabpot
Copy link
Member

fabpot commented Jul 6, 2017

Thank you @javiereguiluz.

fabpot added a commit that referenced this pull request Jul 6, 2017
This PR was squashed before being merged into the 3.4 branch (closes #23223).

Discussion
----------

Add support for microseconds in Stopwatch

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no?
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #18756
| License       | MIT
| Doc PR        | -

Stopwatch component already supports microseconds (and nanoseconds, etc.) ... but the problem is that it converts the millisecond times to integers, so anything smaller to 1 millisecond is lost. This PR changes that to fix issues like the one explained in #18756.

### Before
![before-stopwatch](https://user-images.githubusercontent.com/73419/27279393-c745db54-54e4-11e7-8f26-01e2063604ce.png)

### After

![after-stopwatch](https://user-images.githubusercontent.com/73419/27279396-c8894dac-54e4-11e7-9a3a-bff027377047.png)

Commits
-------

0db8d7f Add support for microseconds in Stopwatch
@fabpot fabpot closed this Jul 6, 2017
@keradus
Copy link
Member

keradus commented Jul 6, 2017

Great to see this without BC breakers ;)

xabbuh added a commit to symfony/symfony-docs that referenced this pull request Jul 21, 2017
…h (javiereguiluz)

This PR was squashed before being merged into the 3.4 branch (closes #8146).

Discussion
----------

Added a tip about the support of microseconds in Stopwatch

This documents symfony/symfony#23223. See also https://symfony.com/blog/new-in-symfony-3-4-stopwatch-improvements.

Commits
-------

24718a6 Added a tip about the support of microseconds in Stopwatch
This was referenced Oct 18, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants