Skip to content

Commit c3a06df

Browse files
committed
pythongh-119396: Optimize %S format of PyUnicode_FromFormat()
Add fast paths for str, int and float object types. Benchmark on %S and %R formats: +----------------+--------+----------------------+ | Benchmark | ref | change | +================+========+======================+ | str() | 654 ns | 556 ns: 1.18x faster | +----------------+--------+----------------------+ | repr() | 722 ns | 627 ns: 1.15x faster | +----------------+--------+----------------------+ | Geometric mean | (ref) | 1.16x faster | +----------------+--------+----------------------+
1 parent d472b4f commit c3a06df

1 file changed

Lines changed: 78 additions & 14 deletions

File tree

‎Objects/unicodeobject.c‎

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,57 @@ PyUnicode_AsUCS4Copy(PyObject *string)
23252325
return as_ucs4(string, NULL, 0, 1);
23262326
}
23272327

2328+
2329+
static int
2330+
unicode_writer_write_object_str(_PyUnicodeWriter *writer, PyObject *obj)
2331+
{
2332+
// Fast paths for specific types
2333+
PyTypeObject *type = Py_TYPE(obj);
2334+
if (type == &PyUnicode_Type) {
2335+
return _PyUnicodeWriter_WriteStr(writer, obj);
2336+
}
2337+
else if (type == &PyLong_Type) {
2338+
return _PyLong_FormatWriter(writer, obj, 10, 0);
2339+
}
2340+
else if (type == &PyFloat_Type) {
2341+
return _PyFloat_FormatAdvancedWriter(writer, obj,
2342+
NULL, 0, 0);
2343+
}
2344+
2345+
// Default implementation
2346+
PyObject *str = PyObject_Str(obj);
2347+
if (!str) {
2348+
return -1;
2349+
}
2350+
int res = _PyUnicodeWriter_WriteStr(writer, str);
2351+
Py_DECREF(str);
2352+
return res;
2353+
}
2354+
2355+
static int
2356+
unicode_writer_write_object_repr(_PyUnicodeWriter *writer, PyObject *obj)
2357+
{
2358+
// Fast paths for specific types
2359+
PyTypeObject *type = Py_TYPE(obj);
2360+
if (type == &PyLong_Type) {
2361+
return _PyLong_FormatWriter(writer, obj, 10, 0);
2362+
}
2363+
else if (type == &PyFloat_Type) {
2364+
return _PyFloat_FormatAdvancedWriter(writer, obj,
2365+
NULL, 0, 0);
2366+
}
2367+
2368+
// Default implementation
2369+
PyObject *repr = PyObject_Repr(obj);
2370+
if (!repr) {
2371+
return -1;
2372+
}
2373+
int res = _PyUnicodeWriter_WriteStr(writer, repr);
2374+
Py_DECREF(repr);
2375+
return res;
2376+
}
2377+
2378+
23282379
/* maximum number of characters required for output of %jo or %jd or %p.
23292380
We need at most ceil(log8(256)*sizeof(intmax_t)) digits,
23302381
plus 1 for the sign, plus 2 for the 0x prefix (for %p),
@@ -2747,32 +2798,45 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
27472798
case 'S':
27482799
{
27492800
PyObject *obj = va_arg(*vargs, PyObject *);
2750-
PyObject *str;
27512801
assert(obj);
2752-
str = PyObject_Str(obj);
2753-
if (!str)
2754-
return NULL;
2755-
if (unicode_fromformat_write_str(writer, str, width, precision, flags) == -1) {
2802+
if (precision < 0 && width < 0) {
2803+
if (unicode_writer_write_object_str(writer, obj) < 0) {
2804+
return NULL;
2805+
}
2806+
}
2807+
else {
2808+
PyObject *str = PyObject_Str(obj);
2809+
if (!str)
2810+
return NULL;
2811+
if (unicode_fromformat_write_str(writer, str, width, precision, flags) == -1) {
2812+
Py_DECREF(str);
2813+
return NULL;
2814+
}
27562815
Py_DECREF(str);
2757-
return NULL;
27582816
}
2759-
Py_DECREF(str);
27602817
break;
27612818
}
27622819

27632820
case 'R':
27642821
{
27652822
PyObject *obj = va_arg(*vargs, PyObject *);
2766-
PyObject *repr;
27672823
assert(obj);
2768-
repr = PyObject_Repr(obj);
2769-
if (!repr)
2770-
return NULL;
2771-
if (unicode_fromformat_write_str(writer, repr, width, precision, flags) == -1) {
2824+
if (precision < 0 && width < 0) {
2825+
if (unicode_writer_write_object_repr(writer, obj) < 0) {
2826+
return NULL;
2827+
}
2828+
}
2829+
else {
2830+
PyObject *repr = PyObject_Repr(obj);
2831+
if (!repr) {
2832+
return NULL;
2833+
}
2834+
if (unicode_fromformat_write_str(writer, repr, width, precision, flags) == -1) {
2835+
Py_DECREF(repr);
2836+
return NULL;
2837+
}
27722838
Py_DECREF(repr);
2773-
return NULL;
27742839
}
2775-
Py_DECREF(repr);
27762840
break;
27772841
}
27782842

0 commit comments

Comments
 (0)