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 <set>
#include <string_view>
#include <string.h>
#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<std::string_view> 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;