Skip to content

Commit e5bdccc

Browse files
committed
Backport of r63826.
Optimization of str.format() for cases with str, unicode, int, long, and float arguments. This gives about 30% speed improvement for the simplest (but most common) cases. This patch skips the __format__ dispatch, and also avoids creating an object to hold the format_spec. Unfortunately there's a complication in 2.6 with int, long, and float because they always expect str format_specs. So in the unicode version of this optimization, just check for unicode objects. int, float, long, and str can be added later, if needed.
1 parent 8e439a1 commit e5bdccc

File tree

1 file changed

+42
-6
lines changed

1 file changed

+42
-6
lines changed

Objects/stringlib/string_format.h

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -483,13 +483,49 @@ render_field(PyObject *fieldobj, SubString *format_spec, OutputString *output)
483483
{
484484
int ok = 0;
485485
PyObject *result = NULL;
486+
PyObject *format_spec_object = NULL;
487+
PyObject *(*formatter)(PyObject *, STRINGLIB_CHAR *, Py_ssize_t) = NULL;
488+
STRINGLIB_CHAR* format_spec_start = format_spec->ptr ?
489+
format_spec->ptr : NULL;
490+
Py_ssize_t format_spec_len = format_spec->ptr ?
491+
format_spec->end - format_spec->ptr : 0;
492+
493+
/* If we know the type exactly, skip the lookup of __format__ and just
494+
call the formatter directly. */
495+
#if STRINGLIB_IS_UNICODE
496+
if (PyUnicode_CheckExact(fieldobj))
497+
formatter = _PyUnicode_FormatAdvanced;
498+
/* Unfortunately, there's a problem with checking for int, long,
499+
and float here. If we're being included as unicode, their
500+
formatters expect string format_spec args. For now, just skip
501+
this optimization for unicode. This could be fixed, but it's a
502+
hassle. */
503+
#else
504+
if (PyString_CheckExact(fieldobj))
505+
formatter = _PyBytes_FormatAdvanced;
506+
else if (PyInt_CheckExact(fieldobj))
507+
formatter =_PyInt_FormatAdvanced;
508+
else if (PyLong_CheckExact(fieldobj))
509+
formatter =_PyLong_FormatAdvanced;
510+
else if (PyFloat_CheckExact(fieldobj))
511+
formatter = _PyFloat_FormatAdvanced;
512+
#endif
486513

487-
/* we need to create an object out of the pointers we have */
488-
PyObject *format_spec_object = SubString_new_object_or_empty(format_spec);
489-
if (format_spec_object == NULL)
490-
goto done;
514+
if (formatter) {
515+
/* we know exactly which formatter will be called when __format__ is
516+
looked up, so call it directly, instead. */
517+
result = formatter(fieldobj, format_spec_start, format_spec_len);
518+
}
519+
else {
520+
/* We need to create an object out of the pointers we have, because
521+
__format__ takes a string/unicode object for format_spec. */
522+
format_spec_object = STRINGLIB_NEW(format_spec_start,
523+
format_spec_len);
524+
if (format_spec_object == NULL)
525+
goto done;
491526

492-
result = PyObject_Format(fieldobj, format_spec_object);
527+
result = PyObject_Format(fieldobj, format_spec_object);
528+
}
493529
if (result == NULL)
494530
goto done;
495531

@@ -512,7 +548,7 @@ render_field(PyObject *fieldobj, SubString *format_spec, OutputString *output)
512548
ok = output_data(output,
513549
STRINGLIB_STR(result), STRINGLIB_LEN(result));
514550
done:
515-
Py_DECREF(format_spec_object);
551+
Py_XDECREF(format_spec_object);
516552
Py_XDECREF(result);
517553
return ok;
518554
}

0 commit comments

Comments
 (0)