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

Run PhpCsFixer tasks asynchronously #210

Merged
merged 2 commits into from
Oct 25, 2016
Merged

Run PhpCsFixer tasks asynchronously #210

merged 2 commits into from
Oct 25, 2016

Conversation

jyggen
Copy link
Contributor

@jyggen jyggen commented Oct 20, 2016

Q A
Branch master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Documented? yes
Fixed tickets -

PhpCsFixer takes quite some time when running it on changed files, and this is of course because GrumPHP needs to run one process per file. Symfony's Process component supports running processes asynchronously, so I thought why not give it a spin!

The following benchmark is performed on 23 files with PhpCsFixerV2 as the only task. The former is GrumPHP 0.9.6 and the latter is with this PR. From 10s to 2s is pretty sweet!

$ time ./vendor/bin/grumphp run
GrumPHP is sniffing your code!
Running task 1/1: PhpCsFixerV2

real    0m10.478s
user    0m1.734s
sys     0m8.297s
$ time ./vendor/bin/grumphp run
GrumPHP is sniffing your code!
Running task 1/1: PhpCsFixerV2

real    0m2.350s
user    0m1.563s
sys     0m7.078s

The array_chunk() and extra foreach loop is there because I don't like the idea of potentially kicking off hundreds of php-cs-fixer processes at the same time. Right now the size of each chuck is hardcoded to ten, maybe something worth making configurable?

@veewee
Copy link
Contributor

veewee commented Oct 21, 2016

Great idea!

Can you move the logic of the chunked process runner into a separate service named ParallelProcessRunner or something like that?
This way we can reuse the logic in other tasks when we need to.
Lets also try to keep 1 level of indentation in the classes.

Currently the processes are running in chunks of 10 which means that it waits untill every task is finished before it starts it's next chunk. It would be really nice if it would start a new task once an old task finished with a maximum of 10 tasks. Would that be possible?

@veewee veewee added this to the Version 0.9.7 milestone Oct 22, 2016
@jyggen
Copy link
Contributor Author

jyggen commented Oct 22, 2016

I've pushed a refactor that uses a new AsyncProcessRunner. Let me know what you think.

$this->running = 0;

while ($this->watchProcesses()) {
usleep(1000);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this setting could also be configurable?

unset($this->processes[$key]);
}

$shouldRunAgain = max($shouldRunAgain, !$isTerminated);
Copy link
Contributor

Choose a reason for hiding this comment

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

why are you using the max() method on booleans? This is just a regular or isn't it?

{
$isTerminated = false;

if (!$process->isStarted() && $this->running < $this->config->getProcessAsyncLimit()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Try to avoid else / elseif's.
Just invert the if and return early.
We're trying to folow the object calisthenics rules.

*
* @package GrumPHP\Process
*/
class AsyncProcessRunner
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add some spec tests to make sure this class works as expected?

$shouldRunAgain = max($shouldRunAgain, !$isTerminated);
}

return $shouldRunAgain;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't the logic for $shouldRunAgain be simplified in count($this->processes) !== 0

Copy link
Contributor

@veewee veewee left a comment

Choose a reason for hiding this comment

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

I've added some remarks to the code.
It looks pretty good, but I think it can be simplified at some points.
Also: It cannot be merged if there are no tests indicating that the new async runner is working correctly.

The CI tools are also failing.

@jyggen
Copy link
Contributor Author

jyggen commented Oct 24, 2016

Refactored it a bit and added some tests 👍

Copy link
Contributor

@veewee veewee left a comment

Choose a reason for hiding this comment

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

I've taken another look at the code and added some minor change requests. It looks pretty powerfull :)

@@ -73,6 +73,22 @@ public function ignoreUnstagedChanges()
}

/**
* @return int
*/
public function getProcessAsyncLimit()
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you ass a spec for this method?

/**
* @return int
*/
public function getProcessAsyncWaitTime()
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you ass a spec for this method?

public function let(GrumPHP $grumPHP) {
$this->beConstructedWith($grumPHP);

$grumPHP->getProcessAsyncWaitTime()->willReturn(1000);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you can set this value to 0 to make sure the tests aren't slowed down by the usleep() call?

/**
* @mixin AsyncProcessRunner
*/
class AsyncProcessRunnerSpec extends ObjectBehavior
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it's a good idea to test the async process limit part as well?

@jyggen
Copy link
Contributor Author

jyggen commented Oct 24, 2016

Done and done!

@veewee veewee merged commit 2fa5c57 into phpro:master Oct 25, 2016
@veewee
Copy link
Contributor

veewee commented Oct 25, 2016

Thanks for your work!

@jyggen jyggen deleted the feature/asynchronous-phpcsfixer branch November 12, 2016 14:24
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.

2 participants