From 0e82fa34163dba21121e3a9cffa7f896c81c4d93 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 26 Sep 2017 16:31:57 -0700 Subject: [PATCH] Avoid some unnecessary copying in Fformat etc. This patch is just for performance; it should not affect behavior. On my platform, it made the microbenchmark (format "%S" load-path) run about 45% faster. It should also speed up calls like (message "%s" STRING). * src/callint.c (Fcall_interactively): * src/dbusbind.c (XD_OBJECT_TO_STRING): * src/editfns.c (Fmessage, Fmessage_box): * src/xdisp.c (vadd_to_log, Ftrace_to_stderr): Use styled_format instead of Fformat or Fformat_message, to avoid unnecessary copying. * src/editfns.c (styled_format): New arg NEW_RESULT. All uses changed. Reuse an input string if it has the right value and if !NEW_RESULT. * src/lisp.h (style_format): New decl. --- src/callint.c | 7 ++----- src/dbusbind.c | 3 ++- src/editfns.c | 47 +++++++++++++++++++++++++++++++++++++---------- src/lisp.h | 1 + src/xdisp.c | 4 ++-- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/callint.c b/src/callint.c index 105ec071d07..469205cc380 100644 --- a/src/callint.c +++ b/src/callint.c @@ -272,7 +272,7 @@ invoke it. If KEYS is omitted or nil, the return value of { /* `args' will contain the array of arguments to pass to the function. `visargs' will contain the same list but in a nicer form, so that if we - pass it to `Fformat_message' it will be understandable to a human. */ + pass it to styled_format it will be understandable to a human. */ Lisp_Object *args, *visargs; Lisp_Object specs; Lisp_Object filter_specs; @@ -502,10 +502,7 @@ invoke it. If KEYS is omitted or nil, the return value of for (i = 2; *tem; i++) { visargs[1] = make_string (tem + 1, strcspn (tem + 1, "\n")); - if (strchr (SSDATA (visargs[1]), '%')) - callint_message = Fformat_message (i - 1, visargs + 1); - else - callint_message = visargs[1]; + callint_message = styled_format (i - 1, visargs + 1, true, false); switch (*tem) { diff --git a/src/dbusbind.c b/src/dbusbind.c index 4a7068416fe..789aa008611 100644 --- a/src/dbusbind.c +++ b/src/dbusbind.c @@ -237,7 +237,8 @@ static char * XD_OBJECT_TO_STRING (Lisp_Object object) { AUTO_STRING (format, "%s"); - return SSDATA (CALLN (Fformat, format, object)); + Lisp_Object args[] = { format, object }; + return SSDATA (styled_format (ARRAYELTS (args), args, false, false)); } #define XD_DBUS_VALIDATE_BUS_ADDRESS(bus) \ diff --git a/src/editfns.c b/src/editfns.c index 2f8b075817a..ef0374199cc 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -74,7 +74,6 @@ static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, static long int tm_gmtoff (struct tm *); static int tm_diff (struct tm *, struct tm *); static void update_buffer_properties (ptrdiff_t, ptrdiff_t); -static Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool); #ifndef HAVE_TM_GMTOFF # define HAVE_TM_GMTOFF false @@ -3959,7 +3958,7 @@ usage: (message FORMAT-STRING &rest ARGS) */) } else { - Lisp_Object val = Fformat_message (nargs, args); + Lisp_Object val = styled_format (nargs, args, true, false); message3 (val); return val; } @@ -3985,7 +3984,7 @@ usage: (message-box FORMAT-STRING &rest ARGS) */) } else { - Lisp_Object val = Fformat_message (nargs, args); + Lisp_Object val = styled_format (nargs, args, true, false); Lisp_Object pane, menu; pane = list1 (Fcons (build_string ("OK"), Qt)); @@ -4141,7 +4140,7 @@ produced text. usage: (format STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - return styled_format (nargs, args, false); + return styled_format (nargs, args, false, true); } DEFUN ("format-message", Fformat_message, Sformat_message, 1, MANY, 0, @@ -4157,13 +4156,16 @@ and right quote replacement characters are specified by usage: (format-message STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - return styled_format (nargs, args, true); + return styled_format (nargs, args, true, true); } -/* Implement ‘format-message’ if MESSAGE is true, ‘format’ otherwise. */ +/* Implement ‘format-message’ if MESSAGE is true, ‘format’ otherwise. + If NEW_RESULT, the result is a new string; otherwise, the result + may be one of the arguments. */ -static Lisp_Object -styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) +Lisp_Object +styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, + bool new_result) { ptrdiff_t n; /* The number of the next arg to substitute. */ char initial_buffer[4000]; @@ -4193,6 +4195,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) /* The start and end bytepos in the output string. */ ptrdiff_t start, end; + /* Whether the argument is a newly created string. */ + bool_bf new_string : 1; + /* Whether the argument is a string with intervals. */ bool_bf intervals : 1; } *info; @@ -4342,7 +4347,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) memset (&discarded[format0 - format_start], 1, format - format0 - (conversion == '%')); if (conversion == '%') - goto copy_char; + { + new_result = true; + goto copy_char; + } ++n; if (! (n < nargs)) @@ -4352,6 +4360,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (nspec < ispec) { spec->argument = args[n]; + spec->new_string = false; spec->intervals = false; nspec = ispec; } @@ -4369,11 +4378,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) { Lisp_Object noescape = conversion == 'S' ? Qnil : Qt; spec->argument = arg = Fprin1_to_string (arg, noescape); + spec->new_string = true; if (STRING_MULTIBYTE (arg) && ! multibyte) { multibyte = true; goto retry; } + new_result = false; } conversion = 's'; } @@ -4387,6 +4398,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) goto retry; } spec->argument = arg = Fchar_to_string (arg); + spec->new_string = true; } if (!EQ (arg, args[n])) @@ -4409,6 +4421,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (conversion == 's') { + if (format == end && format - format_start == 2 + && (!new_result || spec->new_string) + && ! string_intervals (args[0])) + return arg; + /* handle case (precision[n] >= 0) */ ptrdiff_t prec = -1; @@ -4487,6 +4504,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (string_intervals (arg)) spec->intervals = arg_intervals = true; + new_result = true; continue; } } @@ -4754,6 +4772,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } spec->end = nchars; + new_result = true; continue; } } @@ -4772,9 +4791,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } convsrc = format_char == '`' ? uLSQM : uRSQM; convbytes = 3; + new_result = true; } else if (format_char == '`' && quoting_style == STRAIGHT_QUOTING_STYLE) - convsrc = "'"; + { + convsrc = "'"; + new_result = true; + } else { /* Copy a single character from format to buf. */ @@ -4798,6 +4821,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) int c = BYTE8_TO_CHAR (format_char); convbytes = CHAR_STRING (c, str); convsrc = (char *) str; + new_result = true; } } @@ -4844,6 +4868,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (bufsize < p - buf) emacs_abort (); + if (! new_result) + return args[0]; + if (maybe_combine_byte) nchars = multibyte_chars_in_text ((unsigned char *) buf, p - buf); Lisp_Object val = make_specified_string (buf, nchars, p - buf, multibyte); diff --git a/src/lisp.h b/src/lisp.h index c5030824427..0c3ca3ae06b 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3969,6 +3969,7 @@ extern _Noreturn void time_overflow (void); extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool); +extern Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool, bool); extern void init_editfns (bool); extern void syms_of_editfns (void); diff --git a/src/xdisp.c b/src/xdisp.c index 141275f15a0..86164eb9f6f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -10194,7 +10194,7 @@ vadd_to_log (char const *format, va_list ap) for (ptrdiff_t i = 1; i <= nargs; i++) args[i] = va_arg (ap, Lisp_Object); Lisp_Object msg = Qnil; - msg = Fformat_message (nargs, args); + msg = styled_format (nargs, args, true, false); ptrdiff_t len = SBYTES (msg) + 1; USE_SAFE_ALLOCA; @@ -19525,7 +19525,7 @@ DEFUN ("trace-to-stderr", Ftrace_to_stderr, Strace_to_stderr, 1, MANY, "", usage: (trace-to-stderr STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - Lisp_Object s = Fformat (nargs, args); + Lisp_Object s = styled_format (nargs, args, false, false); fwrite (SDATA (s), 1, SBYTES (s), stderr); return Qnil; } -- 2.39.5