Skip to content

Commit

Permalink
[goalc] Cleaned up speedups (#3066)
Browse files Browse the repository at this point in the history
Started at 349,880,038 allocations and 42s

- Switched to making `Symbol` in GOOS be a "fixed type", just a wrapper
around a `const char*` pointing to the string in the symbol table. This
is a step toward making a lot of things better, but by itself not a huge
improvement. Some things may be worse due to more temp `std::string`
allocations, but one day all these can be removed. On linux it saved
allocations (347,685,429), and saved a second or two (41 s).
- cache `#t` and `#f` in interpreter, better lookup for special
forms/builtins (hashtable of pointers instead of strings, vector for the
small special form list). Dropped time to 38s.
- special-case in quasiquote when splicing is the last thing in a list.
Allocation dropped to 340,603,082
- custom hash table for environment lookups (lexical vars). Dropped to
36s and 314,637,194
- less allocation in `read_list` 311,613,616. Time about the same.
- `let` and `let*` in Interpreter.cpp 191,988,083, time down to 28s.
  • Loading branch information
water111 authored Oct 7, 2023
1 parent af6f489 commit 395c98d
Show file tree
Hide file tree
Showing 43 changed files with 950 additions and 551 deletions.
500 changes: 362 additions & 138 deletions common/goos/Interpreter.cpp

Large diffs are not rendered by default.

56 changes: 43 additions & 13 deletions common/goos/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Interpreter {
void set_global_variable_to_int(const std::string& name, int value);
Object eval(Object obj, const std::shared_ptr<EnvironmentObject>& env);
Object intern(const std::string& name);
HeapObject* intern_ptr(const std::string& name);
InternedSymbolPtr intern_ptr(const std::string& name);
void disable_printfs();
Object eval_symbol(const Object& sym, const std::shared_ptr<EnvironmentObject>& env);
bool eval_symbol(const Object& sym,
Expand Down Expand Up @@ -55,7 +55,7 @@ class Interpreter {
private:
friend class Goal;
void load_goos_library();
void define_var_in_env(Object& env, Object& var, const std::string& name);
void define_var_in_env(Object& env, const Object& var, const std::string& name);
void expect_env(const Object& form, const Object& o);
void vararg_check(
const Object& form,
Expand Down Expand Up @@ -264,6 +264,16 @@ class Interpreter {
Object eval_while(const Object& form,
const Object& rest,
const std::shared_ptr<EnvironmentObject>& env);
Object eval_let(const Object& form,
const Object& rest,
const std::shared_ptr<EnvironmentObject>& env);
Object eval_let_star(const Object& form,
const Object& rest,
const std::shared_ptr<EnvironmentObject>& env);
Object eval_let_common(const Object& form,
const Object& rest,
const std::shared_ptr<EnvironmentObject>& env,
bool star);

Object eval_make_string_hash_table(const Object& form,
Arguments& args,
Expand All @@ -275,26 +285,46 @@ class Interpreter {
Arguments& args,
const std::shared_ptr<EnvironmentObject>& env);

const Object& true_or_false(bool val);

void init_special_forms(
const std::unordered_map<std::string,
Object (Interpreter::*)(const Object&,
const Object&,
const std::shared_ptr<EnvironmentObject>&)>&
forms);

void init_builtin_forms(
const std::unordered_map<std::string,
Object (Interpreter::*)(const Object&,
Arguments&,
const std::shared_ptr<EnvironmentObject>&)>&
forms);

bool want_exit = false;
bool disable_printing = false;

std::unordered_map<std::string,
Object (Interpreter::*)(const Object& form,
Arguments& args,
const std::shared_ptr<EnvironmentObject>& env)>
std::unordered_map<
void*,
Object (Interpreter::*)(const Object&, Arguments&, const std::shared_ptr<EnvironmentObject>&)>
builtin_forms;

std::unordered_map<
std::string,
std::function<Object(const Object&, Arguments&, const std::shared_ptr<EnvironmentObject>&)>>
std::vector<std::pair<
void*,
std::function<Object(const Object&, Arguments&, const std::shared_ptr<EnvironmentObject>&)>>>
m_custom_forms;
std::unordered_map<std::string,
Object (Interpreter::*)(const Object& form,
const Object& rest,
const std::shared_ptr<EnvironmentObject>& env)>

std::vector<std::pair<void*,
Object (Interpreter::*)(const Object& form,
const Object& rest,
const std::shared_ptr<EnvironmentObject>& env)>>
special_forms;
int64_t gensym_id = 0;

std::unordered_map<std::string, ObjectType> string_to_type;

const char* m_false_sym = nullptr;
const char* m_true_sym = nullptr;
Object m_false_object, m_true_object;
};
} // namespace goos
94 changes: 84 additions & 10 deletions common/goos/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,90 @@
#include <cstring>

#include "common/util/FileUtil.h"
#include "common/util/crc32.h"
#include "common/util/print_float.h"

#include "third-party/fmt/core.h"

namespace goos {

SymbolTable::SymbolTable() {
m_power_of_two_size = 1; // 2 ^ 1 = 2
m_entries.resize(2);
m_used_entries = 0;
m_next_resize = (m_entries.size() * kMaxUsed);
m_mask = 0b1;
}

SymbolTable::~SymbolTable() {
for (auto& e : m_entries) {
delete[] e.name;
}
}

InternedSymbolPtr SymbolTable::intern(const char* str) {
InternedSymbolPtr result;
size_t string_len = strlen(str);
u32 hash = crc32((const u8*)str, string_len);

// probe
for (u32 i = 0; i < m_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = m_entries[slot_addr];
if (!slot.name) {
// not found, insert!
slot.hash = hash;
auto* name = new char[string_len + 1];
memcpy(name, str, string_len + 1);
slot.name = name;
m_used_entries++;

if (m_used_entries >= m_next_resize) {
resize();
return intern(str);
}
return {name};
} else {
if (slot.hash != hash) {
continue; // bad hash
}
if (strcmp(slot.name, str) != 0) {
continue; // bad name
}
return {slot.name};
}
}

// should be impossible to reach.
ASSERT_NOT_REACHED();
}

void SymbolTable::resize() {
m_power_of_two_size++;
m_mask = (1U << m_power_of_two_size) - 1;

std::vector<Entry> new_entries(m_entries.size() * 2);
for (const auto& old_entry : m_entries) {
if (old_entry.name) {
bool done = false;
for (u32 i = 0; i < new_entries.size(); i++) {
u32 slot_addr = (old_entry.hash + i) & m_mask;
auto& slot = new_entries[slot_addr];
if (!slot.name) {
slot.name = old_entry.name;
slot.hash = old_entry.hash;
done = true;
break;
}
}
ASSERT(done);
}
}

m_entries = std::move(new_entries);
m_next_resize = kMaxUsed * m_entries.size();
}

/*!
* Convert type to string (name in brackets)
*/
Expand Down Expand Up @@ -137,14 +215,9 @@ std::string fixed_to_string(char x) {
return {buff};
}

/*!
* Create a new symbol object by interning
*/
Object SymbolObject::make_new(SymbolTable& st, const std::string& name) {
Object obj;
obj.type = ObjectType::SYMBOL;
obj.heap_obj = st.intern(name);
return obj;
template <>
std::string fixed_to_string(InternedSymbolPtr x) {
return x.name_ptr;
}

/*!
Expand Down Expand Up @@ -238,8 +311,9 @@ bool Object::operator==(const Object& other) const {
return float_obj == other.float_obj;
case ObjectType::CHAR:
return char_obj == other.char_obj;

case ObjectType::SYMBOL:
return symbol_obj == other.symbol_obj;

case ObjectType::ENVIRONMENT:
case ObjectType::LAMBDA:
case ObjectType::MACRO:
Expand Down Expand Up @@ -271,7 +345,7 @@ bool Object::operator==(const Object& other) const {
}

bool Object::is_symbol(const std::string& name) const {
return is_symbol() && as_symbol()->name == name;
return is_symbol() && name == as_symbol().name_ptr;
}

bool Object::is_string(const std::string& val) const {
Expand Down
Loading

0 comments on commit 395c98d

Please sign in to comment.