[PATCH 2 of 6 v2] util: add an elapsed time wrapper
Simon Farnsworth
simonfar at fb.com
Fri Feb 3 22:26:07 UTC 2017
On 03/02/2017 00:54, Bryan O'Sullivan wrote:
> On Thu, Feb 2, 2017 at 11:32 AM, Bryan O'Sullivan <bos at serpentine.com
> <mailto:bos at serpentine.com>> wrote:
>
>
> To be honest, this seems like a heavily over-engineered approach to me.
>
>
> I don't like giving such vague feedback, but I was hopping off the
> shuttle this morning and couldn't write anything more concrete at the
> time. Sorry about that.
>
> I also feel somewhat apologetic about seagulling in abruptly with a
> critique after months and months of silence, but ... hi?
>
> I wanted to substantiate my sense of unease with this patchset. Here's
> what I came up with.
>
> First, you'll want to take a look at my different approach, which I just
> sent out a moment ago. (It's not cooked, and I forgot to add the "RFC"
> tag, but you get the idea.)
>
> Next, performance. Here's a little
> benchmark: http://pastebin.com/f9eFEWiq
> <https://urldefense.proofpoint.com/v2/url?u=http-3A__pastebin.com_f9eFEWiq&d=DwMFaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=mEgSWILcY4c4W3zjApBQLA&m=Z_ILapZ4MOSTKVH08OWF7ibTePT7tfFDPNqh5YnzmFQ&s=iN5BYr4m1lyQkPVl1UVCnw43-0a3kpVooUdu2lfAGzU&e=>
>
> On my laptop, base Mercurial runs it in 0.15 seconds.
> With performance logging enabled, your patchset takes 0.36 seconds.
> And my patchset takes 0.16, with no need to enable or disable anything.
>
> To be clear, the performance numbers should not be persuasive, because a
> typical invocation of Mercurial will call ui.write maybe 1% as often as
> the benchmark does. But what they do is give me another angle from which
> to look at "this doesn't feel right to me".
>
I'd assumed that with Mercurial encouraging extension authors to wrap
functions, the overhead wasn't significant. Your patchset and message
had me run a couple of experiments on my Mac, using the timeit module,
and a trivial function:
def func():
print "Hello"
This is both trivial, and on the sort of complexity order of the
functions I'm trying to measure.
The baseline I got was that 1,000,000 invocations of this function take
2.0 seconds elapsed time.
I then added if True to the function, to get a sense of the pain of
making it conditional:
def func():
if True:
print "Hello"
This takes 2.3 seconds for the same million repetitions.
I then restored the unconditional version of func(); using a simple
wrapper that just does if True: func(), I get 2.4 seconds, while a
class-based unconditional wrapper is 2.7 seconds:
class wrapper:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
self.func(*args, **kwargs)
It looks to me like the conditional actually adds significant pain in
its own right, almost as much as the complex class based wrapper does.
Assuming the community doesn't object strongly, I'll redo to be
unconditionally measuring time (and only conditionally logging it), as
the overhead of conditionality seems excessive for this purpose.
> Your patch adds a bunch of allocation and indirection on what for this
> benchmark is a fast path, which is why it appears so costly.
>
> Abstractions in Python have a heavy runtime cost and of course the usual
> "now I have an abstraction to understand" overhead, and so you have to
> be particularly judicious in creating and using them.
>
> The above approach of indirection via a wrapper object doesn't meet my
> personal "abstraction that pays for the cost of understanding and using
> it" bar, especially as the alternative approach in my patchset is both
> simple to understand and has almost no overhead.
>
> Also, the way that you're conditionally recording time used in both this
> and later patches (for crecord and extdiff) strikes me as unnecessarily
> complex. My patches demonstrate that doing so unconditionally is simpler
> and has inconsequential overhead.
I definitely want the reporting to be conditional - if you're not going
to use the time, we shouldn't output it. My mental model for Python
costs was wrong (and is now corrected), and I'll redo the time gathering
accordingly.
From my point of view, the goals are:
1. Establish groups of "not Mercurial's fault" slowness - stdio,
extdiff, editor invoked for histedit etc. This combines with FB infra to
let us focus on cases where Mercurial code is the bottleneck.
2. Do so in such a way that users not on FB infra aren't affected
unless they choose to be, even if they're using things like logtoprocess
today.
It looks like I met goal 2 at the expense of goal 1, and I'm going to
need to rework to get a nicer compromise in place between the goals.
--
Simon Farnsworth
More information about the Mercurial-devel
mailing list