-
Notifications
You must be signed in to change notification settings - Fork 222
[[ VarAccess ]] Cleanup and optimization #5100
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome. 🥇
While you're at it, why not convert MCContainer::m_path
to an MCAutoNameRefArray
or something?
engine/src/cmdse.cpp
Outdated
MCContainer *t_container; | ||
if (tptr -> evalcontainer(ctxt, t_container)) | ||
MCContainer *t_container = new(nothrow) MCContainer; | ||
if (tptr -> evalcontainer(ctxt, *t_container)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment, this will leak the container if evalcontainer()
fails. I suggest an MCAutoPointer<MCContainer>
here.
engine/src/exec-keywords.cpp
Outdated
MCContainer *t_container; | ||
if (tptr -> evalcontainer(ctxt, t_container)) | ||
MCContainer *t_container = new(nothrow) MCContainer; | ||
if (tptr -> evalcontainer(ctxt, *t_container)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will also leak the allocated MCContainer
instance.
engine/src/externalv1.cpp
Outdated
} | ||
{ | ||
MCContainer *t_container; | ||
t_container = p_parameters -> eval_argument_container(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a managed lifetime here if possible!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it is possible as the MCParameter owns the MCContainer, and the MCReferenceExternalVariable will share it temporarily.
return NULL; | ||
} | ||
|
||
MCArrayRef t_array = (MCArrayRef)*t_value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that there are two pointers to the value live on the stack at the same time, one managed and one unmanaged. I suggest something like:
MCAutoArrayRef t_array = static_cast<MCArrayRef>(t_value.Take());
Using Take()
avoids frobbing the reference count.
Also, don't forget that we're supposed to be avoiding C-style casts these days! ;-) There are a couple of examples like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm - I'm not sure this works with the current definition of the MCArrayRef constructor in MCAutoValueRefBase as it retains the incoming argument. Here we don't want to fettle with reference counts at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I suppose the alternative would be:
MCAutoArrayRef t_array = static_cast<MCArrayRef>(*t_value);
Admittedly this does require taking another reference to the value, but that's not necessarily a bad thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it is a bad thing as the operation is atomic so the reference count bump is entirely unnecessary. I'd rather we 'fixed' the auto classes to allow movement of a MCValueRef into another auto container or a specific MCRef container - I'm just not sure what the 'correct' pattern for that is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather we 'fixed' the auto classes to allow movement of a MCValueRef into another auto container or a specific MCRef container
Good idea. I'll think of something. 👍
r_out[i + p_base.size()] = p_extra[i]; | ||
|
||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider:
static MCAutoPointer<MCNameRef[]>
__join_paths(MCSpan<MCNameRef> p_base,
MCSpan<MCNameRef> p_extra)
{
MCAutoPointer t_result =
new (nothrow) MCNameRef[p_base.size() + p_extra.size()];
if (t_result)
{
/* Populate t_result */
}
return std::move(t_result);
}
Then, when you use it, you can write:
MCAutoPointer<MCNameRef[]> t_full_path =
__join_paths(MCMakeSpan(m_path, m_length),
MCMakeSpan(p_path, p_path_length));
if (!t_full_path)
return false;
This ensures RAII everywhere, and it's the pattern recommended by the C++ Guideline Standard for functions that create an owned value.
BTW I generally recommend MCMakeSpan()
for creating spans in argument contexts like this.
This patch moves all implementations of hash functions to inline implementations in a private header foundation-hash.h. Hash implementations are provided for integer-like things, byte-like things and Unicode codepoint type things.
b9db42f
to
80f53c7
Compare
@runrevmark Did you intentionally include the hash functions refactor in this PR? |
@peter-b: Yes - it is an st-git stack so I can separate things out subsequently if necessary. The hash cleanup is required for an optimization to looking up elements in sequences. |
This patch adds some tests and some benchmarks covering variable access operations.
This patch removes the 'rootmatches()' method from MCVarref along with its uses in the arithmetic commands. The latter used it to compute overlap between base and value expressions but this information is no longer needed.
This patch changes the creation of MCContainers so that the constructors act on a reference, rather than returning a new object. This allows stack allocation of MCContainer classes in almost all cases.
This patch removes the 'evalvar()' virtual method from MCExpression replacing all uses with 'evalcontainer()'.
The MCNameLookup method has been renamed to MCNameLookupCaseless to more accurately describe its function.
This patch adds a MCStringIsEqualToChars function to libfoundation allowing direct comparison between a MCStringRef and buffer of unichar_t.
This patch improves the performance of names when used as indexes into arrays. It adds an optimized codepath for looking up and creating names which are integers (in the range supported by index_t) and ensures these codepaths are used when converting from a value to a nameref if the initial value is a number.
80f53c7
to
e8c6ab2
Compare
This patch adds a virtual method 'is_pure' to the MCExpression class. This method returns true if evaluating the expression has no side-effects. Note: This is currently incomplete - only MCLiteral, MCConstant and MCVarref have a non-default implementation.
This patch adds an MCArrayMutateValue function to libfoundation. The function allows direct access to the slot of an element within an array, allowing the caller to assign to it directly, rather than having to fetch and then store.
This patch adds a 'capacity' field to MCAutoArrayRefBase allowing the auto class to either be used as a fixed size array, or a dynamically sizable array more easily.
This patch rewrites the resolve method to use a template class which is customizable. Customizations have been implemented for: - deferring evaluation of array keys (i.e. storing the full list of MCNameRefs) - fetching an array element when the dimensions are all pure (eval_ctxt) - storing into an array element when the dimensions are all pure (set and give_value) - removing an array element when the dimensions are all pure (dofree) Note: 'before' and 'after' mutation operations do not currently have a 'pure' code path.
This removes the pure path for dofree() and ensures a var being set via setvalueref is clearuql'd first (otherwise the matchText tests fail).
t_result[i + p_base.size()] = p_extra[i]; | ||
} | ||
|
||
return std::move(t_result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave you duff information here. return
implicitly moves its argument, so this can just be:
return t_result;
r_container.m_variable = p_var; | ||
r_container.m_length = 0; | ||
r_container.m_path = nil; | ||
r_container.m_case_sensitive = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also:
createwithpath(p_var, nullptr, 0, r_container;
Variable access performance improvements Cherry-picked from #5100.
This is work-in-progress set of st-git patches which cleans up the implementation of variable access in the engine.
At present it includes the following:
This patch provides a substantial speed up to variable manipulation operations. Indeed, indexing operations using strings or literals (names) are 2-3 times faster (both storing and fetching); whilst indexing operations using integers are 4 times faster (storing) and 7 times faster (fetching). (All timings compared against 9dp4).
Note: This additionally changes the external interfaces so that internally they act on a container, rather than a variable - this should mean that externals should now be able to correctly set variables which are references to array elements... Tests need to be added for the externals interfaces to ensure that nothing has broken!
Note: The 'before' and 'after' operations still need optimized codepaths to be implemented; this should be combined with a refactoring of the code which manipulates Data and String values in this way since it is currently spread out in several places and the semantics are not 100% clear.
Note: There remains a fault with case-sensitivity being remembered for reference parameters - they are currently evaluated with the caseSensitive of the callee, rather then when the path was evaluated in the caller.
Note: The 'pure' dofree() code path is currently broken (I thought it might be because it is creating nested arrays when not necessary, but changing that does not seem to fix things - the IDE won't run properly with that code path enabled).
Note: For some reason one of the matchText tests fails without ensuring 'clearuql()' is done in MCVariable::setvalueref. There must be a subtle change somewhere which means the 'v0' undeclared var in the test is not getting cleared whereas it did before.