Skip to content

Commit

Permalink
Add __slots__ class property to all compiler IR entities. (#138)
Browse files Browse the repository at this point in the history
This should reduce the memory footprint of compilation since the IR entities will be represented more compactly, and should also produce a slight performance improvement because of the more-constrained object layout.

Details on motivation and effects:
https://docs.python.org/3/reference/datamodel.html#slots
https://stackoverflow.com/questions/472000/usage-of-slots
  • Loading branch information
obi1kenobi committed Oct 8, 2018
1 parent 78939f7 commit be7f890
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 5 deletions.
26 changes: 26 additions & 0 deletions graphql_compiler/compiler/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
class QueryRoot(BasicBlock):
"""The starting object of the query to be compiled."""

__slots__ = ('start_class',)

def __init__(self, start_class):
"""Construct a QueryRoot object that starts querying at the specified class name.
Expand Down Expand Up @@ -56,6 +58,8 @@ def to_gremlin(self):
class CoerceType(BasicBlock):
"""A special type of filter that discards any data that is not of the specified set of types."""

__slots__ = ('target_class',)

def __init__(self, target_class):
"""Construct a CoerceType object that filters out any data that is not of the given types.
Expand Down Expand Up @@ -91,6 +95,8 @@ def to_gremlin(self):
class ConstructResult(BasicBlock):
"""A transformation of the data into a new form, for output."""

__slots__ = ('fields',)

def __init__(self, fields):
"""Construct a ConstructResult object that maps the given field names to their expressions.
Expand Down Expand Up @@ -157,6 +163,8 @@ def to_gremlin(self):
class Filter(BasicBlock):
"""A filter that ensures data matches a predicate expression, and discards all other data."""

__slots__ = ('predicate',)

def __init__(self, predicate):
"""Create a new Filter with the specified Expression as a predicate."""
super(Filter, self).__init__(predicate)
Expand Down Expand Up @@ -186,6 +194,8 @@ def to_gremlin(self):
class MarkLocation(BasicBlock):
"""A block that assigns a name to a given location in the query."""

__slots__ = ('location',)

def __init__(self, location):
"""Create a new MarkLocation at the specified Location.
Expand Down Expand Up @@ -213,6 +223,8 @@ def to_gremlin(self):
class Traverse(BasicBlock):
"""A block that encodes a traversal across an edge, in either direction."""

__slots__ = ('direction', 'edge_name', 'optional', 'within_optional_scope')

def __init__(self, direction, edge_name, optional=False, within_optional_scope=False):
"""Create a new Traverse block in the given direction and across the given edge.
Expand Down Expand Up @@ -296,6 +308,8 @@ def to_gremlin(self):
class Recurse(BasicBlock):
"""A block for recursive traversal of an edge, collecting all endpoints along the way."""

__slots__ = ('direction', 'edge_name', 'depth', 'within_optional_scope')

def __init__(self, direction, edge_name, depth, within_optional_scope=False):
"""Create a new Recurse block which traverses the given edge up to "depth" times.
Expand Down Expand Up @@ -360,6 +374,8 @@ def to_gremlin(self):
class Backtrack(BasicBlock):
"""A block that specifies a return to a given Location in the query."""

__slots__ = ('location', 'optional')

def __init__(self, location, optional=False):
"""Create a new Backtrack block, returning to the given location in the query.
Expand Down Expand Up @@ -409,6 +425,8 @@ class OutputSource(MarkerBlock):
See the comment on the @output_source directive in schema.py on why this is necessary.
"""

__slots__ = ()

def validate(self):
"""Validate the OutputSource block. An OutputSource block is always valid in isolation."""
pass
Expand All @@ -417,6 +435,8 @@ def validate(self):
class Fold(MarkerBlock):
"""A marker for the start of a @fold context."""

__slots__ = ('fold_scope_location',)

def __init__(self, fold_scope_location):
"""Create a new Fold block rooted at the given location."""
super(Fold, self).__init__(fold_scope_location)
Expand All @@ -433,6 +453,8 @@ def validate(self):
class Unfold(MarkerBlock):
"""A marker for the end of a @fold context."""

__slots__ = ()

def validate(self):
"""Unfold blocks are always valid in isolation."""
pass
Expand All @@ -444,6 +466,8 @@ class EndOptional(MarkerBlock):
Optional scope is entered through an optional Traverse Block.
"""

__slots__ = ()

def validate(self):
"""In isolation, EndOptional blocks are always valid."""
pass
Expand All @@ -452,6 +476,8 @@ def validate(self):
class GlobalOperationsStart(MarkerBlock):
"""Marker block for the end of MATCH traversals, and the beginning of global operations."""

__slots__ = ()

def validate(self):
"""In isolation, GlobalOperationsStart blocks are always valid."""
pass
8 changes: 8 additions & 0 deletions graphql_compiler/compiler/compiler_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
class CompilerEntity(object):
"""An abstract compiler entity. Can represent things like basic blocks and expressions."""

__slots__ = ('_print_args', '_print_kwargs')

def __init__(self, *args, **kwargs):
"""Construct a new CompilerEntity."""
self._print_args = args
Expand Down Expand Up @@ -60,6 +62,8 @@ def to_gremlin(self):
class Expression(CompilerEntity):
"""An expression that produces a value in the GraphQL compiler."""

__slots__ = ()

def visit_and_update(self, visitor_fn):
"""Create an updated version (if needed) of the Expression via the visitor pattern.
Expand All @@ -86,6 +90,8 @@ def visit_and_update(self, visitor_fn):
class BasicBlock(CompilerEntity):
"""A basic operation block of the GraphQL compiler."""

__slots__ = ()

def visit_and_update_expressions(self, visitor_fn):
"""Create an updated version (if needed) of the BasicBlock via the visitor pattern.
Expand Down Expand Up @@ -113,6 +119,8 @@ def visit_and_update_expressions(self, visitor_fn):
class MarkerBlock(BasicBlock):
"""A block that is used to mark that a context-affecting operation with no output happened."""

__slots__ = ()

def to_gremlin(self):
"""Return the Gremlin representation of the block, which should almost always be empty.
Expand Down
32 changes: 27 additions & 5 deletions graphql_compiler/compiler/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Literal(Expression):
Think long and hard about the above before allowing literals in user-supplied GraphQL!
"""

__slots__ = ('value',)

def __init__(self, value):
"""Construct a new Literal object with the given value."""
super(Literal, self).__init__(value)
Expand Down Expand Up @@ -106,6 +108,8 @@ def _to_output_code(self):
class Variable(Expression):
"""A variable for a parameterized query, to be filled in at runtime."""

__slots__ = ('variable_name', 'inferred_type')

def __init__(self, variable_name, inferred_type):
"""Construct a new Variable object for the given variable name.
Expand Down Expand Up @@ -203,6 +207,8 @@ def __ne__(self, other):
class LocalField(Expression):
"""A field at the current position in the query."""

__slots__ = ('field_name',)

def __init__(self, field_name):
"""Construct a new LocalField object that references a field at the current position."""
super(LocalField, self).__init__(field_name)
Expand Down Expand Up @@ -240,6 +246,8 @@ def to_gremlin(self):
class SelectEdgeContextField(Expression):
"""An edge field drawn from the global context, for use in a SELECT WHERE statement."""

__slots__ = ('location',)

def __init__(self, location):
"""Construct a new SelectEdgeContextField object that references an edge field.
Expand Down Expand Up @@ -287,6 +295,8 @@ def to_gremlin(self):
class ContextField(Expression):
"""A field drawn from the global context, e.g. if selected earlier in the query."""

__slots__ = ('location',)

def __init__(self, location):
"""Construct a new ContextField object that references a field from the global context.
Expand Down Expand Up @@ -345,6 +355,8 @@ def to_gremlin(self):
class OutputContextField(Expression):
"""A field used in ConstructResult blocks to output data from the global context."""

__slots__ = ('location', 'field_type')

def __init__(self, location, field_type):
"""Construct a new OutputContextField object for the field at the given location.
Expand Down Expand Up @@ -443,6 +455,8 @@ def __ne__(self, other):
class FoldedOutputContextField(Expression):
"""An expression used to output data captured in a @fold scope."""

__slots__ = ('fold_scope_location', 'field_type')

def __init__(self, fold_scope_location, field_type):
"""Construct a new FoldedOutputContextField object for this folded field.
Expand Down Expand Up @@ -527,6 +541,8 @@ class ContextFieldExistence(Expression):
Useful to determine whether e.g. a field at the end of an optional edge is defined or not.
"""

__slots__ = ('location',)

def __init__(self, location):
"""Construct a new ContextFieldExistence object for a vertex field from the global context.
Expand Down Expand Up @@ -572,8 +588,9 @@ def _validate_operator_name(operator, supported_operators):
class UnaryTransformation(Expression):
"""An expression that modifies an underlying expression with a unary operator."""

SUPPORTED_OPERATORS = frozenset(
{u'size'})
SUPPORTED_OPERATORS = frozenset({u'size'})

__slots__ = ('operator', 'inner_expression')

def __init__(self, operator, inner_expression):
"""Construct a UnaryExpression that modifies the given inner expression."""
Expand Down Expand Up @@ -638,9 +655,12 @@ def to_gremlin(self):
class BinaryComposition(Expression):
"""An expression created by composing two expressions together."""

SUPPORTED_OPERATORS = frozenset(
{u'=', u'!=', u'>=', u'<=', u'>', u'<', u'+', u'||', u'&&', u'contains', u'intersects',
u'has_substring', u'LIKE', u'INSTANCEOF'})
SUPPORTED_OPERATORS = frozenset({
u'=', u'!=', u'>=', u'<=', u'>', u'<', u'+', u'||', u'&&',
u'contains', u'intersects', u'has_substring', u'LIKE', u'INSTANCEOF',
})

__slots__ = ('operator', 'left', 'right')

def __init__(self, operator, left, right):
"""Construct an expression that connects two expressions with an operator.
Expand Down Expand Up @@ -764,6 +784,8 @@ def to_gremlin(self):
class TernaryConditional(Expression):
"""A ternary conditional expression, returning one of two expressions depending on a third."""

__slots__ = ('predicate', 'if_true', 'if_false')

def __init__(self, predicate, if_true, if_false):
"""Construct an expression that evaluates a predicate and returns one of two results.
Expand Down

0 comments on commit be7f890

Please sign in to comment.