Skip to content

Commit

Permalink
18479: Improves performance and memory reuse (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
howsohazard authored Nov 27, 2023
1 parent 361f921 commit e52971e
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 124 deletions.
6 changes: 5 additions & 1 deletion src/Amalgam/SeparableBoxFilterDataStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ void SeparableBoxFilterDataStore::BuildLabel(size_t column_index, const std::vec
auto &column_data = columnData[column_index];
auto label_id = column_data->stringId;

//if the label is accessible, then don't need to check every label for being private,
//can just inform entity to get on self for performance
bool is_label_accessible = !Entity::IsLabelPrivate(label_id);

auto &entities_with_number_values = parametersAndBuffers.entitiesWithValues;
entities_with_number_values.clear();

Expand All @@ -25,7 +29,7 @@ void SeparableBoxFilterDataStore::BuildLabel(size_t column_index, const std::vec
{
EvaluableNodeImmediateValueType value_type;
EvaluableNodeImmediateValue value;
value_type = entities[entity_index]->GetValueAtLabelAsImmediateValue(label_id, value);
value_type = entities[entity_index]->GetValueAtLabelAsImmediateValue(label_id, value, is_label_accessible);
GetValue(entity_index, column_index) = value;

column_data->InsertNextIndexValueExceptNumbers(value_type, value, entity_index, entities_with_number_values);
Expand Down
6 changes: 0 additions & 6 deletions src/Amalgam/entity/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,6 @@ bool Entity::GetValueAtLabelAsString(StringInternPool::StringID label_sid, std::
EvaluableNodeImmediateValueType Entity::GetValueAtLabelAsImmediateValue(StringInternPool::StringID label_sid,
EvaluableNodeImmediateValue &value_out, bool on_self)
{
if(label_sid <= StringInternPool::EMPTY_STRING_ID)
{
value_out.number = std::numeric_limits<double>::quiet_NaN();
return ENIVT_NOT_EXIST;
}

if(!on_self && IsLabelPrivate(label_sid))
{
value_out.number = std::numeric_limits<double>::quiet_NaN();
Expand Down
1 change: 1 addition & 0 deletions src/Amalgam/evaluablenode/EvaluableNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ class EvaluableNode
//fully clears node and sets it to new_type
inline void ClearAndSetType(EvaluableNodeType new_type)
{
ClearMetadata();
DestructValue();
InitializeType(new_type);
}
Expand Down
52 changes: 52 additions & 0 deletions src/Amalgam/evaluablenode/EvaluableNodeManagement.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,58 @@ class EvaluableNodeManager
};
EvaluableNode *AllocNode(EvaluableNode *original, EvaluableNodeMetadataModifier metadata_modifier = ENMM_NO_CHANGE);

//attempts to reuse candidate if it is unique and change it into the specified type
//if candidate is not unique, then it allocates and returns a new node
inline EvaluableNodeReference ReuseOrAllocNode(EvaluableNodeReference candidate, EvaluableNodeType type)
{
if(candidate.unique && candidate != nullptr)
{
//if not cyclic, can attempt to free all child nodes
//if cyclic, don't try, in case a child node points back to candidate
if(!candidate->GetNeedCycleCheck())
{
if(candidate->IsAssociativeArray())
{
for(auto &[_, e] : candidate->GetMappedChildNodesReference())
{
if(e != nullptr)
FreeNodeTreeRecurse(e);
}
}
else if(!candidate->IsImmediate())
{
for(auto &e : candidate->GetOrderedChildNodesReference())
{
if(e != nullptr)
FreeNodeTreeRecurse(e);
}
}
}

candidate->ClearAndSetType(type);
return candidate;
}
else
{
return EvaluableNodeReference(AllocNode(type), true);
}
}

//like ReuseOrAllocNode, but picks whichever node is reusable and frees the other if possible
//will try candidate_1 first
inline EvaluableNodeReference ReuseOrAllocOneOfNodes(
EvaluableNodeReference candidate_1, EvaluableNodeReference candidate_2, EvaluableNodeType type)
{
if(candidate_1.unique && candidate_1 != nullptr)
{
FreeNodeTreeIfPossible(candidate_2);
return ReuseOrAllocNode(candidate_1, type);
}

//candidate_1 wasn't unique, so try for candidate 2
return ReuseOrAllocNode(candidate_2, type);
}

//Copies the data structure and everything underneath it, modifying labels as specified
// if cycle_free is true on input, then it can perform a faster copy
inline EvaluableNodeReference DeepAllocCopy(EvaluableNode *tree, EvaluableNodeMetadataModifier metadata_modifier = ENMM_NO_CHANGE)
Expand Down
10 changes: 3 additions & 7 deletions src/Amalgam/interpreter/InterpreterOpcodesBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNPARSE(EvaluableNode *en)

auto tree = InterpretNodeForImmediateUse(ocn[0]);
std::string s = Parser::Unparse(tree, evaluableNodeManager, pretty, true, deterministic_order);
evaluableNodeManager->FreeNodeTreeIfPossible(tree);

return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_STRING, s), true);
EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(tree, ENT_STRING);
result->SetStringValue(s);
return result;
}

EvaluableNodeReference Interpreter::InterpretNode_ENT_IF(EvaluableNode *en)
Expand Down Expand Up @@ -1410,14 +1411,12 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RAND(EvaluableNode *en)
//want to generate multiple values, so return a list
//try to reuse param if can so don't need to allocate more memory
EvaluableNodeReference retval;
bool free_param = false;
if(param.unique)
{
retval = param;
}
else
{
free_param = true;
retval = EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_LIST), true);
retval->SetOrderedChildNodes(param->GetOrderedChildNodes());
}
Expand All @@ -1435,9 +1434,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RAND(EvaluableNode *en)
//free unneeded nodes that weren't part of the shuffle
if(param.unique && !param->GetNeedCycleCheck())
{
if(free_param)
evaluableNodeManager->FreeNodeIfPossible(param);

for(size_t i = number_to_generate; i < num_elements; i++)
evaluableNodeManager->FreeNodeTree(retval_ocn[i]);
}
Expand Down
10 changes: 5 additions & 5 deletions src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,11 +478,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_ENTITY_and_CALL_ENTIT
EvaluableNodeReference call_stack = ConvertArgsToCallStack(args, evaluableNodeManager);
node_stack.PushEvaluableNode(call_stack);

//get a write lock on the entity
EntityWriteReference called_entity = InterpretNodeIntoRelativeSourceEntityWriteReferenceFromInterpretedEvaluableNodeIDPath(ocn[0]);
if(called_entity == nullptr)
return EvaluableNodeReference::Null();

//current pointer to write listeners
std::vector<EntityWriteListener *> *cur_write_listeners = writeListeners;
//another storage container in case getting entity changes
Expand All @@ -497,6 +492,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_ENTITY_and_CALL_ENTIT
cur_write_listeners = &get_changes_write_listeners;
}

//get a write lock on the entity
EntityWriteReference called_entity = InterpretNodeIntoRelativeSourceEntityWriteReferenceFromInterpretedEvaluableNodeIDPath(ocn[0]);
if(called_entity == nullptr)
return EvaluableNodeReference::Null();

ExecutionCycleCount num_steps_executed = 0;
size_t num_nodes_allocated = 0;
EvaluableNodeReference retval = called_entity->Execute(num_steps_allowed, num_steps_executed,
Expand Down
22 changes: 12 additions & 10 deletions src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_TAIL(EvaluableNode *en)
//drop the number of characters before this length
size_t utf8_start_offset = StringManipulation::GetNthUTF8CharacterOffset(s, num_chars_to_drop);

evaluableNodeManager->FreeNodeTreeIfPossible(list);

return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_STRING, s.substr(utf8_start_offset, s.size() - utf8_start_offset)), true);
EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_STRING);
result->SetStringValue(s.substr(utf8_start_offset, s.size() - utf8_start_offset));
return result;
}

if(DoesEvaluableNodeTypeUseNumberData(list->GetType()))
Expand All @@ -224,10 +224,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_TAIL(EvaluableNode *en)
if(value == 0.0)
return list;

evaluableNodeManager->FreeNodeTreeIfPossible(list);

//return (value - 1.0) if nonzero
return EvaluableNodeReference(evaluableNodeManager->AllocNode(value - 1.0), true);
EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_NUMBER);
result->SetNumberValue(value - 1.0);
return result;
}
}

Expand Down Expand Up @@ -300,8 +300,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LAST(EvaluableNode *en)

auto [utf8_char_start_offset, utf8_char_length] = StringManipulation::GetLastUTF8CharacterOffsetAndLength(s);

evaluableNodeManager->FreeNodeTreeIfPossible(list);
return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_STRING, s.substr(utf8_char_start_offset, utf8_char_length)), true);
EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_STRING);
result->SetStringValue(s.substr(utf8_char_start_offset, utf8_char_length));
return result;
}

if(DoesEvaluableNodeTypeUseNumberData(list->GetType()))
Expand All @@ -312,8 +313,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LAST(EvaluableNode *en)
return list;

//return 1 if nonzero
evaluableNodeManager->FreeNodeTreeIfPossible(list);
return EvaluableNodeReference(evaluableNodeManager->AllocNode(1.0), true);
EvaluableNodeReference result = evaluableNodeManager->ReuseOrAllocNode(list, ENT_NUMBER);
result->SetNumberValue(1.0);
return result;
}
}

Expand Down
68 changes: 26 additions & 42 deletions src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@

EvaluableNodeReference Interpreter::InterpretNode_ENT_AND(EvaluableNode *en)
{
EvaluableNodeReference cur = EvaluableNodeReference::Null();
auto &ocn = en->GetOrderedChildNodes();
if(ocn.size() == 0)
return EvaluableNodeReference::Null();

EvaluableNodeReference cur = EvaluableNodeReference::Null();

#ifdef MULTITHREAD_SUPPORT
std::vector<EvaluableNodeReference> interpreted_nodes;
Expand All @@ -36,10 +39,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_AND(EvaluableNode *en)
cur = cn;

if(!EvaluableNode::IsTrue(cur))
{
evaluableNodeManager->FreeNodeTreeIfPossible(cur);
return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true);
}
return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE);
}

return cur;
Expand All @@ -54,49 +54,52 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_AND(EvaluableNode *en)
cur = InterpretNode(cn);

if(!EvaluableNode::IsTrue(cur))
{
evaluableNodeManager->FreeNodeTreeIfPossible(cur);
return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true);
}
return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE);
}

return cur;
}

EvaluableNodeReference Interpreter::InterpretNode_ENT_OR(EvaluableNode *en)
{
auto &ocn = en->GetOrderedChildNodes();

if(ocn.size() == 0)
return EvaluableNodeReference::Null();

EvaluableNodeReference cur = EvaluableNodeReference::Null();

#ifdef MULTITHREAD_SUPPORT
std::vector<EvaluableNodeReference> interpreted_nodes;
if(InterpretEvaluableNodesConcurrently(en, ocn, interpreted_nodes))
{
for(auto &cur : interpreted_nodes)
for(auto &cn : interpreted_nodes)
{
//free the previous node if applicable
evaluableNodeManager->FreeNodeTreeIfPossible(cur);

cur = cn;

//if it is a valid node and it is not zero, then return it
if(EvaluableNode::IsTrue(cur))
return cur;

evaluableNodeManager->FreeNodeTreeIfPossible(cur);
}

return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true);
return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE);
}
#endif

for(auto &cn : ocn)
{
auto cur = InterpretNode(cn);
//free the previous node if applicable
evaluableNodeManager->FreeNodeTreeIfPossible(cur);

cur = InterpretNode(cn);

//if it is a valid node and it is not zero, then return it
if(EvaluableNode::IsTrue(cur))
return cur;

evaluableNodeManager->FreeNodeTreeIfPossible(cur);
}
return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true);

return evaluableNodeManager->ReuseOrAllocNode(cur, ENT_FALSE);
}

EvaluableNodeReference Interpreter::InterpretNode_ENT_XOR(EvaluableNode *en)
Expand Down Expand Up @@ -145,15 +148,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_NOT(EvaluableNode *en)
return EvaluableNodeReference::Null();

auto cur = InterpretNodeForImmediateUse(ocn[0]);

bool is_true = EvaluableNode::IsTrue(cur);

if(cur.unique && cur != nullptr)
cur->ClearAndSetType(is_true ? ENT_FALSE : ENT_TRUE);
else
cur = EvaluableNodeReference(evaluableNodeManager->AllocNode(is_true ? ENT_FALSE : ENT_TRUE), true);

return cur;
return evaluableNodeManager->ReuseOrAllocNode(cur, is_true ? ENT_FALSE : ENT_TRUE);
}

EvaluableNodeReference Interpreter::InterpretNode_ENT_EQUAL(EvaluableNode *en)
Expand Down Expand Up @@ -181,12 +178,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_EQUAL(EvaluableNode *en)
}

if(!EvaluableNode::AreDeepEqual(to_match, cur))
{
evaluableNodeManager->FreeNodeTreeIfPossible(to_match);
evaluableNodeManager->FreeNodeTreeIfPossible(cur);

return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true);
}
return evaluableNodeManager->ReuseOrAllocOneOfNodes(to_match, cur, ENT_FALSE);

evaluableNodeManager->FreeNodeTreeIfPossible(cur);
}
Expand All @@ -213,12 +205,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_EQUAL(EvaluableNode *en)
}

if(!EvaluableNode::AreDeepEqual(to_match, cur))
{
evaluableNodeManager->FreeNodeTreeIfPossible(to_match);
evaluableNodeManager->FreeNodeTreeIfPossible(cur);

return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_FALSE), true);
}
return evaluableNodeManager->ReuseOrAllocOneOfNodes(to_match, cur, ENT_FALSE);

evaluableNodeManager->FreeNodeTreeIfPossible(cur);
}
Expand Down Expand Up @@ -273,10 +260,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_NEQUAL(EvaluableNode *en)
EvaluableNodeReference b = InterpretNodeForImmediateUse(ocn[1]);

bool a_b_not_equal = (!EvaluableNode::AreDeepEqual(a, b));
evaluableNodeManager->FreeNodeTreeIfPossible(a);
evaluableNodeManager->FreeNodeTreeIfPossible(b);

return EvaluableNodeReference(evaluableNodeManager->AllocNode(a_b_not_equal ? ENT_TRUE : ENT_FALSE), true);
return evaluableNodeManager->ReuseOrAllocOneOfNodes(a, b, a_b_not_equal ? ENT_TRUE : ENT_FALSE);
}

auto node_stack = CreateInterpreterNodeStackStateSaver();
Expand Down
Loading

0 comments on commit e52971e

Please sign in to comment.