[lldb-dev] lldb.frame.EvaluateExpression slows down when called a lot

jingham at apple.com jingham at apple.com
Fri Apr 18 09:28:12 PDT 2014


The cluster manager's job is to make sure that you can hand out an SBValue for one sub-element of a "root value" and have it keep alive all the parent objects.  For instance you could do:

sub_element = my_frame.GetValueForVariablePath("foo.bar.baz->lala->dada")

and for sub_element to be valid it needs to keep alive all the parent elements "foo", "foo.bar", etc.  Since nothing else is referencing them, we use this "ClusterManager" to keep them alive.  There's one ClusterManager for each root value object, and all the sub-elements get registered with it.

It currently keeps the sub-elements in a std::vector, and when you make a new shared pointer for a member (either because you are adding an element or getting a shared pointer to an extant element) it has to look it up in its list.  It looks like you are making root objects with many sub-objects?  If so, then we're running into what is probably a linear search in std::vector::find.  We can try switching this over to a map.  That will make adding elements slower, and make the cluster manager a little bigger, but it will make looking them up and managing them faster.

Jim



On Apr 18, 2014, at 5:35 AM, Scott Knight <knightsc at gmail.com> wrote:

> I tried to put together a small sample that was similar to what ruby is actually doing. In the attached sample I set up a similar structure vm with list of pages and each page had a list of objects. The only thing on the object is a int flag that I just increment so when I print it out in python and can gauge progress. In the python script then I try to time each call in the loop and print that out as well. The time it takes each time through the loop seems to get higher and higher even though FindFirstGlobalVariable is only called once.
> 
> Thanks,
> Scott Knight
> 
> 
> On Thu, Apr 17, 2014 at 8:50 PM, Greg Clayton <gclayton at apple.com> wrote:
> How many objects are we talking about here?
> 
> On Apr 17, 2014, at 5:46 PM, Scott Knight <knightsc at gmail.com> wrote:
> 
> > So when I call my "ruby objects" command it will create an instance of my RubyObjects class and call invoke on it once. In the invoke is where I have this call
> >
> >     self.ruby_current_vm = lldb.value(lldb.target.FindFirstGlobalVariable('ruby_current_vm'))
> >
> > Then in print_stats it calls into all_objects which does this
> >
> >
> >   def all_objects (self):
> >     self.heaps_used = self.ruby_current_vm.objspace.heap_pages.used
> >
> >     for i in xrange(self.heaps_used):
> >       page = self.ruby_current_vm.objspace.heap_pages.sorted[i]
> >       print "page %i" % i
> >
> >       for j in xrange(page.limit):
> >         rvalue = page.start[j].__getattr__('as')
> >         flags = rvalue.basic.flags
> >         yield rvalue, flags
> >
> > So self.ruby_current_vm should already be reused. If I stop the loop through page.limit everything is fast, but with the code within that second for loop things just slow down more and more.
> >
> > -Scott
> >
> >
> > On Thu, Apr 17, 2014 at 8:40 PM, Greg Clayton <gclayton at apple.com> wrote:
> > Since you are dealing with a global, you are not tied to a stack frame, so you should be able to cache this value and re-use it:
> >
> >
> >
> >     if not self.ruby_current_vm:
> >         self.ruby_current_vm = lldb.value(lldb.target.FindFirstGlobalVariable('ruby_current_vm'))
> >
> > Then you should be able to use this over and over without re-fetching it. And you should be really fast. Each time you fetch a variable from SBTarget::FindFirstGlobalVariable(), it re-wraps the variable in a new VariableObjectSP which has its own cluster manager. Why? Because you might do something like:
> >
> > f = lldb.value(lldb.target.FindFirstGlobalVariable('g_ptr'))
> >
> > f = f.a.b.c.d
> >
> > Now we need a reference to the ValueObjectSP for "g_ptr" (the underlying variable that roots the entire expression) to stay alive as long as anyone has a reference to anything that is a child of "g_ptr". Here "f" now reference "g_ptr->a.b.c.d", so any value in this chain is correctly reference counted using a ClusterMananger that keeps all of them alive as long as someone has a reference to any of them.
> >
> > So if you reuse your "self.ruby_current_vm", you should only have a single ClusterManager and they should stay shared as long as you use them. Currently you are re-creating the root with each call and then referencing a bunch of children which adds new shared references to each cluster manager.
> >
> > Let me know how reusing the one instance goes.
> >
> > Greg
> >
> >
> > On Apr 17, 2014, at 5:22 PM, Scott Knight <knightsc at gmail.com> wrote:
> >
> > > I attached the instruments trace here in case it might be helpful. Seems like a lot of time is spent in the ClusterManager. It seems like thats called from all the ValueObject. I do realize that I'm getting values over and over again in a loop, but it seems to just take longer each time through the loop. I also attached the python script I'm using in the zip file as well.
> > >
> > > -Scott
> > >
> > >
> > > On Thu, Apr 17, 2014 at 7:56 PM, Greg Clayton <gclayton at apple.com> wrote:
> > > No idea. If you are running this on MacOSX, I would run a time profile in instruments on it and see what is going on.
> > >
> > > On Apr 17, 2014, at 4:32 PM, Scott Knight <knightsc at gmail.com> wrote:
> > >
> > > > Any of idea why making that call over and over again would seem to slow down over time?
> > > >
> > > > -Scott
> > > >
> > > > On Apr 17, 2014 7:29 PM, "Greg Clayton" <gclayton at apple.com> wrote:
> > > > Yep, it is the python keyword... You currently need to use your workaround:
> > > >
> > > > rvalue.__getattr__("as")
> > > >
> > > > Glad we found it and that there is nothing wrong with the API (we are finding children of anonymous unions, phew!).
> > > >
> > > > Greg
> > > >
> > > > On Apr 17, 2014, at 3:46 PM, Scott Knight <knightsc at gmail.com> wrote:
> > > >
> > > > > typedef struct RVALUE {
> > > > >     union {
> > > > >       struct {
> > > > >           VALUE flags;                /* always 0 for freed obj */
> > > > >           struct RVALUE *next;
> > > > >       } free;
> > > > >       struct RBasic  basic;
> > > > >       struct RObject object;
> > > > >       struct RClass  klass;
> > > > >       struct RFloat  flonum;
> > > > >       struct RString string;
> > > > >       struct RArray  array;
> > > > >       struct RRegexp regexp;
> > > > >       struct RHash   hash;
> > > > >       struct RData   data;
> > > > >       struct RTypedData   typeddata;
> > > > >       struct RStruct rstruct;
> > > > >       struct RBignum bignum;
> > > > >       struct RFile   file;
> > > > >       struct RNode   node;
> > > > >       struct RMatch  match;
> > > > >       struct RRational rational;
> > > > >       struct RComplex complex;
> > > > >       struct {
> > > > >           struct RBasic basic;
> > > > >           VALUE v1;
> > > > >           VALUE v2;
> > > > >           VALUE v3;
> > > > >       } values;
> > > > >     } as;
> > > > > #if GC_DEBUG
> > > > >     const char *file;
> > > > >     VALUE line;
> > > > > #endif
> > > > > } RVALUE;
> > > > >
> > > >
> > >
> > >
> > > <lldb-cpu-time.zip>
> >
> >
> 
> 
> <TestLoopSpeed.zip>_______________________________________________
> lldb-dev mailing list
> lldb-dev at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev




More information about the lldb-dev mailing list