Skip to content

Commit 849288b

Browse files
committed
Add CPython parser benchmark C source (unused extension attempt)
Early attempt at a C extension module for benchmarking CPython's PEG parser. Superseded by the standalone bench_cpython_parser.c program, but kept for reference. https://claude.ai/code/session_0116H8dSsjY7pmMiZs5WWjF3
1 parent d93e463 commit 849288b

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

Modules/_parsebench.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* _parsebench: C extension module that benchmarks CPython's internal PEG parser.
3+
*
4+
* Calls _PyParser_ASTFromString() which produces mod_ty (C struct),
5+
* WITHOUT creating Python AST objects. This measures the raw C-level
6+
* parsing cost only.
7+
*/
8+
9+
#define Py_BUILD_CORE
10+
11+
#include "Python.h"
12+
#include "pycore_parser.h"
13+
#include "pycore_pyarena.h"
14+
15+
#include <time.h>
16+
17+
/*
18+
* parse_raw(source: str) -> (time_us: int, has_error: bool)
19+
*
20+
* Parse source as a module, returning (elapsed_microseconds, had_error).
21+
* Only measures the C-level parse (mod_ty creation), not Python AST objects.
22+
*/
23+
static PyObject *
24+
parsebench_parse_raw(PyObject *self, PyObject *args)
25+
{
26+
const char *source;
27+
Py_ssize_t source_len;
28+
29+
if (!PyArg_ParseTuple(args, "s#", &source, &source_len))
30+
return NULL;
31+
32+
PyObject *filename = PyUnicode_FromString("<bench>");
33+
if (filename == NULL)
34+
return NULL;
35+
36+
PyCompilerFlags cf = _PyCompilerFlags_INIT;
37+
cf.cf_flags = PyCF_SOURCE_IS_UTF8 | PyCF_IGNORE_COOKIE;
38+
39+
PyArena *arena = _PyArena_New();
40+
if (arena == NULL) {
41+
Py_DECREF(filename);
42+
return NULL;
43+
}
44+
45+
struct timespec start, end;
46+
clock_gettime(CLOCK_MONOTONIC, &start);
47+
48+
mod_ty mod = _PyParser_ASTFromString(source, filename, Py_file_input,
49+
&cf, arena);
50+
51+
clock_gettime(CLOCK_MONOTONIC, &end);
52+
53+
int has_error = (mod == NULL);
54+
if (has_error) {
55+
/* Clear the exception — we just want to record the error */
56+
PyErr_Clear();
57+
}
58+
59+
_PyArena_Free(arena);
60+
Py_DECREF(filename);
61+
62+
long long elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000LL
63+
+ (end.tv_nsec - start.tv_nsec);
64+
long long elapsed_us = elapsed_ns / 1000;
65+
66+
return Py_BuildValue("(Li)", elapsed_us, has_error);
67+
}
68+
69+
static PyMethodDef parsebench_methods[] = {
70+
{"parse_raw", parsebench_parse_raw, METH_VARARGS,
71+
"Parse source at C level (mod_ty only), return (time_us, has_error)."},
72+
{NULL, NULL, 0, NULL}
73+
};
74+
75+
static struct PyModuleDef parsebench_module = {
76+
PyModuleDef_HEAD_INIT,
77+
"_parsebench",
78+
"Benchmark CPython's internal PEG parser at the C struct level.",
79+
-1,
80+
parsebench_methods
81+
};
82+
83+
PyMODINIT_FUNC
84+
PyInit__parsebench(void)
85+
{
86+
return PyModule_Create(&parsebench_module);
87+
}

0 commit comments

Comments
 (0)