@@ -568,20 +568,53 @@ _PyObject_GetXIData(PyThreadState *tstate,
568568
569569/* pickle C-API */
570570
571+ /* Get a cached reference to pickle.dumps for the current interpreter. */
572+ static PyObject *
573+ _get_pickle_dumps (PyThreadState * tstate )
574+ {
575+ _PyXI_state_t * state = _PyXI_GET_STATE (tstate -> interp );
576+ PyObject * dumps = state -> pickle .dumps ;
577+ if (dumps != NULL ) {
578+ return dumps ;
579+ }
580+ dumps = PyImport_ImportModuleAttrString ("pickle" , "dumps" );
581+ if (dumps == NULL ) {
582+ return NULL ;
583+ }
584+ state -> pickle .dumps = dumps ; // owns the reference
585+ return dumps ;
586+ }
587+
588+ /* Get a cached reference to pickle.loads for the current interpreter. */
589+ static PyObject *
590+ _get_pickle_loads (PyThreadState * tstate )
591+ {
592+ _PyXI_state_t * state = _PyXI_GET_STATE (tstate -> interp );
593+ PyObject * loads = state -> pickle .loads ;
594+ if (loads != NULL ) {
595+ return loads ;
596+ }
597+ loads = PyImport_ImportModuleAttrString ("pickle" , "loads" );
598+ if (loads == NULL ) {
599+ return NULL ;
600+ }
601+ state -> pickle .loads = loads ; // owns the reference
602+ return loads ;
603+ }
604+
571605struct _pickle_context {
572606 PyThreadState * tstate ;
573607};
574608
575609static PyObject *
576610_PyPickle_Dumps (struct _pickle_context * ctx , PyObject * obj )
577611{
578- PyObject * dumps = PyImport_ImportModuleAttrString ( "pickle" , "dumps" );
612+ PyObject * dumps = _get_pickle_dumps ( ctx -> tstate );
579613 if (dumps == NULL ) {
580614 return NULL ;
581615 }
582- PyObject * bytes = PyObject_CallOneArg (dumps , obj );
583- Py_DECREF (dumps );
584- return bytes ;
616+ // dumps is a borrowed reference from the cache.
617+ return PyObject_CallOneArg (dumps , obj );
585618}
586619
587620
@@ -636,7 +669,8 @@ _PyPickle_Loads(struct _unpickle_context *ctx, PyObject *pickled)
636669 PyThreadState * tstate = ctx -> tstate ;
637670
638671 PyObject * exc = NULL ;
639- PyObject * loads = PyImport_ImportModuleAttrString ("pickle" , "loads" );
672+ // loads is a borrowed reference from the per-interpreter cache.
673+ PyObject * loads = _get_pickle_loads (tstate );
640674 if (loads == NULL ) {
641675 return NULL ;
642676 }
@@ -682,7 +716,6 @@ _PyPickle_Loads(struct _unpickle_context *ctx, PyObject *pickled)
682716 // It might make sense to chain it (__context__).
683717 _PyErr_SetRaisedException (tstate , exc );
684718 }
685- Py_DECREF (loads );
686719 return obj ;
687720}
688721
@@ -3107,6 +3140,10 @@ _Py_xi_state_init(_PyXI_state_t *state, PyInterpreterState *interp)
31073140 return -1 ;
31083141 }
31093142
3143+ // Initialize pickle function cache.
3144+ state -> pickle .dumps = NULL ;
3145+ state -> pickle .loads = NULL ;
3146+
31103147 return 0 ;
31113148}
31123149
@@ -3116,6 +3153,10 @@ _Py_xi_state_fini(_PyXI_state_t *state, PyInterpreterState *interp)
31163153 assert (state != NULL );
31173154 assert (interp == NULL || state == _PyXI_GET_STATE (interp ));
31183155
3156+ // Clear pickle function cache before other cleanup.
3157+ Py_CLEAR (state -> pickle .dumps );
3158+ Py_CLEAR (state -> pickle .loads );
3159+
31193160 fini_heap_exctypes (& state -> exceptions );
31203161 if (interp != NULL ) {
31213162 fini_static_exctypes (& state -> exceptions , interp );
0 commit comments