Skip to content

Commit 21d3212

Browse files
erraelchrisbra
authored andcommitted
patch 9.0.1842: Need more accurate profiling
Problem: Need more accurate profiling Solution: Improve profiling results closes: #12192 Reduce overhead of checking if a function should be profiled, by caching results of checking (which are done with regexp). Cache uf_hash for uf_name in ufunc_T. Cache cleared when regexps are changed. Break at first match for has_profiling lookup. Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Ernie Rael <errael@raelity.com>
1 parent 9d093fd commit 21d3212

8 files changed

Lines changed: 133 additions & 7 deletions

File tree

src/debugger.c

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,35 @@ static int has_expr_breakpoint = FALSE;
528528
#ifdef FEAT_PROFILE
529529
// Profiling uses file and func names similar to breakpoints.
530530
static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL};
531+
532+
// Profiling caches results of regexp lookups for function/script name.
533+
#define N_PROF_HTAB 2
534+
static hashtab_T prof_cache[N_PROF_HTAB];
535+
#define PROF_HTAB_FUNCS 0
536+
#define PROF_HTAB_FILES 1
537+
static int prof_cache_initialized;
538+
typedef struct profentry_S
539+
{
540+
char pen_flags; // cache data booleans: profiling, forceit
541+
char_u pen_name[1]; // actually longer
542+
} profentry_T;
543+
#define PEN_FLAG_PROFILING 1
544+
#define PEN_FLAG_FORCEIT 2
545+
#define PEN_SET_PROFILING(pe) ((pe)->pen_flags |= PEN_FLAG_PROFILING)
546+
#define PEN_SET_FORCEIT(pe) ((pe)->pen_flags |= PEN_FLAG_FORCEIT)
547+
#define PEN_IS_PROFILING(pe) (((pe)->pen_flags & PEN_FLAG_PROFILING) != 0)
548+
#define PEN_IS_FORCEIT(pe) (((pe)->pen_flags & PEN_FLAG_FORCEIT) != 0)
549+
550+
#define PE2HIKEY(pe) ((pe)->pen_name)
551+
#define HIKEY2PE(p) ((profentry_T *)((p) - (offsetof(profentry_T, pen_name))))
552+
#define HI2PE(hi) HIKEY2PE((hi)->hi_key)
553+
554+
static void prof_clear_cache(void);
555+
#define PROF_CLEAR_CACHE(gap) do {if ((gap) == &prof_ga) prof_clear_cache();} while (0)
556+
// Can enable to get some info about profile caching
557+
// #define PROF_CACHE_LOG
558+
#else
559+
#define PROF_CLEAR_CACHE(gap) do {} while (0)
531560
#endif
532561
#define DBG_FUNC 1
533562
#define DBG_FILE 2
@@ -708,6 +737,7 @@ ex_breakadd(exarg_T *eap)
708737
++debug_tick;
709738
}
710739
++gap->ga_len;
740+
PROF_CLEAR_CACHE(gap);
711741
}
712742
}
713743
else
@@ -845,6 +875,7 @@ ex_breakdel(exarg_T *eap)
845875
if (!del_all)
846876
break;
847877
}
878+
PROF_CLEAR_CACHE(gap);
848879

849880
// If all breakpoints were removed clear the array.
850881
if (gap->ga_len == 0)
@@ -899,17 +930,102 @@ dbg_find_breakpoint(
899930
}
900931

901932
#if defined(FEAT_PROFILE) || defined(PROTO)
933+
#if defined(PROF_CACHE_LOG)
934+
static int count_lookups[2];
935+
#endif
902936
/*
903937
* Return TRUE if profiling is on for a function or sourced file.
938+
* Cache the results of debuggy_find().
939+
* Cache is cleared whenever prof_ga.ga_len is changed.
904940
*/
905941
int
906942
has_profiling(
907943
int file, // TRUE for a file, FALSE for a function
908944
char_u *fname, // file or function name
909-
int *fp) // return: forceit
945+
int *fp, // return: forceit
946+
hash_T *hashp) // use/return fname hash, may be NULL
910947
{
911-
return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
912-
!= (linenr_T)0);
948+
if (prof_ga.ga_len == 0 || !prof_cache_initialized)
949+
return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
950+
!= (linenr_T)0;
951+
952+
hash_T hash;
953+
if (hashp != NULL)
954+
{
955+
hash = *hashp;
956+
if (hash == 0)
957+
{
958+
hash = hash_hash(fname);
959+
*hashp = hash;
960+
}
961+
}
962+
else
963+
hash = hash_hash(fname);
964+
965+
hashtab_T *ht = &prof_cache[file ? PROF_HTAB_FILES : PROF_HTAB_FUNCS];
966+
hashitem_T *hi = hash_lookup(ht, fname, hash);
967+
profentry_T *pe;
968+
if (HASHITEM_EMPTY(hi))
969+
{
970+
pe = alloc(offsetof(profentry_T, pen_name) + STRLEN(fname) + 1);
971+
if (pe == NULL)
972+
return FALSE;
973+
STRCPY(pe->pen_name, fname);
974+
pe->pen_flags = 0;
975+
// run debuggy_find and capture return and forceit
976+
int f;
977+
int lnum = debuggy_find(file, fname, (linenr_T)0, &prof_ga, &f);
978+
if (lnum)
979+
{
980+
PEN_SET_PROFILING(pe);
981+
if (f)
982+
PEN_SET_FORCEIT(pe);
983+
}
984+
hash_add_item(ht, hi, pe->pen_name, hash);
985+
#if defined(PROF_CACHE_LOG)
986+
ch_log(NULL, "has_profiling: %s %s forceit %s, profile %s",
987+
file ? "file" : "func", fname,
988+
PEN_IS_FORCEIT(pe) ? "true" : "false",
989+
PEN_IS_PROFILING(pe) ? "true" : "false");
990+
#endif
991+
}
992+
else
993+
pe = HI2PE(hi);
994+
if (fp)
995+
*fp = PEN_IS_FORCEIT(pe);
996+
#if defined(PROF_CACHE_LOG)
997+
count_lookups[file ? PROF_HTAB_FILES : PROF_HTAB_FUNCS]++;
998+
#endif
999+
return PEN_IS_PROFILING(pe);
1000+
}
1001+
1002+
static void
1003+
prof_clear_cache()
1004+
{
1005+
if (!prof_cache_initialized)
1006+
{
1007+
hash_init(&prof_cache[PROF_HTAB_FUNCS]);
1008+
hash_init(&prof_cache[PROF_HTAB_FILES]);
1009+
prof_cache_initialized = TRUE;
1010+
}
1011+
1012+
hashtab_T *ht;
1013+
for (ht = &prof_cache[0]; ht < &prof_cache[N_PROF_HTAB]; ht++)
1014+
{
1015+
if (ht->ht_used > 0)
1016+
{
1017+
#if defined(PROF_CACHE_LOG)
1018+
int idx = ht == &prof_cache[PROF_HTAB_FUNCS]
1019+
? PROF_HTAB_FUNCS : PROF_HTAB_FILES;
1020+
ch_log(NULL, "prof_clear_cache: %s, used: %ld, lookups: %d",
1021+
idx == PROF_HTAB_FUNCS ? "function" : "script",
1022+
ht->ht_used, count_lookups[idx]);
1023+
count_lookups[idx] = 0;
1024+
#endif
1025+
hash_clear_all(ht, offsetof(profentry_T, pen_name));
1026+
hash_init(ht);
1027+
}
1028+
}
9131029
}
9141030
#endif
9151031

@@ -979,6 +1095,10 @@ debuggy_find(
9791095
*fp = bp->dbg_forceit;
9801096
}
9811097
got_int |= prev_got_int;
1098+
#ifdef FEAT_PROFILE
1099+
if (lnum && gap == &prof_ga)
1100+
break;
1101+
#endif
9821102
}
9831103
#ifdef FEAT_EVAL
9841104
else if (bp->dbg_type == DBG_EXPR)

src/profiler.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,8 @@ script_prof_save(
664664
void
665665
profile_may_start_func(profinfo_T *info, ufunc_T *fp, ufunc_T *caller)
666666
{
667-
if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL))
667+
if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL,
668+
&fp->uf_hash))
668669
{
669670
info->pi_started_profiling = TRUE;
670671
func_do_profile(fp);

src/proto/debugger.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ int debug_has_expr_breakpoint(void);
1010
void ex_breakdel(exarg_T *eap);
1111
void ex_breaklist(exarg_T *eap);
1212
linenr_T dbg_find_breakpoint(int file, char_u *fname, linenr_T after);
13-
int has_profiling(int file, char_u *fname, int *fp);
13+
int has_profiling(int file, char_u *fname, int *fp, hash_T *hash);
1414
void dbg_breakpoint(char_u *name, linenr_T lnum);
1515
/* vim: set ft=c : */

src/scriptfile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,7 @@ do_source_ext(
17211721
int forceit;
17221722

17231723
// Check if we do profiling for this script.
1724-
if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
1724+
if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit, NULL))
17251725
{
17261726
script_do_profile(si);
17271727
si->sn_pr_force = forceit;

src/structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,7 @@ struct ufunc_S
18231823
# ifdef FEAT_PROFILE
18241824
int uf_profiling; // TRUE when func is being profiled
18251825
int uf_prof_initialized;
1826+
hash_T uf_hash; // hash for uf_name when profiling
18261827
// profiling the function as a whole
18271828
int uf_tm_count; // nr of calls
18281829
proftime_T uf_tm_total; // time spent in function + children

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,8 @@ static char *(features[]) =
699699

700700
static int included_patches[] =
701701
{ /* Add new patch number below this line */
702+
/**/
703+
1842,
702704
/**/
703705
1841,
704706
/**/

src/vim9class.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,7 @@ ex_class(exarg_T *eap)
14751475

14761476
// Add the class to the script-local variables.
14771477
// TODO: handle other context, e.g. in a function
1478+
// TODO: does uf_hash need to be cleared?
14781479
typval_T tv;
14791480
tv.v_type = VAR_CLASS;
14801481
tv.vval.v_class = cl;

src/vim9compile.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2983,7 +2983,8 @@ get_compile_type(ufunc_T *ufunc)
29832983
#ifdef FEAT_PROFILE
29842984
if (do_profiling == PROF_YES)
29852985
{
2986-
if (!ufunc->uf_profiling && has_profiling(FALSE, ufunc->uf_name, NULL))
2986+
if (!ufunc->uf_profiling && has_profiling(FALSE, ufunc->uf_name, NULL,
2987+
&ufunc->uf_hash))
29872988
func_do_profile(ufunc);
29882989
if (ufunc->uf_profiling)
29892990
return CT_PROFILE;

0 commit comments

Comments
 (0)