Skip to content

Commit

Permalink
optimize simple get/set operations in __getitem__ and __setitem__
Browse files Browse the repository at this point in the history
these basic operations are very common so optimisation is warranted here.
micro-benchmarks show a 3× speed-up for __getitem__ on a small dictionary,
and a 2× speed-up for __setitem__.

before:

    $ python -m timeit -r10 -s 'import sanest; d = sanest.dict(a=1, b=2, c=3)' 'd["a"]'
    1000000 loops, best of 10: 1.55 usec per loop

    $ python -m timeit -r10 -s 'import sanest; d = sanest.dict(a=1, b=2, c=3)' 'd["a"] = "foo"'
    100000 loops, best of 10: 2.24 usec per loop

after:

    $ python -m timeit -r10 -s 'import sanest; d = sanest.dict(a=1, b=2, c=3)' 'd["a"]'
    1000000 loops, best of 10: 0.409 usec per loop

    $ python -m timeit -r10 -s 'import sanest; d = sanest.dict(a=1, b=2, c=3)' 'd["a"] = "foo"'
    1000000 loops, best of 10: 1.05 usec per loop

due to earlier refactoring, previous "fast code paths" were dropped in these commits:

    24e439e __getitem__
    d3aa993 __setitem__

...but now it is time to optimise those basic cases again. :)
  • Loading branch information
wbolster committed Jul 2, 2017
1 parent a3830ad commit 56c9bae
Showing 1 changed file with 24 additions and 8 deletions.
32 changes: 24 additions & 8 deletions src/sanest/sanest.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,16 @@ def __getitem__(self, path_like):
"""
Look up the item that ``path_like`` (with optional type) points to.
"""
key_or_index, path, type = parse_path_like_with_type(path_like)
value = resolve_path(self._data, path)
if type is not None:
check_type(value, type=type, path=path)
if typeof(path_like) is self._key_or_index_type: # fast path
try:
value = self._data[path_like]
except LookupError as exc:
raise typeof(exc)([path_like]) from None
else:
key_or_index, path, type = parse_path_like_with_type(path_like)
value = resolve_path(self._data, path)
if type is not None:
check_type(value, type=type, path=path)
if typeof(value) in CONTAINER_TYPES:
value = wrap(value, check=False)
return value
Expand All @@ -436,10 +442,16 @@ def __setitem__(self, path_like, value):
"""
Set the item that ``path_like`` (with optional type) points to.
"""
key_or_index, path, type = parse_path_like_with_type(path_like)
value = clean_value(value, type=type)
obj, key_or_index = resolve_path(
self._data, path, partial=True, create=True)
if typeof(path_like) is self._key_or_index_type: # fast path
obj = self._data
key_or_index = path_like
path = [key_or_index]
value = clean_value(value)
else:
key_or_index, path, type = parse_path_like_with_type(path_like)
value = clean_value(value, type=type)
obj, key_or_index = resolve_path(
self._data, path, partial=True, create=True)
try:
obj[key_or_index] = value
except IndexError as exc: # list assignment can fail
Expand Down Expand Up @@ -556,6 +568,8 @@ class dict(
"""
__slots__ = ('_data',)

_key_or_index_type = str

def __init__(self, *args, **kwargs):
self._data = {}
if args or kwargs:
Expand Down Expand Up @@ -841,6 +855,8 @@ class list(
"""
__slots__ = ('_data',)

_key_or_index_type = int

def __init__(self, *args):
self._data = []
if args:
Expand Down

0 comments on commit 56c9bae

Please sign in to comment.