Performance improvement for expr slots using Mapping-derived class for bindings #28
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR deals with issue #27, and addresses both points in that issue. It addresses performance issues with expr slots in a slot_derivation. The first point in the issue was that bindings are recalculated any time an expr slot is found, and the recalculation occurs even if the bindings are the same. The second point was that when the bindings are calculated, ALL bindings are calculated, whether that binding is used or not. To address this, I created a Bindings class in object_transformer.py that calculates a binding only when requested with the get method. This class is derived from the Mappings class which behaves like a regular dictionary. The code for calculating the bindings is the same as the code that was previously found in ObjectTransformer.map_object() for expr slots, it has been placed in Bindings.get_ctxt_obj_and_dict() and the resulting bindings are cached in Bindings.bindings. Bindings.get_ctxt_obj_and_dict() is called from Bindings.getitem, using only the item requested. Also, eval_utils.eval_expr_with_mapping() has been added, which is the same as eval_expr but a Mapping object is used as the bindings parameter, instead of **kwargs (eval_expr has also been changed to simply call eval_expr_with_mapping).
One problem with this is that the cache keys used by ObjectIndex will be the same for the various different objects passed to ObjectIndex.bless, even if different dictionary keys are passed in from the source object. This means a possibly incorrect cached ProxyObject might be returned. To deal with this the cache is cleared by calling object_index.clear_proxy_object_cache(). This is a bit messy so suggestions for cleaning this up are welcome.
An important thing to keep in mind is that if an unrestricted evaluation is required for executing the code from an expr slot, then ALL bindings/variables are required to execute the code with asteval.Interpreter. This can be found in the except block where bindings.get_ctxt_obj_and_dict() is called (with None as a parameter to specify that ALL bindings are requested).
A simpler solution that is a bit cleaner (but not as good performance-wise) is found in expr-opt-b.
Fixes #27