Skip to content

Commit f77f297

Browse files
committed
[mypyc] feat: cache ids for fallback pythonic method lookups
1 parent dce8e1c commit f77f297

File tree

4 files changed

+138
-84
lines changed

4 files changed

+138
-84
lines changed

mypyc/lib-rt/bytes_ops.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,22 @@ PyObject *CPyBytes_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) {
9595
return CPyObject_GetSlice(obj, start, end);
9696
}
9797

98+
static PyObject *join_id_unicode = NULL;
99+
98100
// Like _PyBytes_Join but fallback to dynamic call if 'sep' is not bytes
99101
// (mostly commonly, for bytearrays)
100102
PyObject *CPyBytes_Join(PyObject *sep, PyObject *iter) {
101103
if (PyBytes_CheckExact(sep)) {
102104
return PyBytes_Join(sep, iter);
103105
} else {
104-
_Py_IDENTIFIER(join);
105-
PyObject *name = _PyUnicode_FromId(&PyId_join); /* borrowed */
106-
if (name == NULL) {
107-
return NULL;
106+
if (join_id_unicode == NULL) {
107+
_Py_IDENTIFIER(join);
108+
PyObject *join_id_unicode = _PyUnicode_FromId(&PyId_join); /* borrowed */
109+
if (join_id_unicode == NULL) {
110+
return NULL;
111+
}
108112
}
109-
return PyObject_CallMethodOneArg(sep, name, iter);
113+
return PyObject_CallMethodOneArg(sep, join_id_unicode, iter);
110114
}
111115
}
112116

mypyc/lib-rt/dict_ops.c

Lines changed: 93 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
#define Py_TPFLAGS_MAPPING (1 << 6)
1010
#endif
1111

12+
// Caching _PyUnicode_FromId results for identifiers
13+
static PyObject *setdefault_id_unicode = NULL;
14+
static PyObject *update_id_unicode = NULL;
15+
static PyObject *keys_id_unicode = NULL;
16+
static PyObject *values_id_unicode = NULL;
17+
static PyObject *items_id_unicode = NULL;
18+
static PyObject *clear_id_unicode = NULL;
19+
static PyObject *copy_id_unicode = NULL;
20+
1221
// Dict subclasses like defaultdict override things in interesting
1322
// ways, so we don't want to just directly use the dict methods. Not
1423
// sure if it is actually worth doing all this stuff, but it saves
@@ -77,12 +86,14 @@ PyObject *CPyDict_SetDefault(PyObject *dict, PyObject *key, PyObject *value) {
7786
Py_XINCREF(ret);
7887
return ret;
7988
}
80-
_Py_IDENTIFIER(setdefault);
81-
PyObject *name = _PyUnicode_FromId(&PyId_setdefault); /* borrowed */
82-
if (name == NULL) {
83-
return NULL;
89+
if (setdefault_id_unicode == NULL) {
90+
_Py_IDENTIFIER(setdefault);
91+
setdefault_id_unicode = _PyUnicode_FromId(&PyId_setdefault); /* borrowed */
92+
if (setdefault_id_unicode == NULL) {
93+
return NULL;
94+
}
8495
}
85-
return PyObject_CallMethodObjArgs(dict, name, key, value, NULL);
96+
return PyObject_CallMethodObjArgs(dict, setdefault_id_unicode, key, value, NULL);
8697
}
8798

8899
PyObject *CPyDict_SetDefaultWithNone(PyObject *dict, PyObject *key) {
@@ -136,12 +147,14 @@ static inline int CPy_ObjectToStatus(PyObject *obj) {
136147
}
137148

138149
static int CPyDict_UpdateGeneral(PyObject *dict, PyObject *stuff) {
139-
_Py_IDENTIFIER(update);
140-
PyObject *name = _PyUnicode_FromId(&PyId_update); /* borrowed */
141-
if (name == NULL) {
142-
return -1;
150+
if (update_id_unicode == NULL) {
151+
_Py_IDENTIFIER(update);
152+
update_id_unicode = _PyUnicode_FromId(&PyId_update); /* borrowed */
153+
if (update_id_unicode == NULL) {
154+
return -1;
155+
}
143156
}
144-
PyObject *res = PyObject_CallMethodOneArg(dict, name, stuff);
157+
PyObject *res = PyObject_CallMethodOneArg(dict, update_id_unicode, stuff);
145158
return CPy_ObjectToStatus(res);
146159
}
147160

@@ -207,36 +220,42 @@ PyObject *CPyDict_KeysView(PyObject *dict) {
207220
if (PyDict_CheckExact(dict)){
208221
return _CPyDictView_New(dict, &PyDictKeys_Type);
209222
}
210-
_Py_IDENTIFIER(keys);
211-
PyObject *name = _PyUnicode_FromId(&PyId_keys); /* borrowed */
212-
if (name == NULL) {
213-
return NULL;
223+
if (keys_id_unicode == NULL) {
224+
_Py_IDENTIFIER(keys);
225+
keys_id_unicode = _PyUnicode_FromId(&PyId_keys); /* borrowed */
226+
if (keys_id_unicode == NULL) {
227+
return NULL;
228+
}
214229
}
215-
return PyObject_CallMethodNoArgs(dict, name);
230+
return PyObject_CallMethodNoArgs(dict, keys_id_unicode);
216231
}
217232

218233
PyObject *CPyDict_ValuesView(PyObject *dict) {
219234
if (PyDict_CheckExact(dict)){
220235
return _CPyDictView_New(dict, &PyDictValues_Type);
221236
}
222-
_Py_IDENTIFIER(values);
223-
PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */
224-
if (name == NULL) {
225-
return NULL;
237+
if (values_id_unicode == NULL) {
238+
_Py_IDENTIFIER(values);
239+
values_id_unicode = _PyUnicode_FromId(&PyId_values); /* borrowed */
240+
if (values_id_unicode == NULL) {
241+
return NULL;
242+
}
226243
}
227-
return PyObject_CallMethodNoArgs(dict, name);
244+
return PyObject_CallMethodNoArgs(dict, values_id_unicode);
228245
}
229246

230247
PyObject *CPyDict_ItemsView(PyObject *dict) {
231248
if (PyDict_CheckExact(dict)){
232249
return _CPyDictView_New(dict, &PyDictItems_Type);
233250
}
234-
_Py_IDENTIFIER(items);
235-
PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */
236-
if (name == NULL) {
237-
return NULL;
251+
if (items_id_unicode == NULL) {
252+
_Py_IDENTIFIER(items);
253+
items_id_unicode = _PyUnicode_FromId(&PyId_items); /* borrowed */
254+
if (items_id_unicode == NULL) {
255+
return NULL;
256+
}
238257
}
239-
return PyObject_CallMethodNoArgs(dict, name);
258+
return PyObject_CallMethodNoArgs(dict, items_id_unicode);
240259
}
241260

242261
PyObject *CPyDict_Keys(PyObject *dict) {
@@ -245,12 +264,14 @@ PyObject *CPyDict_Keys(PyObject *dict) {
245264
}
246265
// Inline generic fallback logic to also return a list.
247266
PyObject *list = PyList_New(0);
248-
_Py_IDENTIFIER(keys);
249-
PyObject *name = _PyUnicode_FromId(&PyId_keys); /* borrowed */
250-
if (name == NULL) {
251-
return NULL;
267+
if (keys_id_unicode == NULL) {
268+
_Py_IDENTIFIER(keys);
269+
keys_id_unicode = _PyUnicode_FromId(&PyId_keys); /* borrowed */
270+
if (keys_id_unicode == NULL) {
271+
return NULL;
272+
}
252273
}
253-
PyObject *view = PyObject_CallMethodNoArgs(dict, name);
274+
PyObject *view = PyObject_CallMethodNoArgs(dict, keys_id_unicode);
254275
if (view == NULL) {
255276
return NULL;
256277
}
@@ -268,12 +289,14 @@ PyObject *CPyDict_Values(PyObject *dict) {
268289
}
269290
// Inline generic fallback logic to also return a list.
270291
PyObject *list = PyList_New(0);
271-
_Py_IDENTIFIER(values);
272-
PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */
273-
if (name == NULL) {
274-
return NULL;
292+
if (values_id_unicode == NULL) {
293+
_Py_IDENTIFIER(values);
294+
values_id_unicode = _PyUnicode_FromId(&PyId_values); /* borrowed */
295+
if (values_id_unicode == NULL) {
296+
return NULL;
297+
}
275298
}
276-
PyObject *view = PyObject_CallMethodNoArgs(dict, name);
299+
PyObject *view = PyObject_CallMethodNoArgs(dict, values_id_unicode);
277300
if (view == NULL) {
278301
return NULL;
279302
}
@@ -291,12 +314,14 @@ PyObject *CPyDict_Items(PyObject *dict) {
291314
}
292315
// Inline generic fallback logic to also return a list.
293316
PyObject *list = PyList_New(0);
294-
_Py_IDENTIFIER(items);
295-
PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */
296-
if (name == NULL) {
297-
return NULL;
317+
if (items_id_unicode == NULL) {
318+
_Py_IDENTIFIER(items);
319+
items_id_unicode = _PyUnicode_FromId(&PyId_items); /* borrowed */
320+
if (items_id_unicode == NULL) {
321+
return NULL;
322+
}
298323
}
299-
PyObject *view = PyObject_CallMethodNoArgs(dict, name);
324+
PyObject *view = PyObject_CallMethodNoArgs(dict, items_id_unicode);
300325
if (view == NULL) {
301326
return NULL;
302327
}
@@ -312,12 +337,14 @@ char CPyDict_Clear(PyObject *dict) {
312337
if (PyDict_CheckExact(dict)) {
313338
PyDict_Clear(dict);
314339
} else {
315-
_Py_IDENTIFIER(clear);
316-
PyObject *name = _PyUnicode_FromId(&PyId_clear); /* borrowed */
317-
if (name == NULL) {
318-
return 0;
340+
if (clear_id_unicode == NULL) {
341+
_Py_IDENTIFIER(clear);
342+
clear_id_unicode = _PyUnicode_FromId(&PyId_clear); /* borrowed */
343+
if (clear_id_unicode == NULL) {
344+
return 0;
345+
}
319346
}
320-
PyObject *res = PyObject_CallMethodNoArgs(dict, name);
347+
PyObject *res = PyObject_CallMethodNoArgs(dict, clear_id_unicode);
321348
if (res == NULL) {
322349
return 0;
323350
}
@@ -329,12 +356,14 @@ PyObject *CPyDict_Copy(PyObject *dict) {
329356
if (PyDict_CheckExact(dict)) {
330357
return PyDict_Copy(dict);
331358
}
332-
_Py_IDENTIFIER(copy);
333-
PyObject *name = _PyUnicode_FromId(&PyId_copy); /* borrowed */
334-
if (name == NULL) {
335-
return NULL;
359+
if (copy_id_unicode == NULL) {
360+
_Py_IDENTIFIER(copy);
361+
copy_id_unicode = _PyUnicode_FromId(&PyId_copy); /* borrowed */
362+
if (copy_id_unicode == NULL) {
363+
return NULL;
364+
}
336365
}
337-
return PyObject_CallMethodNoArgs(dict, name);
366+
return PyObject_CallMethodNoArgs(dict, copy_id_unicode);
338367
}
339368

340369
PyObject *CPyDict_GetKeysIter(PyObject *dict) {
@@ -352,12 +381,14 @@ PyObject *CPyDict_GetItemsIter(PyObject *dict) {
352381
Py_INCREF(dict);
353382
return dict;
354383
}
355-
_Py_IDENTIFIER(items);
356-
PyObject *name = _PyUnicode_FromId(&PyId_items); /* borrowed */
357-
if (name == NULL) {
358-
return NULL;
384+
if (items_id_unicode == NULL) {
385+
_Py_IDENTIFIER(items);
386+
items_id_unicode = _PyUnicode_FromId(&PyId_items); /* borrowed */
387+
if (items_id_unicode == NULL) {
388+
return NULL;
389+
}
359390
}
360-
PyObject *view = PyObject_CallMethodNoArgs(dict, name);
391+
PyObject *view = PyObject_CallMethodNoArgs(dict, items_id_unicode);
361392
if (view == NULL) {
362393
return NULL;
363394
}
@@ -372,12 +403,14 @@ PyObject *CPyDict_GetValuesIter(PyObject *dict) {
372403
Py_INCREF(dict);
373404
return dict;
374405
}
375-
_Py_IDENTIFIER(values);
376-
PyObject *name = _PyUnicode_FromId(&PyId_values); /* borrowed */
377-
if (name == NULL) {
378-
return NULL;
406+
if (values_id_unicode == NULL) {
407+
_Py_IDENTIFIER(values);
408+
values_id_unicode = _PyUnicode_FromId(&PyId_values); /* borrowed */
409+
if (values_id_unicode == NULL) {
410+
return NULL;
411+
}
379412
}
380-
PyObject *view = PyObject_CallMethodNoArgs(dict, name);
413+
PyObject *view = PyObject_CallMethodNoArgs(dict, values_id_unicode);
381414
if (view == NULL) {
382415
return NULL;
383416
}

mypyc/lib-rt/list_ops.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#define Py_TPFLAGS_SEQUENCE (1 << 5)
1010
#endif
1111

12+
static PyObject *clear_id_unicode = NULL;
13+
static PyObject *copy_id_unicode = NULL;
14+
1215
PyObject *CPyList_Build(Py_ssize_t len, ...) {
1316
Py_ssize_t i;
1417

@@ -33,12 +36,14 @@ char CPyList_Clear(PyObject *list) {
3336
if (PyList_CheckExact(list)) {
3437
PyList_Clear(list);
3538
} else {
36-
_Py_IDENTIFIER(clear);
37-
PyObject *name = _PyUnicode_FromId(&PyId_clear);
38-
if (name == NULL) {
39-
return 0;
39+
if (clear_id_unicode == NULL) {
40+
_Py_IDENTIFIER(clear);
41+
PyObject *clear_id_unicode = _PyUnicode_FromId(&PyId_clear);
42+
if (clear_id_unicode == NULL) {
43+
return 0;
44+
}
4045
}
41-
PyObject *res = PyObject_CallMethodNoArgs(list, name);
46+
PyObject *res = PyObject_CallMethodNoArgs(list, clear_id_unicode);
4247
if (res == NULL) {
4348
return 0;
4449
}
@@ -50,13 +55,14 @@ PyObject *CPyList_Copy(PyObject *list) {
5055
if(PyList_CheckExact(list)) {
5156
return PyList_GetSlice(list, 0, PyList_GET_SIZE(list));
5257
}
53-
_Py_IDENTIFIER(copy);
54-
55-
PyObject *name = _PyUnicode_FromId(&PyId_copy);
56-
if (name == NULL) {
57-
return NULL;
58+
if (copy_id_unicode == NULL) {
59+
_Py_IDENTIFIER(copy);
60+
PyObject *copy_id_unicode = _PyUnicode_FromId(&PyId_copy);
61+
if (copy_id_unicode == NULL) {
62+
return NULL;
63+
}
5864
}
59-
return PyObject_CallMethodNoArgs(list, name);
65+
return PyObject_CallMethodNoArgs(list, copy_id_unicode);
6066
}
6167

6268
PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index) {

mypyc/lib-rt/misc_ops.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,23 @@ PyObject *CPy_GetCoro(PyObject *obj)
2020
}
2121
}
2222

23+
static PyObject *send_id_unicode = NULL;
24+
2325
PyObject *CPyIter_Send(PyObject *iter, PyObject *val)
2426
{
2527
// Do a send, or a next if second arg is None.
2628
// (This behavior is to match the PEP 380 spec for yield from.)
2729
if (Py_IsNone(val)) {
2830
return CPyIter_Next(iter);
2931
} else {
30-
_Py_IDENTIFIER(send);
31-
PyObject *name = _PyUnicode_FromId(&PyId_send); /* borrowed */
32-
if (name == NULL) {
33-
return NULL;
32+
if (send_id_unicode == NULL) {
33+
_Py_IDENTIFIER(send);
34+
PyObject *send_id_unicode = _PyUnicode_FromId(&PyId_send); /* borrowed */
35+
if (send_id_unicode == NULL) {
36+
return NULL;
37+
}
3438
}
35-
return PyObject_CallMethodOneArg(iter, name, val);
39+
return PyObject_CallMethodOneArg(iter, send_id_unicode, val);
3640
}
3741
}
3842

@@ -1058,14 +1062,21 @@ PyObject *CPy_GetANext(PyObject *aiter)
10581062

10591063
#if CPY_3_11_FEATURES
10601064

1065+
static PyObject *__name___id_unicode = NULL;
1066+
10611067
// Return obj.__name__ (specialized to type objects, which are the most common target).
10621068
PyObject *CPy_GetName(PyObject *obj) {
10631069
if (PyType_Check(obj)) {
10641070
return PyType_GetName((PyTypeObject *)obj);
10651071
}
1066-
_Py_IDENTIFIER(__name__);
1067-
PyObject *name = _PyUnicode_FromId(&PyId___name__); /* borrowed */
1068-
return PyObject_GetAttr(obj, name);
1072+
if (__name___id_unicode == NULL) {
1073+
_Py_IDENTIFIER(__name__);
1074+
__name___id_unicode = _PyUnicode_FromId(&PyId___name__); /* borrowed */
1075+
if (__name___id_unicode == NULL) {
1076+
return NULL;
1077+
}
1078+
}
1079+
return PyObject_GetAttr(obj, __name___id_unicode);
10691080
}
10701081

10711082
#endif

0 commit comments

Comments
 (0)