Speed up accelerator table lookups

When debugging a large program like clang and doing "frame variable
*this", the ValueObject pretty printer is doing hundreds of scoped
FindTypes lookups. The ones that take longest are the ones where the
DWARFDeclContext ends in something like ::Iterator which produces many
false positives that need to be filtered out *after* extracting the
DIEs. This patch demonstrates a way to filter out false positives at
the accerator table lookup step.

With this patch
  lldb clang-10 -o "b EmitFunctionStart" -o r -o "f 2" -o "fr v *this" -b -- ...
goes (in user time) from 5.6s -> 4.8s
or (in wall clock) from 6.9s -> 6.0s.

Differential Revision: https://reviews.llvm.org/D68678

llvm-svn: 374401
This commit is contained in:
Adrian Prantl 2019-10-10 17:59:15 +00:00
parent 715bfa4ef8
commit 418893d8f2
12 changed files with 116 additions and 3 deletions

View File

@ -0,0 +1,7 @@
# There is no guaranteed order in which the linker will order these
# files, so we just have a lot of them to make it unlikely that we hit
# the right one first by pure luck.
CXX_SOURCES := main.cpp a.cpp b.cpp c.cpp d.cpp e.cpp f.cpp g.cpp
include Makefile.rules

View File

@ -0,0 +1,31 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class CPPAcceleratorTableTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@skipUnlessDarwin
@skipIf(debug_info=no_match(["dwarf"]))
def test(self):
"""Test that type lookups fail early (performance)"""
self.build()
logfile = self.getBuildArtifact('dwarf.log')
self.expect('log enable dwarf lookups -f' + logfile)
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
self, 'break here', lldb.SBFileSpec('main.cpp'))
# Pick one from the middle of the list to have a high chance
# of it not being in the first file looked at.
self.expect('frame variable inner_d')
log = open(logfile, 'r')
n = 0
for line in log:
if re.findall(r'[abcdefg]\.o: FindByNameAndTag\(\)', line):
self.assertTrue("d.o" in line)
n += 1
self.assertEqual(n, 1, log)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(A)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(B)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(C)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(D)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(E)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(F)

View File

@ -0,0 +1,2 @@
#include "source.h"
CLASS(G)

View File

@ -0,0 +1,28 @@
#define CLASS(NAME) \
class NAME { \
public: \
struct Inner; \
Inner *i = nullptr; \
}; \
NAME::Inner &getInner##NAME();
CLASS(A)
CLASS(B)
CLASS(C)
CLASS(D)
CLASS(E)
CLASS(F)
CLASS(G)
int main()
{
A::Inner &inner_a = getInnerA();
B::Inner &inner_b = getInnerB();
C::Inner &inner_c = getInnerC();
D::Inner &inner_d = getInnerD();
E::Inner &inner_e = getInnerE();
F::Inner &inner_f = getInnerF();
G::Inner &inner_g = getInnerG();
return 0; // break here
}

View File

@ -0,0 +1,12 @@
#define CLASS(NAME) \
class NAME { \
public: \
class Inner { \
int j = #NAME[0]; \
}; \
Inner *i = nullptr; \
}; \
\
static NAME::Inner inner; \
static NAME obj; \
NAME::Inner &getInner##NAME() { return inner; }

View File

@ -110,6 +110,7 @@ void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
const bool has_qualified_name_hash =
m_apple_types_up->GetHeader().header_data.ContainsAtom(
DWARFMappedHash::eAtomTypeQualNameHash);
const ConstString type_name(context[0].name);
const dw_tag_t tag = context[0].tag;
if (has_tag && has_qualified_name_hash) {
@ -119,12 +120,32 @@ void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
m_apple_types_up->FindByNameAndTagAndQualifiedNameHash(
type_name.GetStringRef(), tag, qualified_name_hash, offsets);
} else if (has_tag) {
return;
}
if (has_tag) {
// When searching for a scoped type (for example,
// "std::vector<int>::const_iterator") searching for the innermost
// name alone ("const_iterator") could yield many false
// positives. By searching for the parent type ("vector<int>")
// first we can avoid extracting type DIEs from object files that
// would fail the filter anyway.
if (!has_qualified_name_hash && (context.GetSize() > 1) &&
(context[1].tag == DW_TAG_class_type ||
context[1].tag == DW_TAG_structure_type)) {
DIEArray class_matches;
m_apple_types_up->FindByName(context[1].name, class_matches);
if (class_matches.empty())
return;
}
if (log)
m_module.LogMessage(log, "FindByNameAndTag()");
m_apple_types_up->FindByNameAndTag(type_name.GetStringRef(), tag, offsets);
} else
m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
return;
}
m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
}
void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {