Skip to content

Commit 076de79

Browse files
erraelbrammool
authored andcommitted
patch 9.0.1411: accuracy of profiling is not optimal
Problem: Accuracy of profiling is not optimal. Solution: Use CLOCK_MONOTONIC if possible. (Ernie Rael, closes #12129)
1 parent 16110cc commit 076de79

9 files changed

Lines changed: 91 additions & 49 deletions

File tree

runtime/doc/builtin.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7150,7 +7150,8 @@ reltimestr({time}) *reltimestr()*
71507150
call MyFunction()
71517151
echo reltimestr(reltime(start))
71527152
< Note that overhead for the commands will be added to the time.
7153-
The accuracy depends on the system.
7153+
The accuracy depends on the system. Use reltimefloat() for the
7154+
greatest accuracy which is nanoseconds on some systems.
71547155
Leading spaces are used to make the string align nicely. You
71557156
can use split() to remove it. >
71567157
echo split(reltimestr(reltime(start)))[0]
@@ -10753,6 +10754,7 @@ persistent_undo Compiled with support for persistent undo history.
1075310754
postscript Compiled with PostScript file printing.
1075410755
printer Compiled with |:hardcopy| support.
1075510756
profile Compiled with |:profile| support.
10757+
prof_nsec Profile results are in nano seconds.
1075610758
python Python 2.x interface available. |has-python|
1075710759
python_compiled Compiled with Python 2.x interface. |has-python|
1075810760
python_dynamic Python 2.x interface is dynamically loaded. |has-python|

runtime/doc/repeat.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,9 +1148,10 @@ long you take to respond to the input() prompt is irrelevant.
11481148
Profiling should give a good indication of where time is spent, but keep in
11491149
mind there are various things that may clobber the results:
11501150

1151-
- The accuracy of the time measured depends on the gettimeofday() system
1152-
function. It may only be as accurate as 1/100 second, even though the times
1153-
are displayed in micro seconds.
1151+
- The accuracy of the time measured depends on the gettimeofday(), or
1152+
clock_gettime if available, system function. The accuracy ranges from 1/100
1153+
second to nano seconds. With clock_gettime the times are displayed in nano
1154+
seconds, otherwise micro seconds. You can use `has("prof_nsec")`.
11541155

11551156
- Real elapsed time is measured, if other processes are busy they may cause
11561157
delays at unpredictable moments. You may want to run the profiling several

src/evalfunc.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6143,6 +6143,13 @@ f_has(typval_T *argvars, typval_T *rettv)
61436143
1
61446144
#else
61456145
0
6146+
#endif
6147+
},
6148+
{"prof_nsec",
6149+
#ifdef HAVE_TIMER_CREATE
6150+
1
6151+
#else
6152+
0
61466153
#endif
61476154
},
61486155
{"reltime",
@@ -8226,7 +8233,7 @@ init_srand(UINT32_T *x)
82268233
# if defined(MSWIN)
82278234
*x = (UINT32_T)res.LowPart;
82288235
# else
8229-
*x = (UINT32_T)res.tv_usec;
8236+
*x = (UINT32_T)res.tv_fsec;
82308237
# endif
82318238
#else
82328239
*x = vim_time();

src/profiler.c

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ profile_start(proftime_T *tm)
2424
# ifdef MSWIN
2525
QueryPerformanceCounter(tm);
2626
# else
27-
gettimeofday(tm, NULL);
27+
PROF_GET_TIME(tm);
2828
# endif
2929
}
3030

@@ -40,12 +40,12 @@ profile_end(proftime_T *tm)
4040
QueryPerformanceCounter(&now);
4141
tm->QuadPart = now.QuadPart - tm->QuadPart;
4242
# else
43-
gettimeofday(&now, NULL);
44-
tm->tv_usec = now.tv_usec - tm->tv_usec;
43+
PROF_GET_TIME(&now);
44+
tm->tv_fsec = now.tv_fsec - tm->tv_fsec;
4545
tm->tv_sec = now.tv_sec - tm->tv_sec;
46-
if (tm->tv_usec < 0)
46+
if (tm->tv_fsec < 0)
4747
{
48-
tm->tv_usec += 1000000;
48+
tm->tv_fsec += TV_FSEC_SEC;
4949
--tm->tv_sec;
5050
}
5151
# endif
@@ -60,11 +60,11 @@ profile_sub(proftime_T *tm, proftime_T *tm2)
6060
# ifdef MSWIN
6161
tm->QuadPart -= tm2->QuadPart;
6262
# else
63-
tm->tv_usec -= tm2->tv_usec;
63+
tm->tv_fsec -= tm2->tv_fsec;
6464
tm->tv_sec -= tm2->tv_sec;
65-
if (tm->tv_usec < 0)
65+
if (tm->tv_fsec < 0)
6666
{
67-
tm->tv_usec += 1000000;
67+
tm->tv_fsec += TV_FSEC_SEC;
6868
--tm->tv_sec;
6969
}
7070
# endif
@@ -85,7 +85,7 @@ profile_msg(proftime_T *tm)
8585
QueryPerformanceFrequency(&fr);
8686
sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
8787
# else
88-
sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
88+
sprintf(buf, PROF_TIME_FORMAT, (long)tm->tv_sec, (long)tm->tv_fsec);
8989
# endif
9090
return buf;
9191
}
@@ -102,7 +102,7 @@ profile_float(proftime_T *tm)
102102
QueryPerformanceFrequency(&fr);
103103
return (float_T)tm->QuadPart / (float_T)fr.QuadPart;
104104
# else
105-
return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0;
105+
return (float_T)tm->tv_sec + (float_T)tm->tv_fsec / (float_T)TV_FSEC_SEC;
106106
# endif
107107
}
108108

@@ -123,12 +123,12 @@ profile_setlimit(long msec, proftime_T *tm)
123123
QueryPerformanceFrequency(&fr);
124124
tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart);
125125
# else
126-
long usec;
126+
long fsec;
127127

128-
gettimeofday(tm, NULL);
129-
usec = (long)tm->tv_usec + (long)msec * 1000;
130-
tm->tv_usec = usec % 1000000L;
131-
tm->tv_sec += usec / 1000000L;
128+
PROF_GET_TIME(tm);
129+
fsec = (long)tm->tv_fsec + (long)msec * (TV_FSEC_SEC / 1000);
130+
tm->tv_fsec = fsec % (long)TV_FSEC_SEC;
131+
tm->tv_sec += fsec / (long)TV_FSEC_SEC;
132132
# endif
133133
}
134134
}
@@ -149,9 +149,9 @@ profile_passed_limit(proftime_T *tm)
149149
# else
150150
if (tm->tv_sec == 0) // timer was not set
151151
return FALSE;
152-
gettimeofday(&now, NULL);
152+
PROF_GET_TIME(&now);
153153
return (now.tv_sec > tm->tv_sec
154-
|| (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec));
154+
|| (now.tv_sec == tm->tv_sec && now.tv_fsec > tm->tv_fsec));
155155
# endif
156156
}
157157

@@ -164,7 +164,7 @@ profile_zero(proftime_T *tm)
164164
# ifdef MSWIN
165165
tm->QuadPart = 0;
166166
# else
167-
tm->tv_usec = 0;
167+
tm->tv_fsec = 0;
168168
tm->tv_sec = 0;
169169
# endif
170170
}
@@ -189,10 +189,11 @@ profile_divide(proftime_T *tm, int count, proftime_T *tm2)
189189
# ifdef MSWIN
190190
tm2->QuadPart = tm->QuadPart / count;
191191
# else
192-
double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count;
192+
double fsec = (tm->tv_sec * (float_T)TV_FSEC_SEC + tm->tv_fsec)
193+
/ count;
193194

194-
tm2->tv_sec = floor(usec / 1000000.0);
195-
tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0));
195+
tm2->tv_sec = floor(fsec / (float_T)TV_FSEC_SEC);
196+
tm2->tv_fsec = vim_round(fsec - (tm2->tv_sec * (float_T)TV_FSEC_SEC));
196197
# endif
197198
}
198199
}
@@ -213,11 +214,11 @@ profile_add(proftime_T *tm, proftime_T *tm2)
213214
# ifdef MSWIN
214215
tm->QuadPart += tm2->QuadPart;
215216
# else
216-
tm->tv_usec += tm2->tv_usec;
217+
tm->tv_fsec += tm2->tv_fsec;
217218
tm->tv_sec += tm2->tv_sec;
218-
if (tm->tv_usec >= 1000000)
219+
if (tm->tv_fsec >= TV_FSEC_SEC)
219220
{
220-
tm->tv_usec -= 1000000;
221+
tm->tv_fsec -= TV_FSEC_SEC;
221222
++tm->tv_sec;
222223
}
223224
# endif
@@ -237,7 +238,7 @@ profile_self(proftime_T *self, proftime_T *total, proftime_T *children)
237238
#else
238239
if (total->tv_sec < children->tv_sec
239240
|| (total->tv_sec == children->tv_sec
240-
&& total->tv_usec <= children->tv_usec))
241+
&& total->tv_fsec <= children->tv_fsec))
241242
return;
242243
#endif
243244
profile_add(self, total);
@@ -274,7 +275,7 @@ profile_equal(proftime_T *tm1, proftime_T *tm2)
274275
# ifdef MSWIN
275276
return (tm1->QuadPart == tm2->QuadPart);
276277
# else
277-
return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
278+
return (tm1->tv_fsec == tm2->tv_fsec && tm1->tv_sec == tm2->tv_sec);
278279
# endif
279280
}
280281

@@ -288,7 +289,7 @@ profile_cmp(const proftime_T *tm1, const proftime_T *tm2)
288289
return (int)(tm2->QuadPart - tm1->QuadPart);
289290
# else
290291
if (tm1->tv_sec == tm2->tv_sec)
291-
return tm2->tv_usec - tm1->tv_usec;
292+
return tm2->tv_fsec - tm1->tv_fsec;
292293
return tm2->tv_sec - tm1->tv_sec;
293294
# endif
294295
}
@@ -551,16 +552,16 @@ prof_func_line(
551552
{
552553
fprintf(fd, "%5d ", count);
553554
if (prefer_self && profile_equal(total, self))
554-
fprintf(fd, " ");
555+
fprintf(fd, PROF_TIME_BLANK);
555556
else
556557
fprintf(fd, "%s ", profile_msg(total));
557558
if (!prefer_self && profile_equal(total, self))
558-
fprintf(fd, " ");
559+
fprintf(fd, PROF_TIME_BLANK);
559560
else
560561
fprintf(fd, "%s ", profile_msg(self));
561562
}
562563
else
563-
fprintf(fd, " ");
564+
fprintf(fd, " %s%s", PROF_TIME_BLANK, PROF_TIME_BLANK);
564565
}
565566

566567
static void
@@ -575,7 +576,7 @@ prof_sort_list(
575576
ufunc_T *fp;
576577

577578
fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
578-
fprintf(fd, "count total (s) self (s) function\n");
579+
fprintf(fd, "%s function\n", PROF_TOTALS_HEADER);
579580
for (i = 0; i < 20 && i < st_len; ++i)
580581
{
581582
fp = sorttab[i];
@@ -858,7 +859,7 @@ func_dump_profile(FILE *fd)
858859
fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total));
859860
fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self));
860861
fprintf(fd, "\n");
861-
fprintf(fd, "count total (s) self (s)\n");
862+
fprintf(fd, "%s\n", PROF_TOTALS_HEADER);
862863

863864
for (i = 0; i < fp->uf_lines.ga_len; ++i)
864865
{
@@ -948,7 +949,7 @@ script_dump_profile(FILE *fd)
948949
fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
949950
fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
950951
fprintf(fd, "\n");
951-
fprintf(fd, "count total (s) self (s)\n");
952+
fprintf(fd, "%s\n", PROF_TOTALS_HEADER);
952953

953954
sfd = mch_fopen((char *)si->sn_name, "r");
954955
if (sfd == NULL)

src/testdir/test_profile.vim

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ CheckFeature profile
66
source shared.vim
77
source screendump.vim
88

9-
let s:header = 'count total (s) self (s)'
10-
let s:header_func = 'count total (s) self (s) function'
9+
if has('prof_nsec')
10+
let s:header = 'count total (s) self (s)'
11+
let s:header_func = 'count total (s) self (s) function'
12+
else
13+
let s:header = 'count total (s) self (s)'
14+
let s:header_func = 'count total (s) self (s) function'
15+
endif
1116

1217
func Test_profile_func()
1318
call RunProfileFunc('func', 'let', 'let')

src/testdir/test_sleep.vim

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
" Test for sleep and sleep! commands
22

33
func! s:get_time_ms()
4-
let timestr = reltimestr(reltime())
5-
let dotidx = stridx(timestr, '.')
6-
let sec = str2nr(timestr[:dotidx])
7-
let msec = str2nr(timestr[dotidx + 1:])
8-
return (sec * 1000) + (msec / 1000)
4+
return float2nr(reltimefloat(reltime()) * 1000)
95
endfunc
106

117
func! s:assert_takes_longer(cmd, time_ms)

src/time.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ list2proftime(typval_T *arg, proftime_T *tm)
163163
tm->LowPart = n2;
164164
# else
165165
tm->tv_sec = n1;
166-
tm->tv_usec = n2;
166+
tm->tv_fsec = n2;
167167
# endif
168168
return error ? FAIL : OK;
169169
}
@@ -222,7 +222,7 @@ f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
222222
n2 = res.LowPart;
223223
# else
224224
n1 = res.tv_sec;
225-
n2 = res.tv_usec;
225+
n2 = res.tv_fsec;
226226
# endif
227227
list_append_number(rettv->vval.v_list, (varnumber_T)n1);
228228
list_append_number(rettv->vval.v_list, (varnumber_T)n2);
@@ -258,6 +258,7 @@ f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
258258
void
259259
f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
260260
{
261+
static char buf[50];
261262
# ifdef FEAT_RELTIME
262263
proftime_T tm;
263264
# endif
@@ -269,7 +270,15 @@ f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
269270
return;
270271

271272
if (list2proftime(&argvars[0], &tm) == OK)
273+
{
274+
# ifdef MSWIN
272275
rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
276+
# else
277+
long usec = tm.tv_fsec / (TV_FSEC_SEC / 1000000);
278+
sprintf(buf, "%3ld.%06ld", (long)tm.tv_sec, usec);
279+
rettv->vval.v_string = vim_strsave((char_u *)buf);
280+
# endif
281+
}
273282
else if (in_vim9script())
274283
emsg(_(e_invalid_argument));
275284
# endif
@@ -392,7 +401,7 @@ static timer_T *first_timer = NULL;
392401
static long last_timer_id = 0;
393402

394403
/*
395-
* Return time left until "due". Negative if past "due".
404+
* Return time left, in "msec", until "due". Negative if past "due".
396405
*/
397406
long
398407
proftime_time_left(proftime_T *due, proftime_T *now)
@@ -409,7 +418,7 @@ proftime_time_left(proftime_T *due, proftime_T *now)
409418
if (now->tv_sec > due->tv_sec)
410419
return 0;
411420
return (due->tv_sec - now->tv_sec) * 1000
412-
+ (due->tv_usec - now->tv_usec) / 1000;
421+
+ (due->tv_fsec - now->tv_fsec) / (TV_FSEC_SEC / 1000);
413422
# endif
414423
}
415424

src/version.c

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

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1411,
698700
/**/
699701
1410,
700702
/**/

src/vim.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,8 +1869,27 @@ typedef void *vim_acl_T; // dummy to pass an ACL to a function
18691869
#if (defined(FEAT_PROFILE) || defined(FEAT_RELTIME)) && !defined(PROTO)
18701870
# ifdef MSWIN
18711871
typedef LARGE_INTEGER proftime_T;
1872+
# define PROF_TIME_BLANK " "
1873+
# define PROF_TOTALS_HEADER "count total (s) self (s)"
18721874
# else
1875+
// Use tv_fsec for fraction of second (micro or nano) of proftime_T
1876+
# if defined(HAVE_TIMER_CREATE)
1877+
typedef struct timespec proftime_T;
1878+
# define PROF_GET_TIME(tm) clock_gettime(CLOCK_MONOTONIC, tm)
1879+
# define tv_fsec tv_nsec
1880+
# define TV_FSEC_SEC 1000000000L
1881+
# define PROF_TIME_FORMAT "%3ld.%09ld"
1882+
# define PROF_TIME_BLANK " "
1883+
# define PROF_TOTALS_HEADER "count total (s) self (s)"
1884+
# else
18731885
typedef struct timeval proftime_T;
1886+
# define PROF_GET_TIME(tm) gettimeofday(tm, NULL)
1887+
# define tv_fsec tv_usec
1888+
# define TV_FSEC_SEC 1000000
1889+
# define PROF_TIME_FORMAT "%3ld.%06ld"
1890+
# define PROF_TIME_BLANK " "
1891+
# define PROF_TOTALS_HEADER "count total (s) self (s)"
1892+
# endif
18741893
# endif
18751894
#else
18761895
typedef int proftime_T; // dummy for function prototypes

0 commit comments

Comments
 (0)