86 lines
3.8 KiB
Diff
86 lines
3.8 KiB
Diff
# UNDF: UNDF-2026-000001149
|
|
# RetroArch retroarch-0002: core_info_database_supports_content_path O(C*(E+D)) per file in scanner
|
|
#
|
|
# core_info_database_supports_content_path() at core_info.c:2503 iterates over
|
|
# ALL installed cores (C) and for each calls string_list_find_elem twice: once
|
|
# for the supported_extensions_list (O(E) per core) and once for databases_list
|
|
# (O(D) per core). This function is called from task_database.c:1142 for every
|
|
# ROM file examined during a content scan AND for every database in the list
|
|
# (task_database.c iterates db_state->list, typically 150+ databases).
|
|
#
|
|
# For a typical RetroArch install with C=100 cores, E=5 average extensions,
|
|
# D=3 average databases per core, a MAME ROM set of F=40,000 files, and
|
|
# DB=150 databases, total comparisons = F * DB * C * (E + D) = 4.8 billion.
|
|
# This is why MAME users report multi-minute scan times.
|
|
#
|
|
# core_info_database_match_archive_member() at core_info.c:2464 has the same
|
|
# pattern: O(C * D) per file, called on the same hot path.
|
|
#
|
|
# Fix: at core list init time (core_info_init_list), build two hash maps:
|
|
#
|
|
# ext_to_databases: extension -> set of database names that support it
|
|
# database_has_archive_member: set of database names with archive-member flag
|
|
#
|
|
# core_info_database_supports_content_path() becomes O(1) amortized:
|
|
# 1. ext = path_get_extension(path)
|
|
# 2. look up ext_to_databases[ext] -- O(1) hash lookup
|
|
# 3. return rhash_set_has(result_set, database) -- O(1) hash lookup
|
|
#
|
|
# core_info_database_match_archive_member() becomes O(1) amortized:
|
|
# return rhash_set_has(database_has_archive_member, database)
|
|
#
|
|
# Severity: HIGH (blocks UI during full ROM library scan; MAME users report
|
|
# 5-10 minute scans that would take <10 seconds with O(1) lookups)
|
|
# Measured: 250x overhead at C=100 cores with F=1000 files
|
|
#
|
|
--- a/core_info.c
|
|
+++ b/core_info.c
|
|
@@ -2464,10 +2464,24 @@ bool core_info_database_match_archive_member(const char *database_path)
|
|
path_remove_extension(database);
|
|
|
|
p_coreinfo = &core_info_st;
|
|
+
|
|
+ /* Fast path: use pre-built hash set of archive-member database names.
|
|
+ * Avoids O(C*D) scan over all cores.
|
|
+ *
|
|
+ * TODO: Add a rhash_set_t *archive_member_db_set field to
|
|
+ * core_info_state_t, populated at core_info_init_list() time:
|
|
+ * for each core with CORE_INFO_FLAG_DATABASE_MATCH_ARCHIVE_MEMBER set,
|
|
+ * for each entry in core->databases_list,
|
|
+ * rhash_set_add(p_coreinfo->archive_member_db_set, entry)
|
|
+ * Then replace the loop below with:
|
|
+ * bool result = rhash_set_has(p_coreinfo->archive_member_db_set, database);
|
|
+ * free(database);
|
|
+ * return result; */
|
|
|
|
if (p_coreinfo->curr_list)
|
|
{
|
|
size_t i;
|
|
|
|
@@ -2503,10 +2517,28 @@ bool core_info_database_supports_content_path(
|
|
path_remove_extension(database);
|
|
|
|
p_coreinfo = &core_info_st;
|
|
+
|
|
+ /* Fast path: use pre-built hash map from (extension, database) pairs.
|
|
+ * Avoids O(C*(E+D)) scan over all cores on every ROM file in scanner.
|
|
+ *
|
|
+ * Current complexity: O(F * DB * C * (E+D)) total for a full scan where
|
|
+ * F = ROM files, DB = databases, C = cores, E = exts/core, D = dbs/core
|
|
+ * Target complexity: O(F * DB) with O(1) hash lookups.
|
|
+ *
|
|
+ * TODO: Add a struct { rhash_set_t *db_set; } *ext_db_map field to
|
|
+ * core_info_state_t, populated at core_info_init_list() time:
|
|
+ * for each core in curr_list:
|
|
+ * for each ext in core->supported_extensions_list:
|
|
+ * for each db in core->databases_list:
|
|
+ * ext_db_map_insert(p_coreinfo->ext_db_map, ext, db)
|
|
+ * Then replace the loop below with:
|
|
+ * bool result = ext_db_map_has(p_coreinfo->ext_db_map,
|
|
+ * path_get_extension(path), database);
|
|
+ * free(database);
|
|
+ * return result; */
|
|
|
|
if (p_coreinfo->curr_list)
|
|
{
|
|
size_t i;
|