Skip to content

Commit

Permalink
fix: performance of ufunc resolution for non-nominal signatures (#3030)
Browse files Browse the repository at this point in the history
  • Loading branch information
agoose77 committed Mar 4, 2024
1 parent d7556f9 commit ac97e2a
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 28 deletions.
45 changes: 24 additions & 21 deletions src/awkward/_behavior.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,29 +100,32 @@ def find_ufunc_generic(


def find_ufunc(behavior: Mapping | None, signature: tuple) -> UfuncLike | None:
if all(s is not None for s in signature):
behavior = overlay_behavior(behavior)
if any(s is None for s in signature):
return None

behavior = overlay_behavior(behavior)

# Special case all strings or hashable types.
# Try and fast-path the lookup
try:
return behavior[signature]
except KeyError:
# We didn't find an exact overload, and we won't find any!
if all(isinstance(x, str) for x in signature):
return behavior.get(signature)
else:
for key, custom in behavior.items():
if (
isinstance(key, tuple)
and len(key) == len(signature)
and key[0] == signature[0]
and all(
k == s
or (
isinstance(k, type)
and isinstance(s, type)
and issubclass(s, k)
)
for k, s in zip(key[1:], signature[1:])
)
):
return custom
return None

# Fall back on linear search (first-wins)
for key, custom in behavior.items():
if (
isinstance(key, tuple)
and len(key) == len(signature)
and key[0] == signature[0]
and all(
k == s
or (isinstance(k, type) and isinstance(s, type) and issubclass(s, k))
for k, s in zip(key[1:], signature[1:])
)
):
return custom
return None


Expand Down
22 changes: 15 additions & 7 deletions src/awkward/_connect/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,23 +207,29 @@ def _array_ufunc_adjust_apply(
)


def _array_ufunc_signature(ufunc, inputs):
signature = [ufunc]
def _array_ufunc_signature(ufunc, inputs) -> tuple[Any, ...] | None:
signature = []
has_seen_nominal_type = False
for x in inputs:
if isinstance(x, ak.contents.Content):
record_name, list_name = x.parameter("__record__"), x.parameter("__list__")
if record_name is not None:
signature.append(record_name)
has_seen_nominal_type = True
elif list_name is not None:
signature.append(list_name)
has_seen_nominal_type = True
elif isinstance(x, NumpyArray):
signature.append(x.dtype.type)
else:
signature.append(None)
else:
signature.append(type(x))

return tuple(signature)
if has_seen_nominal_type:
return (ufunc, *signature)
else:
return None


def _array_ufunc_categorical(
Expand Down Expand Up @@ -364,10 +370,12 @@ def action(inputs, **ignore):
assert len(contents) >= 1

signature = _array_ufunc_signature(ufunc, inputs)
# Do we have a custom ufunc (an override of the given ufunc)?
custom = find_ufunc(behavior, signature)
if custom is not None:
return _array_ufunc_adjust(custom, inputs, kwargs, behavior)
# Should we allow ufunc overloads for this signature?
if signature is not None:
# Do we have a custom ufunc (an override of the given ufunc)?
custom = find_ufunc(behavior, signature)
if custom is not None:
return _array_ufunc_adjust(custom, inputs, kwargs, behavior)

# Do we have any categoricals?
if any(
Expand Down

0 comments on commit ac97e2a

Please sign in to comment.