revset: optimize nameset.{min,max}

Summary:
Before this change, nameset's min, max use the base class implementation, which
can be undesirably slow sometimes, while `first` and `last` remain fast
since they call into Rust logic which understands better about fast paths.

Optimize `min`, `max` by converting them to `first` or `last` if possible.

Before. Note `min()` is slow:

  In [3]: s=repo.revs('parents(1:10000)')
  warning: ':' is deprecated; use '::' instead. https://fburl.com/hgcolon

  In [4]: s
  Out[4]: <nameset+ <spans [06b96ec2a8b60d984606f36c30d3dbc899d804df:d6867d22c1ad3cf6e384332093e1d0de1fa86cb5+0:9999]>>

  In [5]: %time s.min()
  Out[5]: CPU times: user 51.5 ms, sys: 8.25 ms, total: 59.7 ms
  Wall time: 57.8 ms
  0

  In [6]: %time s.max()
  Out[6]: CPU times: user 64 µs, sys: 15 µs, total: 79 µs
  Wall time: 84.6 µs
  9999

  In [7]: %time s.first()
  Out[7]: CPU times: user 39 µs, sys: 9 µs, total: 48 µs
  Wall time: 50.8 µs
  0

  In [8]: %time s.last()
  Out[8]: CPU times: user 62 µs, sys: 0 ns, total: 62 µs
  Wall time: 66.5 µs
  9999

After:

  In [2]: %time s.min()
  CPU times: user 0 ns, sys: 902 µs, total: 902 µs
  Wall time: 907 µs
  Out[2]: 0

  In [3]: %time s.max()
  CPU times: user 551 µs, sys: 0 ns, total: 551 µs
  Wall time: 557 µs
  Out[3]: 9999

  In [4]: %time s.first()
  CPU times: user 50 µs, sys: 9 µs, total: 59 µs
  Wall time: 62 µs
  Out[4]: 0

  In [5]: %time s.last()
  CPU times: user 49 µs, sys: 9 µs, total: 58 µs
  Wall time: 62 µs
  Out[5]: 9999

Reviewed By: sfilipco

Differential Revision: D26182241

fbshipit-source-id: dedf3788c52d22c6b63ae60847cc0667616f11d2
This commit is contained in:
Jun Wu 2021-02-05 11:50:59 -08:00 committed by Facebook GitHub Bot
parent cc123cc1ce
commit d6838099d5

View File

@ -857,6 +857,36 @@ class nameset(abstractsmartset):
if node: if node:
return self._torev(node) return self._torev(node)
def min(self):
hints = self._set.hints()
if hints.get("desc"):
result = self._set.last()
elif hints.get("asc"):
result = self._set.first()
else:
result = None
if result is None:
result = min(self)
else:
result = self._torev(result)
self.min = lambda: result
return result
def max(self):
hints = self._set.hints()
if hints.get("desc"):
result = self._set.first()
elif hints.get("asc"):
result = self._set.last()
else:
result = None
if result is None:
result = max(self)
else:
result = self._torev(result)
self.max = lambda: result
return result
def _setop(self, other, op): def _setop(self, other, op):
# try to use native set operations as fast paths # try to use native set operations as fast paths