db/simple/DirectorySave: optimize duplicate checks with std::set

This reduces the CPU usage for loading a large database by more than 50%.
This commit is contained in:
Max Kellermann 2023-09-06 14:44:42 +02:00
parent 2c4ef4460f
commit c96e8ab47c
1 changed files with 16 additions and 8 deletions

View File

@ -17,6 +17,9 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <set>
#include <string_view>
#include <string.h> #include <string.h>
#define DIRECTORY_DIR "directory: " #define DIRECTORY_DIR "directory: "
@ -109,9 +112,6 @@ ParseLine(Directory &directory, const char *line)
static Directory * static Directory *
directory_load_subdir(LineReader &file, Directory &parent, std::string_view name) 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); Directory *directory = parent.CreateChild(name);
try { try {
@ -139,20 +139,24 @@ directory_load_subdir(LineReader &file, Directory &parent, std::string_view name
void void
directory_load(LineReader &file, Directory &directory) directory_load(LineReader &file, Directory &directory)
{ {
/* these sets are used to quickly check for duplicates,
avoiding linear lookups */
std::set<std::string_view> children, songs;
const char *line; const char *line;
while ((line = file.ReadLine()) != nullptr && while ((line = file.ReadLine()) != nullptr &&
!StringStartsWith(line, DIRECTORY_END)) { !StringStartsWith(line, DIRECTORY_END)) {
const char *p; const char *p;
if ((p = StringAfterPrefix(line, DIRECTORY_DIR))) { 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))) { } else if ((p = StringAfterPrefix(line, SONG_BEGIN))) {
const char *name = p; const char *name = p;
if (directory.FindSong(name) != nullptr)
throw FmtRuntimeError("Duplicate song '{}'",
name);
std::string target; std::string target;
bool in_playlist = false; bool in_playlist = false;
auto detached_song = song_load(file, name, auto detached_song = song_load(file, name,
@ -163,6 +167,10 @@ directory_load(LineReader &file, Directory &directory)
song->target = std::move(target); song->target = std::move(target);
song->in_playlist = in_playlist; song->in_playlist = in_playlist;
if (!songs.emplace(song->filename).second)
throw FmtRuntimeError("Duplicate song '{}'",
name);
directory.AddSong(std::move(song)); directory.AddSong(std::move(song));
} else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) { } else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) {
const char *name = p; const char *name = p;