From c96e8ab47c1dc59ad2934692b2fa2ff84c6c59c7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 6 Sep 2023 14:44:42 +0200 Subject: [PATCH] db/simple/DirectorySave: optimize duplicate checks with std::set This reduces the CPU usage for loading a large database by more than 50%. --- src/db/plugins/simple/DirectorySave.cxx | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/db/plugins/simple/DirectorySave.cxx b/src/db/plugins/simple/DirectorySave.cxx index 010957e9d..8a3410c01 100644 --- a/src/db/plugins/simple/DirectorySave.cxx +++ b/src/db/plugins/simple/DirectorySave.cxx @@ -17,6 +17,9 @@ #include +#include +#include + #include #define DIRECTORY_DIR "directory: " @@ -109,9 +112,6 @@ ParseLine(Directory &directory, const char *line) static Directory * directory_load_subdir(LineReader &file, Directory &parent, std::string_view name) { - if (parent.FindChild(name) != nullptr) - throw FmtRuntimeError("Duplicate subdirectory '{}'", name); - Directory *directory = parent.CreateChild(name); try { @@ -139,20 +139,24 @@ directory_load_subdir(LineReader &file, Directory &parent, std::string_view name void directory_load(LineReader &file, Directory &directory) { + /* these sets are used to quickly check for duplicates, + avoiding linear lookups */ + std::set children, songs; + const char *line; while ((line = file.ReadLine()) != nullptr && !StringStartsWith(line, DIRECTORY_END)) { const char *p; if ((p = StringAfterPrefix(line, DIRECTORY_DIR))) { - directory_load_subdir(file, directory, p); + auto *child = directory_load_subdir(file, directory, p); + + const std::string_view name = child->GetName(); + if (!children.emplace(name).second) + throw FmtRuntimeError("Duplicate subdirectory '{}'", name); } else if ((p = StringAfterPrefix(line, SONG_BEGIN))) { const char *name = p; - if (directory.FindSong(name) != nullptr) - throw FmtRuntimeError("Duplicate song '{}'", - name); - std::string target; bool in_playlist = false; auto detached_song = song_load(file, name, @@ -163,6 +167,10 @@ directory_load(LineReader &file, Directory &directory) song->target = std::move(target); song->in_playlist = in_playlist; + if (!songs.emplace(song->filename).second) + throw FmtRuntimeError("Duplicate song '{}'", + name); + directory.AddSong(std::move(song)); } else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) { const char *name = p;