From 98c1cf92d20fd4a82deac25fde168aa77566ace4 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Thu, 25 Jun 2015 13:59:24 +0100 Subject: [PATCH] Multithread plugin loading more effectively. Using hardware concurrency instead of the pretty arbitrary mean file size criterion previously used. While this coincidently doesn't have much effect on my computer (4 threads now vs. 5 before), it should help performance for users with CPUs that have different hardware concurrency support (eg. non-hyperthreaded dual-cores, hyperthreaded quad-cores). --- src/backend/game/game.cpp | 79 ++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/src/backend/game/game.cpp b/src/backend/game/game.cpp index d5e518a6..df647f24 100644 --- a/src/backend/game/game.cpp +++ b/src/backend/game/game.cpp @@ -123,64 +123,57 @@ namespace loot { } void Game::LoadPlugins(bool headersOnly) { - boost::thread_group group; - uintmax_t meanFileSize = 0; - unordered_map tempMap; - std::vector groupPlugins; - //First calculate the mean plugin size. Store it temporarily in a map to reduce filesystem lookups and file size recalculation. + // First find out how many plugins there are. BOOST_LOG_TRIVIAL(trace) << "Scanning for plugins in " << this->DataPath(); for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) { if (fs::is_regular_file(it->status()) && Plugin(it->path().filename().string()).IsValid(*this)) { - uintmax_t fileSize = fs::file_size(it->path()); - meanFileSize += fileSize; + Plugin temp(it->path().filename().string()); + BOOST_LOG_TRIVIAL(info) << "Found plugin: " << temp.Name(); - tempMap.insert(pair(it->path().filename().string(), fileSize)); + //Insert the lowercased name as a key for case-insensitive matching. + plugins.insert(pair(boost::locale::to_lower(temp.Name()), temp)); } } - meanFileSize /= tempMap.size(); //Rounding error, but not important. - //Now load plugins. - for (const auto &pluginPair : tempMap) { - BOOST_LOG_TRIVIAL(info) << "Found plugin: " << pluginPair.first; + // Get the number of threads to use. + // hardware_concurrency() may be zero, if so then use only one thread. + unsigned int threadsToUse = std::min(boost::thread::hardware_concurrency(), plugins.size()); + threadsToUse = std::max(threadsToUse, (unsigned)1); - //Insert the lowercased name as a key for case-insensitive matching. - Plugin temp(pluginPair.first); - auto plugin = plugins.insert(pair(boost::locale::to_lower(temp.Name()), temp)); + // Divide the plugins up by thread. + unsigned int pluginsPerThread = ceil((double)plugins.size() / threadsToUse); + BOOST_LOG_TRIVIAL(info) << "Loading " << plugins.size() << " plugins using " << threadsToUse << " threads, with up to " << pluginsPerThread << " plugins per thread."; - if (pluginPair.second > meanFileSize) { - BOOST_LOG_TRIVIAL(trace) << "Creating individual loading thread for: " << pluginPair.first; - group.create_thread([this, plugin, headersOnly]() { - BOOST_LOG_TRIVIAL(trace) << "Loading " << plugin.first->second.Name() << " individually."; + std::vector::iterator>> pluginGroups(4); + size_t pluginGroup = 0; + for (auto it = plugins.begin(); it != plugins.end(); ++it) { + if (pluginGroups[pluginGroup].size() == pluginsPerThread) { + ++pluginGroup; + } + BOOST_LOG_TRIVIAL(trace) << "Adding plugin " << it->second.Name() << " to loading group " << pluginGroup; + pluginGroups[pluginGroup].push_back(it); + } + + // Load the plugins. + BOOST_LOG_TRIVIAL(trace) << "Starting plugin loading."; + boost::thread_group group; + while (group.size() < threadsToUse) { + std::vector::iterator>& pluginGroup = pluginGroups[group.size()]; + group.create_thread([this, &pluginGroup, headersOnly]() { + for (auto it : pluginGroup) { + BOOST_LOG_TRIVIAL(trace) << "Loading " << it->second.Name(); try { - plugin.first->second = Plugin(*this, plugin.first->second.Name(), headersOnly); + it->second = Plugin(*this, it->second.Name(), headersOnly); } catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << plugin.first->second.Name() << ": Exception occurred: " << e.what(); - Plugin p; + BOOST_LOG_TRIVIAL(error) << it->second.Name() << ": Exception occurred: " << e.what(); + Plugin p(it->second.Name()); p.Messages(list(1, Message(Message::error, lc::translate("An exception occurred while loading this plugin. Details: ").str() + " " + e.what()))); - plugin.first->second = p; + it->second = p; } - }); - } - else { - groupPlugins.push_back(&plugin.first->second); - } + } + }); } - group.create_thread([this, &groupPlugins, headersOnly]() { - for (auto plugin : groupPlugins) { - BOOST_LOG_TRIVIAL(trace) << "Loading " << plugin->Name() << " as part of a group."; - try { - *plugin = Plugin(*this, plugin->Name(), headersOnly); - } - catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << plugin->Name() << ": Exception occurred: " << e.what(); - Plugin p; - p.Messages(list(1, Message(Message::error, lc::translate("An exception occurred while loading this plugin. Details:").str() + " " + e.what()))); - *plugin = p; - } - } - }); - group.join_all(); }