From 6d93820499a8bfb19128759893b18c1437f99c6b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Oct 2018 15:04:45 -0400 Subject: [PATCH] Memoize summarize_protover_flags() Our tests showed that this function is responsible for a huge number of our malloc/free() calls. It's a prime candidate for being memoized. Closes ticket 27225. --- changes/ticket27225 | 5 +++ src/app/main/main.c | 2 ++ src/core/or/versions.c | 82 ++++++++++++++++++++++++++++++++++-------- src/core/or/versions.h | 2 ++ 4 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 changes/ticket27225 diff --git a/changes/ticket27225 b/changes/ticket27225 new file mode 100644 index 0000000000..4c05a269d6 --- /dev/null +++ b/changes/ticket27225 @@ -0,0 +1,5 @@ + o Minor features (performance): + - Avoid parsing the same protocol-versions string over and over + in summarize_protover_flags(). This should save us a huge number + of malloc calls on startup, and may reduce memory fragmentation with + some allocators. Closes ticket 27225. diff --git a/src/app/main/main.c b/src/app/main/main.c index ae87add67d..04bbfadcb7 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -33,6 +33,7 @@ #include "core/or/relay.h" #include "core/or/scheduler.h" #include "core/or/status.h" +#include "core/or/versions.h" #include "feature/api/tor_api.h" #include "feature/api/tor_api_internal.h" #include "feature/client/addressmap.h" @@ -791,6 +792,7 @@ tor_free_all(int postfork) dos_free_all(); circuitmux_ewma_free_all(); accounting_free_all(); + protover_summary_cache_free_all(); if (!postfork) { config_free_all(); diff --git a/src/core/or/versions.c b/src/core/or/versions.c index 06274996a7..6f8eea7a67 100644 --- a/src/core/or/versions.c +++ b/src/core/or/versions.c @@ -377,6 +377,62 @@ sort_version_list(smartlist_t *versions, int remove_duplicates) smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_); } +/** If there are more than this many entries, we're probably under + * some kind of weird DoS. */ +static const int MAX_PROTOVER_SUMMARY_MAP_LEN = 1024; + +/** + * Map from protover string to protover_summary_flags_t. + */ +static strmap_t *protover_summary_map = NULL; + +/** + * Helper. Given a non-NULL protover string protocols, set out + * to its summary, and memoize the result in protover_summary_map. + */ +static void +memoize_protover_summary(protover_summary_flags_t *out, + const char *protocols) +{ + if (!protover_summary_map) + protover_summary_map = strmap_new(); + + if (strmap_size(protover_summary_map) >= MAX_PROTOVER_SUMMARY_MAP_LEN) { + protover_summary_cache_free_all(); + } + + const protover_summary_flags_t *cached = + strmap_get(protover_summary_map, protocols); + + if (cached != NULL) { + /* We found a cached entry; no need to parse this one. */ + memcpy(out, cached, sizeof(protover_summary_flags_t)); + tor_assert(out->protocols_known); + return; + } + + memset(out, 0, sizeof(*out)); + out->protocols_known = 1; + out->supports_extend2_cells = + protocol_list_supports_protocol(protocols, PRT_RELAY, 2); + out->supports_ed25519_link_handshake_compat = + protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); + out->supports_ed25519_link_handshake_any = + protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3); + out->supports_ed25519_hs_intro = + protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4); + out->supports_v3_hsdir = + protocol_list_supports_protocol(protocols, PRT_HSDIR, + PROTOVER_HSDIR_V3); + out->supports_v3_rendezvous_point = + protocol_list_supports_protocol(protocols, PRT_HSREND, + PROTOVER_HS_RENDEZVOUS_POINT_V3); + + protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out)); + cached = strmap_set(protover_summary_map, protocols, new_cached); + tor_assert(!cached); +} + /** Summarize the protocols listed in protocols into out, * falling back or correcting them based on version as appropriate. */ @@ -388,21 +444,7 @@ summarize_protover_flags(protover_summary_flags_t *out, tor_assert(out); memset(out, 0, sizeof(*out)); if (protocols) { - out->protocols_known = 1; - out->supports_extend2_cells = - protocol_list_supports_protocol(protocols, PRT_RELAY, 2); - out->supports_ed25519_link_handshake_compat = - protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); - out->supports_ed25519_link_handshake_any = - protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3); - out->supports_ed25519_hs_intro = - protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4); - out->supports_v3_hsdir = - protocol_list_supports_protocol(protocols, PRT_HSDIR, - PROTOVER_HSDIR_V3); - out->supports_v3_rendezvous_point = - protocol_list_supports_protocol(protocols, PRT_HSREND, - PROTOVER_HS_RENDEZVOUS_POINT_V3); + memoize_protover_summary(out, protocols); } if (version && !strcmpstart(version, "Tor ")) { if (!out->protocols_known) { @@ -420,3 +462,13 @@ summarize_protover_flags(protover_summary_flags_t *out, } } } + +/** + * Free all space held in the protover_summary_map. + */ +void +protover_summary_cache_free_all(void) +{ + strmap_free(protover_summary_map, tor_free_); + protover_summary_map = NULL; +} diff --git a/src/core/or/versions.h b/src/core/or/versions.h index 0c773f3f4c..4fc50a0018 100644 --- a/src/core/or/versions.h +++ b/src/core/or/versions.h @@ -41,4 +41,6 @@ void summarize_protover_flags(protover_summary_flags_t *out, const char *protocols, const char *version); +void protover_summary_cache_free_all(void); + #endif /* !defined(TOR_VERSIONS_H) */