From c0ed82533cf8f48c79c4efe86988c81c930b46a1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 9 Sep 2021 00:22:29 -0500 Subject: [PATCH] perf: lookup tables for faster torrent searching. (#1778) * perf: lookup tables for faster torrent searching. tr_torrentFindFromId(), tr_torrentFindFromHashString(), and tr_torrentFindFromHash() are now O(log N) instead of O(N) where N is the number of torrents. * build: fix clang-tidy warning error: integer to pointer cast pessimizes optimization opportunities [performance-no-int-to-ptr,-warnings-as-errors] from TR_BAD_SYS_FILE which expands to INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) --- libtransmission/session.c | 72 ++++++++++++++++++++++++ libtransmission/session.h | 7 +++ libtransmission/torrent.c | 92 ++++++++----------------------- tests/libtransmission/.clang-tidy | 1 + 4 files changed, 104 insertions(+), 68 deletions(-) diff --git a/libtransmission/session.c b/libtransmission/session.c index b9739a59e..5eb0d8be0 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -43,6 +43,7 @@ #include "platform.h" /* tr_lock, tr_getTorrentDir() */ #include "platform-quota.h" /* tr_device_info_free() */ #include "port-forwarding.h" +#include "ptrarray.h" #include "rpc-server.h" #include "session.h" #include "session-id.h" @@ -635,6 +636,9 @@ tr_session* tr_sessionInit(char const* configDir, bool messageQueuingEnabled, tr session->cache = tr_cacheNew(1024 * 1024 * 2); session->magicNumber = SESSION_MAGIC_NUMBER; session->session_id = tr_session_id_new(); + session->torrentsSortedByHash = TR_PTR_ARRAY_INIT; + session->torrentsSortedByHashString = TR_PTR_ARRAY_INIT; + session->torrentsSortedById = TR_PTR_ARRAY_INIT; tr_bandwidthConstruct(&session->bandwidth, session, NULL); tr_variantInitList(&session->removedTorrents, 0); @@ -2137,6 +2141,9 @@ void tr_sessionClose(tr_session* session) } tr_device_info_free(session->downloadDir); + tr_ptrArrayDestruct(&session->torrentsSortedByHash, NULL); + tr_ptrArrayDestruct(&session->torrentsSortedByHashString, NULL); + tr_ptrArrayDestruct(&session->torrentsSortedById, NULL); tr_free(session->torrentDoneScript); tr_free(session->configDir); tr_free(session->resumeDir); @@ -3117,3 +3124,68 @@ int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction dir) return max - active_count; } + +static int compareTorrentsById(void const* va, void const* vb) +{ + tr_torrent const* const a = va; + tr_torrent const* const b = vb; + return a->uniqueId - b->uniqueId; +} + +static int compareTorrentsByHashString(void const* va, void const* vb) +{ + tr_torrent const* const a = va; + tr_torrent const* const b = vb; + return evutil_ascii_strcasecmp(a->info.hashString, b->info.hashString); +} + +static int compareTorrentsByHash(void const* va, void const* vb) +{ + tr_torrent const* const a = va; + tr_torrent const* const b = vb; + return memcmp(a->info.hash, b->info.hash, SHA_DIGEST_LENGTH); +} + +void tr_sessionAddTorrent(tr_session* session, tr_torrent* tor) +{ + /* add tor to tr_session.torrentList */ + tor->next = session->torrentList; + session->torrentList = tor; + + /* add tor to tr_session.torrentsSortedByFoo */ + tr_ptrArrayInsertSorted(&session->torrentsSortedById, tor, compareTorrentsById); + tr_ptrArrayInsertSorted(&session->torrentsSortedByHashString, tor, compareTorrentsByHashString); + tr_ptrArrayInsertSorted(&session->torrentsSortedByHash, tor, compareTorrentsByHash); + + /* increment the torrent count */ + ++session->torrentCount; +} + +void tr_sessionRemoveTorrent(tr_session* session, tr_torrent* tor) +{ + /* remove tor from tr_session.torrentList */ + if (tor == session->torrentList) + { + session->torrentList = tor->next; + } + else + { + for (tr_torrent* t = session->torrentList; t != NULL; t = t->next) + { + if (t->next == tor) + { + t->next = tor->next; + break; + } + } + } + + /* remove tor from tr_session.torrentsSortedByFoo */ + tr_ptrArrayRemoveSortedPointer(&session->torrentsSortedById, tor, compareTorrentsById); + tr_ptrArrayRemoveSortedPointer(&session->torrentsSortedByHashString, tor, compareTorrentsByHashString); + tr_ptrArrayRemoveSortedPointer(&session->torrentsSortedByHash, tor, compareTorrentsByHash); + + /* decrement the torrent count */ + TR_ASSERT(session->torrentCount >= 1); + session->torrentCount--; +} diff --git a/libtransmission/session.h b/libtransmission/session.h index 97636454d..723fd726d 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -17,6 +17,7 @@ #include "bandwidth.h" #include "bitfield.h" #include "net.h" +#include "ptrarray.h" #include "tr-macros.h" #include "utils.h" #include "variant.h" @@ -177,6 +178,9 @@ struct tr_session int torrentCount; tr_torrent* torrentList; + tr_ptrArray torrentsSortedByHash; + tr_ptrArray torrentsSortedByHashString; + tr_ptrArray torrentsSortedById; char* torrentDoneScript; @@ -322,4 +326,7 @@ void tr_sessionGetNextQueuedTorrents(tr_session* session, tr_direction dir, size int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction); +void tr_sessionAddTorrent(tr_session* session, tr_torrent* tor); +void tr_sessionRemoveTorrent(tr_session* session, tr_torrent* tor); + TR_END_DECLS diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index ea8c5d372..99656609b 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -73,49 +73,40 @@ int tr_torrentId(tr_torrent const* tor) return tor != NULL ? tor->uniqueId : -1; } +static int compareKeyToTorrentId(void const* va, void const* vb) +{ + tr_torrent const* const a = va; + int const b = *(int const*)vb; + return a->uniqueId - b; +} + tr_torrent* tr_torrentFindFromId(tr_session* session, int id) { - tr_torrent* tor = NULL; + return tr_ptrArrayFindSorted(&session->torrentsSortedById, &id, compareKeyToTorrentId); +} - while ((tor = tr_torrentNext(session, tor)) != NULL) - { - if (tor->uniqueId == id) - { - return tor; - } - } - - return NULL; +static int compareKeyToTorrentHashString(void const* va, void const* vb) +{ + tr_torrent const* const a = va; + char const* const b = vb; + return evutil_ascii_strcasecmp(a->info.hashString, b); } tr_torrent* tr_torrentFindFromHashString(tr_session* session, char const* str) { - tr_torrent* tor = NULL; + return tr_ptrArrayFindSorted(&session->torrentsSortedByHashString, str, compareKeyToTorrentHashString); +} - while ((tor = tr_torrentNext(session, tor)) != NULL) - { - if (evutil_ascii_strcasecmp(str, tor->info.hashString) == 0) - { - return tor; - } - } - - return NULL; +static int compareKeyToTorrentHash(void const* va, void const* vb) +{ + tr_torrent const* const a = va; + uint8_t const* const b = vb; + return memcmp(a->info.hash, b, SHA_DIGEST_LENGTH); } tr_torrent* tr_torrentFindFromHash(tr_session* session, uint8_t const* torrentHash) { - tr_torrent* tor = NULL; - - while ((tor = tr_torrentNext(session, tor)) != NULL) - { - if ((*tor->info.hash == *torrentHash) && (memcmp(tor->info.hash, torrentHash, SHA_DIGEST_LENGTH) == 0)) - { - return tor; - } - } - - return NULL; + return tr_ptrArrayFindSorted(&session->torrentsSortedByHash, torrentHash, compareKeyToTorrentHash); } tr_torrent* tr_torrentFindFromMagnetLink(tr_session* session, char const* magnet) @@ -975,24 +966,7 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor) tr_torrentSetIdleLimit(tor, tr_sessionGetIdleLimit(tor->session)); } - /* add the torrent to tr_session.torrentList */ - session->torrentCount++; - - if (session->torrentList == NULL) - { - session->torrentList = tor; - } - else - { - tr_torrent* it = session->torrentList; - - while (it->next != NULL) - { - it = it->next; - } - - it->next = tor; - } + tr_sessionAddTorrent(session, tor); /* if we don't have a local .torrent file already, assume the torrent is new */ isNewTorrent = !tr_sys_path_exists(tor->info.torrent, NULL); @@ -1689,25 +1663,7 @@ static void freeTorrent(tr_torrent* tor) tr_free(tor->downloadDir); tr_free(tor->incompleteDir); - if (tor == session->torrentList) - { - session->torrentList = tor->next; - } - else - { - for (tr_torrent* t = session->torrentList; t != NULL; t = t->next) - { - if (t->next == tor) - { - t->next = tor->next; - break; - } - } - } - - /* decrement the torrent count */ - TR_ASSERT(session->torrentCount >= 1); - session->torrentCount--; + tr_sessionRemoveTorrent(session, tor); /* resequence the queue positions */ tr_torrent* t = NULL; diff --git a/tests/libtransmission/.clang-tidy b/tests/libtransmission/.clang-tidy index f46690e30..4305b9814 100644 --- a/tests/libtransmission/.clang-tidy +++ b/tests/libtransmission/.clang-tidy @@ -50,6 +50,7 @@ Checks: > -modernize-concat-nested-namespaces, -modernize-use-trailing-return-type, performance-*, + -performance-no-int-to-ptr, readability-*, -readability-convert-member-functions-to-static, -readability-function-cognitive-complexity,