Skip to content

gh-100227: Move the dtoa State to PyInterpreterState #102331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Include/internal/pycore_dtoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ Bigint {

#ifdef Py_USING_MEMORY_DEBUGGER

struct _dtoa_runtime_state {
struct _dtoa_state {
int _not_used;
};
#define _dtoa_runtime_state_INIT {0}
#define _dtoa_interp_state_INIT(INTERP) \
{0}

#else // !Py_USING_MEMORY_DEBUGGER

Expand All @@ -40,17 +41,17 @@ struct _dtoa_runtime_state {
#define Bigint_PREALLOC_SIZE \
((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))

struct _dtoa_runtime_state {
struct _dtoa_state {
/* p5s is a linked list of powers of 5 of the form 5**(2**i), i >= 2 */
// XXX This should be freed during runtime fini.
struct Bigint *p5s;
struct Bigint *freelist[Bigint_Kmax+1];
double preallocated[Bigint_PREALLOC_SIZE];
double *preallocated_next;
};
#define _dtoa_runtime_state_INIT(runtime) \
#define _dtoa_state_INIT(INTERP) \
{ \
.preallocated_next = runtime.dtoa.preallocated, \
.preallocated_next = (INTERP)->dtoa.preallocated, \
}

#endif // !Py_USING_MEMORY_DEBUGGER
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern "C" {
#include "pycore_code.h" // struct callable_cache
#include "pycore_context.h" // struct _Py_context_state
#include "pycore_dict_state.h" // struct _Py_dict_state
#include "pycore_dtoa.h" // struct _dtoa_state
#include "pycore_exceptions.h" // struct _Py_exc_state
#include "pycore_floatobject.h" // struct _Py_float_state
#include "pycore_function.h" // FUNC_MAX_WATCHERS
Expand Down Expand Up @@ -139,6 +140,7 @@ struct _is {
struct _Py_unicode_state unicode;
struct _Py_float_state float_state;
struct _Py_long_state long_state;
struct _dtoa_state dtoa;
/* Using a cache is very effective since typically only a single slice is
created and then deleted again. */
PySliceObject *slice_cache;
Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ extern "C" {
#include "pycore_atomic.h" /* _Py_atomic_address */
#include "pycore_ceval_state.h" // struct _ceval_runtime_state
#include "pycore_dict_state.h" // struct _Py_dict_runtime_state
#include "pycore_dtoa.h" // struct _dtoa_runtime_state
#include "pycore_floatobject.h" // struct _Py_float_runtime_state
#include "pycore_faulthandler.h" // struct _faulthandler_runtime_state
#include "pycore_function.h" // struct _func_runtime_state
Expand Down Expand Up @@ -141,7 +140,6 @@ typedef struct pyruntimestate {
struct _ceval_runtime_state ceval;
struct _gilstate_runtime_state gilstate;
struct _getargs_runtime_state getargs;
struct _dtoa_runtime_state dtoa;
struct _fileutils_state fileutils;
struct _faulthandler_runtime_state faulthandler;
struct _tracemalloc_runtime_state tracemalloc;
Expand Down
6 changes: 3 additions & 3 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ extern "C" {
.gilstate = { \
.check_enabled = 1, \
}, \
.dtoa = _dtoa_runtime_state_INIT(runtime), \
.fileutils = { \
.force_ascii = -1, \
}, \
Expand Down Expand Up @@ -94,10 +93,10 @@ extern "C" {
}, \
}, \
}, \
._main_interpreter = _PyInterpreterState_INIT, \
._main_interpreter = _PyInterpreterState_INIT(runtime._main_interpreter), \
}

#define _PyInterpreterState_INIT \
#define _PyInterpreterState_INIT(INTERP) \
{ \
.id_refcount = -1, \
.imports = IMPORTS_INIT, \
Expand All @@ -113,6 +112,7 @@ extern "C" {
{ .threshold = 10, }, \
}, \
}, \
.dtoa = _dtoa_state_INIT(&(INTERP)), \
.static_objects = { \
.singletons = { \
._not_used = 1, \
Expand Down
15 changes: 9 additions & 6 deletions Python/dtoa.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@

#include "Python.h"
#include "pycore_dtoa.h" // _PY_SHORT_FLOAT_REPR
#include "pycore_runtime.h" // _PyRuntime
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include <stdlib.h> // exit()

/* if _PY_SHORT_FLOAT_REPR == 0, then don't even try to compile
Expand Down Expand Up @@ -339,9 +339,9 @@ typedef struct Bigint Bigint;
Bfree to PyMem_Free. Investigate whether this has any significant
performance on impact. */

#define freelist _PyRuntime.dtoa.freelist
#define private_mem _PyRuntime.dtoa.preallocated
#define pmem_next _PyRuntime.dtoa.preallocated_next
#define freelist interp->dtoa.freelist
#define private_mem interp->dtoa.preallocated
#define pmem_next interp->dtoa.preallocated_next

/* Allocate space for a Bigint with up to 1<<k digits */

Expand All @@ -351,6 +351,7 @@ Balloc(int k)
int x;
Bigint *rv;
unsigned int len;
PyInterpreterState *interp = _PyInterpreterState_GET();

if (k <= Bigint_Kmax && (rv = freelist[k]))
freelist[k] = rv->next;
Expand Down Expand Up @@ -385,6 +386,7 @@ Bfree(Bigint *v)
if (v->k > Bigint_Kmax)
FREE((void*)v);
else {
PyInterpreterState *interp = _PyInterpreterState_GET();
v->next = freelist[v->k];
freelist[v->k] = v;
}
Expand Down Expand Up @@ -692,15 +694,16 @@ pow5mult(Bigint *b, int k)

if (!(k >>= 2))
return b;
p5 = _PyRuntime.dtoa.p5s;
PyInterpreterState *interp = _PyInterpreterState_GET();
p5 = interp->dtoa.p5s;
if (!p5) {
/* first time */
p5 = i2b(625);
if (p5 == NULL) {
Bfree(b);
return NULL;
}
_PyRuntime.dtoa.p5s = p5;
interp->dtoa.p5s = p5;
p5->next = 0;
}
for(;;) {
Expand Down
20 changes: 19 additions & 1 deletion Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

#include "Python.h"
#include "pycore_ceval.h"
#include "pycore_code.h" // stats
#include "pycore_code.h" // stats
#include "pycore_dtoa.h" // _dtoa_state_INIT()
#include "pycore_frame.h"
#include "pycore_initconfig.h"
#include "pycore_object.h" // _PyType_InitCache()
Expand Down Expand Up @@ -618,6 +619,18 @@ free_interpreter(PyInterpreterState *interp)
e.g. by PyMem_RawCalloc() or memset(), or otherwise pre-initialized.
The runtime state is not manipulated. Instead it is assumed that
the interpreter is getting added to the runtime.

Note that the main interpreter was statically initialized as part
of the runtime and most state is already set properly. That leaves
a small number of fields to initialize dynamically, as well as some
that are initialized lazily.

For subinterpreters we memcpy() the main interpreter in
PyInterpreterState_New(), leaving it in the same mostly-initialized
state. The only difference is that the interpreter has some
self-referential state that is statically initializexd to the
main interpreter. We fix those fields here, in addition
to the other dynamically initialized fields.
*/

static void
Expand Down Expand Up @@ -645,6 +658,11 @@ init_interpreter(PyInterpreterState *interp,
PyConfig_InitPythonConfig(&interp->config);
_PyType_InitCache(interp);

if (interp != &runtime->_main_interpreter) {
/* Fix the self-referential, statically initialized fields. */
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
}

interp->_initialized = 1;
}

Expand Down