Minor optimisations to adding hardcoded edges

It only speeds up sorting by about 3% with my 1619 plugin load order,
but I think the logic is simpler despite all the iterators involved.
This commit is contained in:
Oliver Hamlet
2023-01-06 22:20:33 +00:00
parent 232202c17e
commit ef2a34aa23
4 changed files with 95 additions and 43 deletions
+7
View File
@@ -76,6 +76,13 @@ For each plugin:
4. Add edges coming from all the plugin's load after files that are installed
plugins.
Hardcoded edges
---------------
Some games hardcode certain plugins to load in certain positions, and this
section adds edges in the correct order between those plugins, and between those
plugins and the rest of the plugins in the graph.
Group edges
-----------
+52 -33
View File
@@ -636,9 +636,8 @@ void PluginGraph::AddSpecificEdges() {
}
}
void PluginGraph::AddHardcodedPluginEdges(const Game& game) {
using std::filesystem::u8path;
void PluginGraph::AddHardcodedPluginEdges(
const std::vector<std::string>& hardcodedPlugins) {
const auto logger = getLogger();
if (logger) {
logger->trace(
@@ -646,45 +645,65 @@ void PluginGraph::AddHardcodedPluginEdges(const Game& game) {
"positions...");
}
const auto implicitlyActivePlugins =
game.GetLoadOrderHandler().GetImplicitlyActivePlugins();
if (hardcodedPlugins.empty()) {
return;
}
std::set<std::string> processedPluginPaths;
for (const auto& plugin : implicitlyActivePlugins) {
processedPluginPaths.insert(NormalizeFilename(plugin));
std::map<std::vector<std::string>::const_iterator, vertex_t>
implicitlyActivePluginVertices;
std::vector<vertex_t> otherPluginVertices;
if (game.Type() == GameType::tes5 &&
loot::equivalent(game.DataPath() / u8path(plugin),
game.DataPath() / "update.esm")) {
if (logger) {
logger->debug(
"Skipping adding hardcoded plugin edges for Update.esm as it does "
"not have a hardcoded position for Skyrim.");
continue;
// Build the vertex map for implicitly active plugins and record the vertices
// for other plugins.
for (const auto& vertex : boost::make_iterator_range(GetVertices())) {
const auto pluginName = GetPlugin(vertex).GetName();
const auto it =
std::find_if(hardcodedPlugins.begin(),
hardcodedPlugins.end(),
[&](const std::string& name) {
return CompareFilenames(name, pluginName) == 0;
});
if (it != hardcodedPlugins.end()) {
implicitlyActivePluginVertices.emplace(it, vertex);
} else {
otherPluginVertices.push_back(vertex);
}
}
if (implicitlyActivePluginVertices.empty()) {
return;
}
// Now add edges between consecutive implicitly active plugins.
auto lastImplicitlyActiveVertexIt = implicitlyActivePluginVertices.end();
for (auto it = hardcodedPlugins.begin(); it != hardcodedPlugins.end();) {
const auto fromVertexIt = implicitlyActivePluginVertices.find(it);
// Find the next valid implicitly active plugin and its vertex.
auto toVertexIt = implicitlyActivePluginVertices.end();
for (it = std::next(it); it != hardcodedPlugins.end(); ++it) {
toVertexIt = implicitlyActivePluginVertices.find(it);
if (toVertexIt != implicitlyActivePluginVertices.end()) {
break;
}
}
const auto pluginVertex = GetVertexByName(plugin);
if (fromVertexIt != implicitlyActivePluginVertices.end()) {
lastImplicitlyActiveVertexIt = fromVertexIt;
if (!pluginVertex.has_value()) {
if (logger) {
logger->debug(
"Skipping adding hardcoded plugin edges for \"{}\" as it has not "
"been loaded.",
plugin);
if (toVertexIt != implicitlyActivePluginVertices.end()) {
AddEdge(fromVertexIt->second, toVertexIt->second, EdgeType::hardcoded);
}
continue;
}
}
for (const auto& vertex : boost::make_iterator_range(GetVertices())) {
if (vertex == pluginVertex.value()) {
continue;
}
if (processedPluginPaths.count(
NormalizeFilename(GetPlugin(vertex).GetName())) == 0) {
AddEdge(pluginVertex.value(), vertex, EdgeType::hardcoded);
}
// Finally, add edges from the last implicitly active plugin to the other
// plugins.
if (lastImplicitlyActiveVertexIt != implicitlyActivePluginVertices.end()) {
for (const auto& vertex : otherPluginVertices) {
AddEdge(
lastImplicitlyActiveVertexIt->second, vertex, EdgeType::hardcoded);
}
}
}
+2 -1
View File
@@ -91,7 +91,8 @@ public:
void AddVertex(const PluginSortingData& plugin);
void AddSpecificEdges();
void AddHardcodedPluginEdges(const Game& game);
void AddHardcodedPluginEdges(
const std::vector<std::string>& hardcodedPlugins);
void AddGroupEdges(const std::unordered_map<std::string, Group>& groups);
void AddOverlapEdges();
void AddTieBreakEdges();
+34 -9
View File
@@ -24,6 +24,8 @@
#include "plugin_sort.h"
#include <boost/algorithm/string.hpp>
#include "api/helpers/logging.h"
#include "api/sorting/group_sort.h"
#include "api/sorting/plugin_graph.h"
@@ -192,16 +194,30 @@ std::vector<PluginSortingData> GetPluginsSortingData(
return pluginsSortingData;
}
std::vector<std::string> SortPluginGraph(PluginGraph& graph, const Game& game) {
std::vector<std::string> GetPluginsWithHardcodedPositions(const Game& game) {
auto plugins = game.GetLoadOrderHandler().GetImplicitlyActivePlugins();
if (game.Type() == GameType::tes5) {
auto newEndIt = std::remove_if(
plugins.begin(), plugins.end(), [](const std::string& plugin) {
return boost::iequals(plugin, "update.esm");
});
plugins.erase(newEndIt, plugins.end());
}
return plugins;
}
std::vector<std::string> SortPluginGraph(
PluginGraph& graph,
const std::vector<std::string>& hardcodedPlugins,
const std::unordered_map<std::string, Group>& groupsMap) {
// Now add the interactions between plugins to the graph as edges.
graph.AddSpecificEdges();
graph.AddHardcodedPluginEdges(game);
graph.AddHardcodedPluginEdges(hardcodedPlugins);
std::unordered_map<std::string, Group> groups;
for (const auto& group : game.GetDatabase().GetGroups()) {
groups.emplace(group.GetName(), group);
}
graph.AddGroupEdges(groups);
graph.AddGroupEdges(groupsMap);
// Check for cycles now because from this point on edges are only added if
// they don't cause cycles, and adding tie-break edges is by far the slowest
@@ -275,8 +291,17 @@ std::vector<std::string> SortPlugins(
nonMastersGraph.AddVertex(*it);
}
auto newLoadOrder = SortPluginGraph(mastersGraph, game);
const auto newNonMastersLoadOrder = SortPluginGraph(nonMastersGraph, game);
const auto hardcodedPlugins = GetPluginsWithHardcodedPositions(game);
std::unordered_map<std::string, Group> groupsMap;
for (const auto& group : game.GetDatabase().GetGroups()) {
groupsMap.emplace(group.GetName(), group);
}
auto newLoadOrder =
SortPluginGraph(mastersGraph, hardcodedPlugins, groupsMap);
const auto newNonMastersLoadOrder =
SortPluginGraph(nonMastersGraph, hardcodedPlugins, groupsMap);
newLoadOrder.insert(newLoadOrder.end(),
newNonMastersLoadOrder.begin(),