Merge remote-tracking branch 'origin/cpp_fun_call_performance' into develop

This commit is contained in:
Jason Turner
2015-09-18 13:41:44 -06:00
4 changed files with 178 additions and 89 deletions

View File

@@ -417,9 +417,9 @@ namespace chaiscript
struct State
{
std::map<std::string, std::vector<Proxy_Function> > m_functions;
std::map<std::string, Proxy_Function> m_function_objects;
std::map<std::string, Boxed_Value> m_boxed_functions;
std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> m_functions;
std::vector<std::pair<std::string, Proxy_Function>> m_function_objects;
std::vector<std::pair<std::string, Boxed_Value>> m_boxed_functions;
std::map<std::string, Boxed_Value> m_global_objects;
Type_Name_Map m_types;
std::set<std::string> m_reserved_words;
@@ -518,7 +518,7 @@ namespace chaiscript
throw chaiscript::exception::global_non_const();
}
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
{
@@ -533,7 +533,7 @@ namespace chaiscript
{
validate_object_name(name);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto itr = m_state.m_global_objects.find(name);
if (itr == m_state.m_global_objects.end())
@@ -551,7 +551,7 @@ namespace chaiscript
{
validate_object_name(name);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end())
{
@@ -648,19 +648,21 @@ namespace chaiscript
return stack[stack.size() - 1 - ((loc & static_cast<uint_fast32_t>(Loc::stack_mask)) >> 16)][loc & static_cast<uint_fast32_t>(Loc::loc_mask)].second;
}
// Is the value we are looking for a global?
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
// Is the value we are looking for a global or function?
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto itr = m_state.m_global_objects.find(name);
if (itr != m_state.m_global_objects.end())
{
return itr->second;
}
const auto itr = m_state.m_global_objects.find(name);
if (itr != m_state.m_global_objects.end())
{
return itr->second;
}
// If all that failed, then check to see if it's a function
return get_function_object(name);
// no? is it a function object?
auto obj = get_function_object_int(name, loc);
if (obj.first != loc) t_loc.store(uint_fast32_t(obj.first), std::memory_order_relaxed);
return obj.second;
}
/// Registers a new named type
@@ -718,20 +720,29 @@ namespace chaiscript
return std::vector<std::pair<std::string, Type_Info> >(m_state.m_types.begin(), m_state.m_types.end());
}
std::shared_ptr<std::vector<Proxy_Function>> get_method_missing_functions() const
{
uint_fast32_t method_missing_loc = m_method_missing_loc.load(std::memory_order_relaxed);
auto method_missing_funs = get_function("method_missing", method_missing_loc);
if (method_missing_funs.first != method_missing_loc) m_method_missing_loc.store(uint_fast32_t(method_missing_funs.first), std::memory_order_relaxed);
return std::move(method_missing_funs.second);
}
/// Return a function by name
std::vector< Proxy_Function > get_function(const std::string &t_name) const
std::pair<size_t, std::shared_ptr<std::vector< Proxy_Function>>> get_function(const std::string &t_name, const size_t t_hint) const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto &funs = get_functions_int();
auto itr = funs.find(t_name);
auto itr = find_keyed_value(funs, t_name, t_hint);
if (itr != funs.end())
{
return itr->second;
return std::make_pair(std::distance(funs.begin(), itr), itr->second);
} else {
return std::vector<Proxy_Function>();
return std::make_pair(size_t(0), std::make_shared<std::vector<Proxy_Function>>());
}
}
@@ -739,28 +750,36 @@ namespace chaiscript
/// \throws std::range_error if it does not
Boxed_Value get_function_object(const std::string &t_name) const
{
// std::cout << "Getting function object: " << t_name << '\n';
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
return get_function_object_int(t_name, 0).second;
}
/// \returns a function object (Boxed_Value wrapper) if it exists
/// \throws std::range_error if it does not
/// \warn does not obtain a mutex lock. \sa get_function_object for public version
std::pair<size_t, Boxed_Value> get_function_object_int(const std::string &t_name, const size_t t_hint) const
{
const auto &funs = get_boxed_functions_int();
auto itr = funs.find(t_name);
auto itr = find_keyed_value(funs, t_name, t_hint);
if (itr != funs.end())
{
return itr->second;
return std::make_pair(std::distance(funs.begin(), itr), itr->second);
} else {
throw std::range_error("Object not found: " + t_name);
}
}
/// Return true if a function exists
bool function_exists(const std::string &name) const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
const auto &functions = get_functions_int();
return functions.find(name) != functions.end();
return find_keyed_value(functions, name) != functions.end();
}
/// \returns All values in the local thread state in the parent scope, or if it doesn't exist,
@@ -817,11 +836,8 @@ namespace chaiscript
}
// add the global values
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_global_object_mutex);
retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
}
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end());
return retval;
}
@@ -858,7 +874,7 @@ namespace chaiscript
for (const auto & function : functions)
{
for (const auto & internal_func : function.second)
for (const auto & internal_func : *function.second)
{
rets.emplace_back(function.first, internal_func);
}
@@ -903,9 +919,11 @@ namespace chaiscript
#pragma warning(push)
#pragma warning(disable : 4715)
#endif
Boxed_Value call_member(const std::string &t_name, const std::vector<Boxed_Value> &params, bool t_has_params)
Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params, bool t_has_params)
{
const auto funs = get_function(t_name);
uint_fast32_t loc = t_loc.load(std::memory_order_relaxed);
const auto funs = get_function(t_name, loc);
if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed);
const auto do_attribute_call =
[this](int l_num_params, const std::vector<Boxed_Value> &l_params, const std::vector<Proxy_Function> &l_funs, const Type_Conversions &l_conversions)->Boxed_Value
@@ -941,14 +959,14 @@ namespace chaiscript
}
};
if (is_attribute_call(funs, params, t_has_params)) {
return do_attribute_call(1, params, funs, m_conversions);
if (is_attribute_call(*funs.second, params, t_has_params)) {
return do_attribute_call(1, params, *funs.second, m_conversions);
} else {
std::exception_ptr except;
if (!funs.empty()) {
if (!funs.second->empty()) {
try {
return dispatch::dispatch(funs, params, m_conversions);
return dispatch::dispatch(*funs.second, params, m_conversions);
} catch(chaiscript::exception::dispatch_error&) {
except = std::current_exception();
}
@@ -960,7 +978,9 @@ namespace chaiscript
const auto functions = [&]()->std::vector<Proxy_Function> {
std::vector<Proxy_Function> fs;
for (const auto &f : get_function("method_missing"))
const auto method_missing_funs = get_method_missing_functions();
for (const auto &f : *method_missing_funs)
{
if(f->compare_first_type(params[0], m_conversions)) {
fs.push_back(f);
@@ -996,7 +1016,7 @@ namespace chaiscript
if (except) {
std::rethrow_exception(except);
} else {
throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.begin(), funs.end()));
throw chaiscript::exception::dispatch_error(params, std::vector<Const_Proxy_Function>(funs.second->begin(), funs.second->end()));
}
}
}
@@ -1006,9 +1026,12 @@ namespace chaiscript
Boxed_Value call_function(const std::string &t_name, const std::vector<Boxed_Value> &params) const
Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector<Boxed_Value> &params) const
{
Boxed_Value bv = dispatch::dispatch(get_function(t_name), params, m_conversions);
uint_fast32_t loc = t_loc.load(std::memory_order_relaxed);
const auto funs = get_function(t_name, loc);
if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed);
Boxed_Value bv = dispatch::dispatch(*funs.second, params, m_conversions);
// the result of a clone is never to be marked as a return_value
if (t_name == "clone") {
bv.reset_return_value();
@@ -1016,20 +1039,6 @@ namespace chaiscript
return bv;
}
Boxed_Value call_function(const std::string &t_name) const
{
return call_function(t_name, std::vector<Boxed_Value>());
}
Boxed_Value call_function(const std::string &t_name, Boxed_Value p1) const
{
return call_function(t_name, std::vector<Boxed_Value>({std::move(p1)}));
}
Boxed_Value call_function(const std::string &t_name, Boxed_Value p1, Boxed_Value p2) const
{
return call_function(t_name, std::vector<Boxed_Value>({std::move(p1), std::move(p2)}));
}
/// Dump object info to stdout
void dump_object(const Boxed_Value &o) const
@@ -1140,7 +1149,6 @@ namespace chaiscript
State get_state() const
{
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_global_object_mutex);
return m_state;
}
@@ -1148,7 +1156,6 @@ namespace chaiscript
void set_state(const State &t_state)
{
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l2(m_global_object_mutex);
m_state = t_state;
}
@@ -1245,32 +1252,32 @@ namespace chaiscript
private:
const std::map<std::string, Boxed_Value> &get_boxed_functions_int() const
const std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int() const
{
return m_state.m_boxed_functions;
}
std::map<std::string, Boxed_Value> &get_boxed_functions_int()
std::vector<std::pair<std::string, Boxed_Value>> &get_boxed_functions_int()
{
return m_state.m_boxed_functions;
}
const std::map<std::string, Proxy_Function> &get_function_objects_int() const
const std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int() const
{
return m_state.m_function_objects;
}
std::map<std::string, Proxy_Function> &get_function_objects_int()
std::vector<std::pair<std::string, Proxy_Function>> &get_function_objects_int()
{
return m_state.m_function_objects;
}
const std::map<std::string, std::vector<Proxy_Function> > &get_functions_int() const
const std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int() const
{
return m_state.m_functions;
}
std::map<std::string, std::vector<Proxy_Function> > &get_functions_int()
std::vector<std::pair<std::string, std::shared_ptr<std::vector<Proxy_Function>>>> &get_functions_int()
{
return m_state.m_functions;
}
@@ -1384,6 +1391,49 @@ namespace chaiscript
}
}
template<typename Container, typename Key, typename Value>
static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value)
{
auto itr = find_keyed_value(t_c, t_key);
if (itr == t_c.end()) {
t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here
t_c.emplace_back(t_key, std::forward<Value>(t_value));
} else {
typedef typename Container::value_type value_type;
*itr = value_type(t_key, std::forward<Value>(t_value));
}
}
template<typename Container, typename Key>
static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key)
{
return std::find_if(t_c.begin(), t_c.end(),
[&t_key](const typename Container::value_type &o) {
return o.first == t_key;
});
}
template<typename Container, typename Key>
static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key)
{
return std::find_if(t_c.begin(), t_c.end(),
[&t_key](const typename Container::value_type &o) {
return o.first == t_key;
});
}
template<typename Container, typename Key>
static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint)
{
if (t_c.size() > t_hint && t_c[t_hint].first == t_key) {
return t_c.begin() + t_hint;
} else {
return find_keyed_value(t_c, t_key);
}
}
/// Implementation detail for adding a function.
/// \throws exception::name_conflict_error if there's a function matching the given one being added
void add_function(const Proxy_Function &t_f, const std::string &t_name)
@@ -1392,16 +1442,13 @@ namespace chaiscript
auto &funcs = get_functions_int();
auto itr = funcs.find(t_name);
auto &func_objs = get_function_objects_int();
auto &boxed_funcs = get_boxed_functions_int();
auto itr = find_keyed_value(funcs, t_name);
Proxy_Function new_func =
[&]() -> Proxy_Function {
if (itr != funcs.end())
{
auto &vec = itr->second;
auto vec = *itr->second;
for (const auto &func : vec)
{
if ((*t_f) == *(func))
@@ -1410,33 +1457,35 @@ namespace chaiscript
}
}
vec.reserve(vec.size() + 1); // tightly control vec growth
vec.push_back(t_f);
std::stable_sort(vec.begin(), vec.end(), &function_less_than);
return std::make_shared<Dispatch_Function>(vec);
itr->second = std::make_shared<std::vector<Proxy_Function>>(vec);
return std::make_shared<Dispatch_Function>(std::move(vec));
} else if (t_f->has_arithmetic_param()) {
// if the function is the only function but it also contains
// arithmetic operators, we must wrap it in a dispatch function
// to allow for automatic arithmetic type conversions
std::vector<Proxy_Function> vec({t_f});
funcs.insert(std::make_pair(t_name, vec));
funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(vec));
return std::make_shared<Dispatch_Function>(std::move(vec));
} else {
funcs.insert(std::make_pair(t_name, std::vector<Proxy_Function>{t_f}));
funcs.emplace_back(t_name, std::make_shared<std::vector<Proxy_Function>>(std::initializer_list<Proxy_Function>({t_f})));
return t_f;
}
}();
boxed_funcs[t_name] = const_var(new_func);
func_objs[t_name] = std::move(new_func);
add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func));
add_keyed_value(get_function_objects_int(), t_name, std::move(new_func));
}
mutable chaiscript::detail::threading::shared_mutex m_mutex;
mutable chaiscript::detail::threading::shared_mutex m_global_object_mutex;
Type_Conversions m_conversions;
chaiscript::detail::threading::Thread_Storage<Stack_Holder> m_stack_holder;
mutable std::atomic_uint_fast32_t m_method_missing_loc;
State m_state;
};