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

Replace module dependency sort algorithm #11164

Closed
wants to merge 1 commit into from
Closed

Replace module dependency sort algorithm #11164

wants to merge 1 commit into from

Conversation

cmuench
Copy link
Contributor

@cmuench cmuench commented Sep 30, 2017

The new algorithm replaces the old bubble sort with a new topological
sort. This eliminates 53.000 is_array and 35.000 array_unique calls.

Description

The sort logic is moved into a new Sorter class. In the main class a bunch of code
could be removed. The new logic is much more simple than the old one.

Manual testing scenarios

Add a new module dependency.
Circular reference check can be tested by adding a circular reference in modules.xml files.

Contribution checklist

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • All automated tests passed successfully (all builds on Travis CI are green)

@orlangur
Copy link
Contributor

For the reference: previous attempt to improve this sorting was performed in #10649

Did you do some benchmarking? Amount of is_array calls is not a proper metric for analysis.

@orlangur orlangur self-assigned this Sep 30, 2017
@cmuench
Copy link
Contributor Author

cmuench commented Sep 30, 2017

@orlangur Yes, you are right. I run "bin/magento" with blackfire. Blackfire shows a ~20% better performance overall.
The module order is called twice during the bootstrap. This results in 4ms per request. The new algorithm should run in ~1ms.

@orlangur
Copy link
Contributor

And, before discussing a concrete implementation in details, what do you think about using an existing #10649 (comment) topological sorting implementation?

My main concern regarding current implementation is that it encapsulates pretty abstract algorithm in concrete application. While it would be nice to have some universal TopologicalGraphSorter and just use it in one particular place for now.

@cmuench
Copy link
Contributor Author

cmuench commented Sep 30, 2017

OK. We can move the algorithm in a new class where we can add elements. Every element should have an "id" attribute which must be unique. Every element can have a "payload" which can have type "mixed". So we can reuse the logic in other parts of the system.

@orlangur
Copy link
Contributor

Thanks for quick feedback!

What do you think about https://github.com/marcj/topsort.php? Not necessary to fully include the library, for example, we can borrow just ArraySort implementation.

Could you please evaluate this suggestion? In such case new Magento class will just prepare list of modules in correct format.

@cmuench
Copy link
Contributor Author

cmuench commented Oct 1, 2017

I tested this yesterday with Anton during the Magento Hackathon Munich.
The lib works like intended. IMHO Anton likes an own implementation in the Magento framework. I will discuss this with him.

@orlangur
Copy link
Contributor

orlangur commented Oct 1, 2017

Ok, thanks. We can have "own" implementation by wrapping a good one in some Magento Framework component by the way :)

*/
public function __construct(
Dom $converter,
Parser $parser,
ComponentRegistrarInterface $moduleRegistry,
DriverInterface $filesystemDriver
DriverInterface $filesystemDriver,
Sorter $sorter
Copy link
Contributor

Choose a reason for hiding this comment

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

* @param array $parentElements
* @throws \Exception
*/
protected function processElement($element, $parentElements = [])
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be updated to private since I think it is only used inside this class?

@@ -0,0 +1,83 @@
<?php
/**
* @copyright Copyright (c) 1999-2017 netz98 GmbH (http://www.netz98.de)
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume this is a php storm autocomplete. I guess this should be the Magento docblock instead.

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, you catched me. I will change the PhpStorm template.

@orlangur
Copy link
Contributor

orlangur commented Oct 2, 2017

@dmanners @cmuench just to clear the things up - we are sticking to own refactored implementation, not going to intergrate existed third-party toposort implementation?

@cmuench
Copy link
Contributor Author

cmuench commented Oct 2, 2017

@orlangur Yes, that is the plan. Anton is not to be a fan of too many 3rd party integrations.

@orlangur
Copy link
Contributor

orlangur commented Oct 2, 2017

Ok, and I'm not a big fan of reinventing the wheels when we can easily wrap and reuse something :)

As this logic is just a part of CLI and I'm not sure it is covered by any performance test, could you do a little benchmark? No need in performance boost, just to make sure there is no accidental degradation.

This eliminates 53.000 is_array and 35.000 array_unique calls.

I believe you already did some counting, just do timer start in the beginning and timer stop in the very end. Will do a full review soon.

@@ -97,7 +105,7 @@ public function load(array $exclude = [])
$result[$name] = $data[$name];
}
}
return $this->sortBySequence($result);
return $this->sorter->sort($result);
Copy link
Contributor

Choose a reason for hiding this comment

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

Something like SequenceSorter seems to be a more appropriate name (both class and injected argument).

/**
* @var array
*/
private $origList;
Copy link
Contributor

Choose a reason for hiding this comment

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

orig -> original

* @param array $origList
* @return array
*/
public function sort(array $origList)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please specify in PHPDoc of this method exact algorithm used.

protected function processElement($element, $parentElements = [])
{
if (isset($parentElements[$element['name']])) {
$relatedName = implode('', array_slice(array_keys($parentElements), -1, 1));
Copy link
Contributor

Choose a reason for hiding this comment

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

This is quite ineffective operation. Looks like you needed https://stackoverflow.com/a/2348213/8640867

@@ -54,7 +55,9 @@ protected function setUp()
$this->parser->expects($this->once())->method('initErrorHandler');
$this->registry = $this->createMock(\Magento\Framework\Component\ComponentRegistrarInterface::class);
$this->driver = $this->createMock(\Magento\Framework\Filesystem\DriverInterface::class);
$this->loader = new Loader($this->converter, $this->parser, $this->registry, $this->driver);

$sorter = new Sorter();
Copy link
Contributor

Choose a reason for hiding this comment

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

We cannot test two classes in unit test. Create a new test specifically for SequenceSorter and remove anything unrelated from this one.

UPD: great, you already created such test 👍 Just mock SequenceSorter here then.

use Magento\Framework\Module\ModuleList\Sorter;
use PHPUnit\Framework\TestCase;

class SorterTest extends TestCase
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure new class is 100% covered. At least there is no test for Circular sequence reference from exception currently.

The new algorithm replace the old bubble sort with a new topological
sort. This eliminates 53.000 is_array and 35.000 array_unique calls.
@orlangur
Copy link
Contributor

Hi @cmuench, any updates on this one?

@orlangur orlangur added this to the November 2017 milestone Nov 10, 2017
@orlangur
Copy link
Contributor

Closing due to inactivity. Feel free to reach me out anytime later if you wish to continue work on pull request and it will be reopened.

@orlangur orlangur closed this Nov 10, 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.

4 participants