Мне кажется, что вы можете избавиться от большей части поиска вершин, отслеживая конечные узлы, когда добавляете их в граф и обрабатываете список узлов.
if( @dependencies ) {
for my $dependency (@dependencies) {
unless ($g->has_vertex($dependency)) {
$g->add_vertex($dependency);
}
$g->add_edge($component, $dependency);
}
}
else {
push @end_components, $component;
}
Затем, когда вы обрабатываете свой набор данных, запускаете один поток выполнения (однако вы реализуете свой параллелизм) для каждого конечного узла. Когда поток завершается, любые родительские узлы без других преемников добавляются в список конечных узлов. Продолжайте обрабатывать список конечных узлов, пока график и список узлов не станут пустыми.
sub run_in_parallel {
my $g = shift->copy;
my $end_vertices = shift;
my @queue;
while( $g->vertices ) {
print "running ", join(", ", @queue), " in parallel\n";
for my $compenent (@queue) {
# When process finished.
$g->delete_vertex($component);
push @queue, grep { $g->is_successorless_vertex($_) } $g->predecessors($v);
}
# Add some error check for non-empty graph + empty queue + no processes waiting to finish.
}
}
Это изменение добавляет немного ведения записей и немного более хрупко, чем ваше первоначальное представление. Попробуйте инкапсулировать эти поведения в объекте, который содержит или наследует от Graph
.
Наибольшим выигрышем в производительности будут большие графы с глубокими зависимостями.
Минимальное улучшение будет наблюдаться с небольшими графиками (где затраты на поиск незначительны по сравнению со стоимостью управления процессами), или где графики очень мелкие.
Этот код полностью не проверен.