Reset autoloads in response to variable changes

Prior to this fix, autoloads like function and completion autoloads
would check their path variable (like fish_function_path) on every
autoload request. Switch to invalidating it in response to the variable
changing.

This improves time on a microbenchmark:

    for i in (seq 50000)
      setenv test_env val$i
    end

from ~11 seconds to ~6.5 seconds.
This commit is contained in:
ridiculousfish 2018-02-15 21:58:02 -08:00
parent be13ac353b
commit 9cd952588f
7 changed files with 44 additions and 19 deletions

View File

@ -65,19 +65,10 @@ int autoload_t::load(const wcstring &cmd, bool reload) {
CHECK_BLOCK(0); CHECK_BLOCK(0);
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
if (!this->paths) {
auto path_var = env_get(env_var_name); auto path_var = env_get(env_var_name);
// Do we know where to look?
if (path_var.missing_or_empty()) return 0; if (path_var.missing_or_empty()) return 0;
this->paths = path_var->as_list();
// Check if the lookup path has changed. If so, drop all loaded files.
if (*path_var != this->last_path) {
this->last_path = *path_var;
this->last_path_tokenized.clear();
this->last_path.to_list(this->last_path_tokenized);
scoped_lock locker(lock);
this->evict_all_nodes();
} }
// Mark that we're loading this. Hang onto the iterator for fast erasing later. Note that // Mark that we're loading this. Hang onto the iterator for fast erasing later. Note that
@ -98,7 +89,8 @@ int autoload_t::load(const wcstring &cmd, bool reload) {
return 1; return 1;
} }
// Try loading it. // Try loading it.
res = this->locate_file_and_maybe_load_it(cmd, true, reload, this->last_path_tokenized); assert(paths && "Should have paths");
res = this->locate_file_and_maybe_load_it(cmd, true, reload, *this->paths);
// Clean up. // Clean up.
is_loading_set.erase(where); is_loading_set.erase(where);
return res; return res;
@ -113,6 +105,13 @@ bool autoload_t::can_load(const wcstring &cmd, const env_vars_snapshot_t &vars)
return this->locate_file_and_maybe_load_it(cmd, false, false, path_list); return this->locate_file_and_maybe_load_it(cmd, false, false, path_list);
} }
void autoload_t::invalidate() {
ASSERT_IS_MAIN_THREAD();
scoped_lock locker(lock);
paths.reset();
this->evict_all_nodes();
}
/// Check whether the given command is loaded. /// Check whether the given command is loaded.
bool autoload_t::has_tried_loading(const wcstring &cmd) { bool autoload_t::has_tried_loading(const wcstring &cmd) {
scoped_lock locker(lock); scoped_lock locker(lock);

View File

@ -49,10 +49,8 @@ class autoload_t : public lru_cache_t<autoload_t, autoload_function_t> {
fish_mutex_t lock; fish_mutex_t lock;
/// The environment variable name. /// The environment variable name.
const wcstring env_var_name; const wcstring env_var_name;
/// The path from which we most recently autoloaded. /// The paths from which to autoload, or missing if none.
env_var_t last_path; maybe_t<wcstring_list_t> paths;
/// the most reecently autoloaded path, tokenized (split on separators).
wcstring_list_t last_path_tokenized;
/// A table containing all the files that are currently being loaded. /// A table containing all the files that are currently being loaded.
/// This is here to help prevent recursion. /// This is here to help prevent recursion.
std::unordered_set<wcstring> is_loading_set; std::unordered_set<wcstring> is_loading_set;
@ -91,5 +89,8 @@ class autoload_t : public lru_cache_t<autoload_t, autoload_function_t> {
/// Check whether the given command could be loaded, but do not load it. /// Check whether the given command could be loaded, but do not load it.
bool can_load(const wcstring &cmd, const env_vars_snapshot_t &vars); bool can_load(const wcstring &cmd, const env_vars_snapshot_t &vars);
/// Invalidates all entries. Uesd when the underlying path variable changes.
void invalidate();
}; };
#endif #endif

View File

@ -1558,6 +1558,8 @@ wcstring complete_print() {
return out; return out;
} }
void complete_invalidate_path() { completion_autoloader.invalidate(); }
/// Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list. /// Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list.
static fish_mutex_t wrapper_lock; static fish_mutex_t wrapper_lock;
typedef std::unordered_map<wcstring, wcstring_list_t> wrapper_map_t; typedef std::unordered_map<wcstring, wcstring_list_t> wrapper_map_t;

View File

@ -193,4 +193,7 @@ wcstring_list_t complete_get_wrap_chain(const wcstring &command);
// Wonky interface: returns all wraps. Even-values are the commands, odd values are the targets. // Wonky interface: returns all wraps. Even-values are the commands, odd values are the targets.
wcstring_list_t complete_get_wrap_pairs(); wcstring_list_t complete_get_wrap_pairs();
// Observes that fish_complete_path has changed.
void complete_invalidate_path();
#endif #endif

View File

@ -40,12 +40,14 @@
#include "builtin_bind.h" #include "builtin_bind.h"
#include "common.h" #include "common.h"
#include "complete.h"
#include "env.h" #include "env.h"
#include "env_universal_common.h" #include "env_universal_common.h"
#include "event.h" #include "event.h"
#include "expand.h" #include "expand.h"
#include "fallback.h" // IWYU pragma: keep #include "fallback.h" // IWYU pragma: keep
#include "fish_version.h" #include "fish_version.h"
#include "function.h"
#include "history.h" #include "history.h"
#include "input.h" #include "input.h"
#include "input_common.h" #include "input_common.h"
@ -814,6 +816,18 @@ static void handle_fish_history_change(const wcstring &op, const wcstring &var_n
reader_change_history(history_session_id().c_str()); reader_change_history(history_session_id().c_str());
} }
static void handle_function_path_change(const wcstring &op, const wcstring &var_name) {
UNUSED(op);
UNUSED(var_name);
function_invalidate_path();
}
static void handle_complete_path_change(const wcstring &op, const wcstring &var_name) {
UNUSED(op);
UNUSED(var_name);
complete_invalidate_path();
}
static void handle_tz_change(const wcstring &op, const wcstring &var_name) { static void handle_tz_change(const wcstring &op, const wcstring &var_name) {
UNUSED(op); UNUSED(op);
handle_timezone(var_name.c_str()); handle_timezone(var_name.c_str());
@ -856,6 +870,8 @@ static void setup_var_dispatch_table() {
var_dispatch_table.emplace(L"fish_escape_delay_ms", handle_escape_delay_change); var_dispatch_table.emplace(L"fish_escape_delay_ms", handle_escape_delay_change);
var_dispatch_table.emplace(L"LINES", handle_term_size_change); var_dispatch_table.emplace(L"LINES", handle_term_size_change);
var_dispatch_table.emplace(L"COLUMNS", handle_term_size_change); var_dispatch_table.emplace(L"COLUMNS", handle_term_size_change);
var_dispatch_table.emplace(L"fish_complete_path", handle_complete_path_change);
var_dispatch_table.emplace(L"fish_function_path", handle_function_path_change);
var_dispatch_table.emplace(L"fish_read_limit", handle_read_limit_change); var_dispatch_table.emplace(L"fish_read_limit", handle_read_limit_change);
var_dispatch_table.emplace(L"fish_history", handle_fish_history_change); var_dispatch_table.emplace(L"fish_history", handle_fish_history_change);
var_dispatch_table.emplace(L"TZ", handle_tz_change); var_dispatch_table.emplace(L"TZ", handle_tz_change);

View File

@ -339,6 +339,8 @@ int function_get_definition_lineno(const wcstring &name) {
return 1 + std::count(source.begin(), source.begin() + func_start, L'\n'); return 1 + std::count(source.begin(), source.begin() + func_start, L'\n');
} }
void function_invalidate_path() { function_autoloader.invalidate(); }
// Setup the environment for the function. There are three components of the environment: // Setup the environment for the function. There are three components of the environment:
// 1. argv // 1. argv
// 2. named arguments // 2. named arguments

View File

@ -108,9 +108,11 @@ std::map<wcstring, env_var_t> function_get_inherit_vars(const wcstring &name);
/// is successful. /// is successful.
bool function_copy(const wcstring &name, const wcstring &new_name); bool function_copy(const wcstring &name, const wcstring &new_name);
/// Prepares the environment for executing a function. /// Prepares the environment for executing a function.
void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, void function_prepare_environment(const wcstring &name, const wchar_t *const *argv,
const std::map<wcstring, env_var_t> &inherited_vars); const std::map<wcstring, env_var_t> &inherited_vars);
/// Observes that fish_function_path has changed.
void function_invalidate_path();
#endif #endif