[DTrace-devel] [PATCH 6/7] htab reduction: symtab
Nick Alcock
nick.alcock at oracle.com
Wed Oct 13 07:06:46 PDT 2021
On 11 Oct 2021, Eugene Loh uttered the following:
> I read through this and I confess I kept losing sight of the forest for
> the trees, but
> Reviewed-by: Eugene Loh <eugene.loh at oracle.com>
> And picky comments as usual:
:)
> On 10/5/21 9:38 AM, Nick Alcock wrote:
>> The symtab is a major performance sink. The symtabs in a running DTrace
>> vary from tiny (most modules) to huge (20,000+ entries in the vmlinux
>> symtab), but all of them are stuffed into fixed-size htabs of size 211,
>> leading to hash chains hundreds long. Combine that with the exponential
> I'm not familiar with this. In what way is it exponential?
We do a name lookup for every symbol extracted from the core kernel to
figure out what module it's in: see dt_prov_fbt.c:populate(). Then, for
each of those, we walk across every module and do a symtab lookup.
That's O(n^2), or O(nm) if you prefer, where m is the number of modules
and n is the number of symbols. Much higher than the O(n) we would
prefer (well, actually I'm now wondering if we should just do something
that returns a single giant array of module names sorted in the same
order as dt_prov_fbt expects: that's bound to be even faster, thuogh
we'd want to keep the name lookup speedups too.)
hmm...
>> - * XXX profile this: possibly introduce a symbol->name mapping for
>> - * first-matching-symbol across all modules.
>> */
>> int
>> dtrace_lookup_by_name(dtrace_hdl_t *dtp, const char *object, const char *name,
> s/XXX/FIXME/
This was a removal :) it's done!
>> diff --git a/libdtrace/dt_symtab.c b/libdtrace/dt_symtab.c
>> @@ -145,8 +142,24 @@ static int dt_symbol_search_cmp(const void *lp, const void *rp)
>> +static uint32_t
>> +dt_symtab_hval(const dt_symbol_t *sym, void *arg)
>> +{
>> + return sym->dts_hval ? sym->dts_hval : str2hval(sym->dts_name, 0);
>> +}
> How about caching the computed value? E.g.,
> if (sym->dts_hval == 0)
> sym->dts_hval = str2hval(sym->dts_name, 0);
> return sym->dts_hval;
> Perhaps such caching is unnecessary, but we're checking sym->dts_hval
> anyhow.
That's unneeded: we already compute dts_hvals for all symbols at
insertion time (see dt_symbol_insert). This bit serves to compute the
hval for the template used to do lookups, and that is only ever hashed
once (see dt_symbol_by_name and callers), so there's no need to cache it.
>> @@ -155,12 +168,10 @@ dt_symtab_create(void)
>>
>> memset(symtab, 0, sizeof(struct dt_symtab));
>>
>> - symtab->dtst_symbuckets = _dtrace_strbuckets;
>> - symtab->dtst_syms_by_name = calloc(symtab->dtst_symbuckets,
>> - sizeof(struct dt_symbol *));
>> + if (!dtp->dt_kernsyms)
>> + dtp->dt_kernsyms = dt_htab_create(dtp, &dt_symtab_htab_ops);
>>
>> - if (symtab->dtst_syms_by_name == NULL) {
>> - free(symtab->dtst_syms_by_name);
>> + if (dtp->dt_kernsyms == NULL) {
>> free(symtab);
>> return NULL;
>> }
> We check twice if dtp->dt_kernsyms is NULL: once to see if
> dt_htab_create() should be called and again to see if the call was
> successful. How about using syntactically similar expressions to check
> semantically identical checks?
Good point. We can also initialize dt_kernsyms first, avoiding the free
in the error path.
>> @@ -207,36 +229,28 @@ dt_symbol_insert(dt_symtab_t *symtab, const char *name,
>>
>> if ((dtsp = malloc(sizeof(dt_symbol_t))) == NULL)
>> - return NULL;
>> + goto oom;
>>
>> if (symtab->dtst_num_range >= symtab->dtst_num_range_alloc)
>> - if (dt_symtab_grow_ranges(symtab) == NULL) {
>> - free(dtsp);
>> - return NULL;
>> - }
>> + if (dt_symtab_grow_ranges(symtab) == NULL)
>> + goto oom;
> oom? So if the dtsp=malloc() is successful but the
> dt_symtab_grow_ranges() is not, we go to oom, where we free the
> uninitialized pointer dtsp->dts_name. Not a good idea. I don't think
> there's much value in channeling all those code paths through a common
> oom exit path. The various "return NULL" statements seem fine to me.
Oops! Blasted hard-to-test error paths. Nicely spotted.
>> - if (size > 0) {
>> - symtab->dtst_ranges[symtab->dtst_num_range].dtsr_sym = dtsp;
>> - symtab->dtst_num_range++;
>> - }
>> @@ -244,36 +258,48 @@ dt_symbol_insert(dt_symtab_t *symtab, const char *name,
>> + if (size > 0) {
>> + symtab->dtst_ranges[symtab->dtst_num_range].dtsr_sym = dtsp;
>> + symtab->dtst_num_range++;
>> + }
> Okay, so that code is moving, but while we're at it might as well
> perhaps change to a more common idiom:
> if (size > 0)
> symtab->dtst_range[symtab->dtst_num_range++].dtsr_sym = dtsp;
Ack.
More information about the DTrace-devel
mailing list