Skip to content

Commit bf70dab

Browse files
authored
Use fast Python wrappers in native_internal (#19765)
This makes fixed format cache significantly faster in _interpreted_ mypy (which is important e.g. for tests). In fact, `load_tree_time` with this PR is 15% faster than when using `orjson`.
1 parent 1660a39 commit bf70dab

File tree

2 files changed

+83
-57
lines changed

2 files changed

+83
-57
lines changed

mypyc/lib-rt/native_internal.c

Lines changed: 75 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,13 @@ read_bool_internal(PyObject *data) {
206206
}
207207

208208
static PyObject*
209-
read_bool(PyObject *self, PyObject *args, PyObject *kwds) {
210-
static char *kwlist[] = {"data", NULL};
211-
PyObject *data = NULL;
212-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
209+
read_bool(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
210+
static const char * const kwlist[] = {"data", 0};
211+
static CPyArg_Parser parser = {"O:read_bool", kwlist, 0};
212+
PyObject *data;
213+
if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
213214
return NULL;
215+
}
214216
char res = read_bool_internal(data);
215217
if (unlikely(res == CPY_BOOL_ERROR))
216218
return NULL;
@@ -229,12 +231,14 @@ write_bool_internal(PyObject *data, char value) {
229231
}
230232

231233
static PyObject*
232-
write_bool(PyObject *self, PyObject *args, PyObject *kwds) {
233-
static char *kwlist[] = {"data", "value", NULL};
234-
PyObject *data = NULL;
235-
PyObject *value = NULL;
236-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
234+
write_bool(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
235+
static const char * const kwlist[] = {"data", "value", 0};
236+
static CPyArg_Parser parser = {"OO:write_bool", kwlist, 0};
237+
PyObject *data;
238+
PyObject *value;
239+
if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
237240
return NULL;
241+
}
238242
if (unlikely(!PyBool_Check(value))) {
239243
PyErr_SetString(PyExc_TypeError, "value must be a bool");
240244
return NULL;
@@ -280,11 +284,13 @@ read_str_internal(PyObject *data) {
280284
}
281285

282286
static PyObject*
283-
read_str(PyObject *self, PyObject *args, PyObject *kwds) {
284-
static char *kwlist[] = {"data", NULL};
285-
PyObject *data = NULL;
286-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
287+
read_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
288+
static const char * const kwlist[] = {"data", 0};
289+
static CPyArg_Parser parser = {"O:read_str", kwlist, 0};
290+
PyObject *data;
291+
if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
287292
return NULL;
293+
}
288294
return read_str_internal(data);
289295
}
290296

@@ -319,12 +325,14 @@ write_str_internal(PyObject *data, PyObject *value) {
319325
}
320326

321327
static PyObject*
322-
write_str(PyObject *self, PyObject *args, PyObject *kwds) {
323-
static char *kwlist[] = {"data", "value", NULL};
324-
PyObject *data = NULL;
325-
PyObject *value = NULL;
326-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
328+
write_str(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
329+
static const char * const kwlist[] = {"data", "value", 0};
330+
static CPyArg_Parser parser = {"OO:write_str", kwlist, 0};
331+
PyObject *data;
332+
PyObject *value;
333+
if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
327334
return NULL;
335+
}
328336
if (unlikely(!PyUnicode_Check(value))) {
329337
PyErr_SetString(PyExc_TypeError, "value must be a str");
330338
return NULL;
@@ -350,11 +358,13 @@ read_float_internal(PyObject *data) {
350358
}
351359

352360
static PyObject*
353-
read_float(PyObject *self, PyObject *args, PyObject *kwds) {
354-
static char *kwlist[] = {"data", NULL};
355-
PyObject *data = NULL;
356-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
361+
read_float(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
362+
static const char * const kwlist[] = {"data", 0};
363+
static CPyArg_Parser parser = {"O:read_float", kwlist, 0};
364+
PyObject *data;
365+
if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
357366
return NULL;
367+
}
358368
double retval = read_float_internal(data);
359369
if (unlikely(retval == CPY_FLOAT_ERROR && PyErr_Occurred())) {
360370
return NULL;
@@ -372,12 +382,14 @@ write_float_internal(PyObject *data, double value) {
372382
}
373383

374384
static PyObject*
375-
write_float(PyObject *self, PyObject *args, PyObject *kwds) {
376-
static char *kwlist[] = {"data", "value", NULL};
377-
PyObject *data = NULL;
378-
PyObject *value = NULL;
379-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
385+
write_float(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
386+
static const char * const kwlist[] = {"data", "value", 0};
387+
static CPyArg_Parser parser = {"OO:write_float", kwlist, 0};
388+
PyObject *data;
389+
PyObject *value;
390+
if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
380391
return NULL;
392+
}
381393
if (unlikely(!PyFloat_Check(value))) {
382394
PyErr_SetString(PyExc_TypeError, "value must be a float");
383395
return NULL;
@@ -421,11 +433,13 @@ read_int_internal(PyObject *data) {
421433
}
422434

423435
static PyObject*
424-
read_int(PyObject *self, PyObject *args, PyObject *kwds) {
425-
static char *kwlist[] = {"data", NULL};
426-
PyObject *data = NULL;
427-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
436+
read_int(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
437+
static const char * const kwlist[] = {"data", 0};
438+
static CPyArg_Parser parser = {"O:read_int", kwlist, 0};
439+
PyObject *data;
440+
if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
428441
return NULL;
442+
}
429443
CPyTagged retval = read_int_internal(data);
430444
if (unlikely(retval == CPY_INT_TAG)) {
431445
return NULL;
@@ -466,12 +480,14 @@ write_int_internal(PyObject *data, CPyTagged value) {
466480
}
467481

468482
static PyObject*
469-
write_int(PyObject *self, PyObject *args, PyObject *kwds) {
470-
static char *kwlist[] = {"data", "value", NULL};
471-
PyObject *data = NULL;
472-
PyObject *value = NULL;
473-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
483+
write_int(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
484+
static const char * const kwlist[] = {"data", "value", 0};
485+
static CPyArg_Parser parser = {"OO:write_int", kwlist, 0};
486+
PyObject *data;
487+
PyObject *value;
488+
if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
474489
return NULL;
490+
}
475491
if (unlikely(!PyLong_Check(value))) {
476492
PyErr_SetString(PyExc_TypeError, "value must be an int");
477493
return NULL;
@@ -498,11 +514,13 @@ read_tag_internal(PyObject *data) {
498514
}
499515

500516
static PyObject*
501-
read_tag(PyObject *self, PyObject *args, PyObject *kwds) {
502-
static char *kwlist[] = {"data", NULL};
503-
PyObject *data = NULL;
504-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &data)))
517+
read_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
518+
static const char * const kwlist[] = {"data", 0};
519+
static CPyArg_Parser parser = {"O:read_tag", kwlist, 0};
520+
PyObject *data;
521+
if (unlikely(!CPyArg_ParseStackAndKeywordsOneArg(args, nargs, kwnames, &parser, &data))) {
505522
return NULL;
523+
}
506524
uint8_t retval = read_tag_internal(data);
507525
if (unlikely(retval == CPY_LL_UINT_ERROR && PyErr_Occurred())) {
508526
return NULL;
@@ -520,12 +538,14 @@ write_tag_internal(PyObject *data, uint8_t value) {
520538
}
521539

522540
static PyObject*
523-
write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
524-
static char *kwlist[] = {"data", "value", NULL};
525-
PyObject *data = NULL;
526-
PyObject *value = NULL;
527-
if (unlikely(!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &data, &value)))
541+
write_tag(PyObject *self, PyObject *const *args, size_t nargs, PyObject *kwnames) {
542+
static const char * const kwlist[] = {"data", "value", 0};
543+
static CPyArg_Parser parser = {"OO:write_tag", kwlist, 0};
544+
PyObject *data;
545+
PyObject *value;
546+
if (unlikely(!CPyArg_ParseStackAndKeywordsSimple(args, nargs, kwnames, &parser, &data, &value))) {
528547
return NULL;
548+
}
529549
uint8_t unboxed = CPyLong_AsUInt8(value);
530550
if (unlikely(unboxed == CPY_LL_UINT_ERROR && PyErr_Occurred())) {
531551
CPy_TypeError("u8", value);
@@ -539,17 +559,16 @@ write_tag(PyObject *self, PyObject *args, PyObject *kwds) {
539559
}
540560

541561
static PyMethodDef native_internal_module_methods[] = {
542-
// TODO: switch public wrappers to METH_FASTCALL.
543-
{"write_bool", (PyCFunction)write_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a bool")},
544-
{"read_bool", (PyCFunction)read_bool, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a bool")},
545-
{"write_str", (PyCFunction)write_str, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a string")},
546-
{"read_str", (PyCFunction)read_str, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a string")},
547-
{"write_float", (PyCFunction)write_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a float")},
548-
{"read_float", (PyCFunction)read_float, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a float")},
549-
{"write_int", (PyCFunction)write_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write an int")},
550-
{"read_int", (PyCFunction)read_int, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read an int")},
551-
{"write_tag", (PyCFunction)write_tag, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("write a short int")},
552-
{"read_tag", (PyCFunction)read_tag, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read a short int")},
562+
{"write_bool", (PyCFunction)write_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a bool")},
563+
{"read_bool", (PyCFunction)read_bool, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a bool")},
564+
{"write_str", (PyCFunction)write_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a string")},
565+
{"read_str", (PyCFunction)read_str, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a string")},
566+
{"write_float", (PyCFunction)write_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a float")},
567+
{"read_float", (PyCFunction)read_float, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a float")},
568+
{"write_int", (PyCFunction)write_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write an int")},
569+
{"read_int", (PyCFunction)read_int, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read an int")},
570+
{"write_tag", (PyCFunction)write_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("write a short int")},
571+
{"read_tag", (PyCFunction)read_tag, METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("read a short int")},
553572
{NULL, NULL, 0, NULL}
554573
};
555574

mypyc/lib-rt/setup.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,14 @@ def run(self) -> None:
7878
ext_modules=[
7979
Extension(
8080
"native_internal",
81-
["native_internal.c", "init.c", "int_ops.c", "exc_ops.c", "pythonsupport.c"],
81+
[
82+
"native_internal.c",
83+
"init.c",
84+
"int_ops.c",
85+
"exc_ops.c",
86+
"pythonsupport.c",
87+
"getargsfast.c",
88+
],
8289
include_dirs=["."],
8390
)
8491
],

0 commit comments

Comments
 (0)