Merge pull request #639 from Carbaz/feature/python-c-extension-generator
Python C extension generator using OpenAI GPT-5
This commit is contained in:
21
week4/community-contributions/c_extension_generator/LICENSE
Normal file
21
week4/community-contributions/c_extension_generator/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Carlos Bazaga
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
134
week4/community-contributions/c_extension_generator/README.md
Normal file
134
week4/community-contributions/c_extension_generator/README.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# Python C Extension code generator
|
||||||
|
|
||||||
|
Written by Carlos Bazaga [@carbaz] based on the work of Ed Donner [@ed-donner]
|
||||||
|
under the MIT License.
|
||||||
|
|
||||||
|
This folder contains a Jupyter notebook that demonstrates how to use a Frontier model
|
||||||
|
to generate high-performance Python C extension code from Python code.
|
||||||
|
|
||||||
|
The notebook includes examples of generating C extensions for calculating Pi using the
|
||||||
|
Leibniz formula and finding the maximum sub-array in an array.
|
||||||
|
|
||||||
|
Also, it includes a Gradio app that provides an interactive interface for users to input
|
||||||
|
Python code, generate C extension code, compile it, and test its performance against
|
||||||
|
the original Python code.
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
>
|
||||||
|
> **Always review the generated codes before running them, as they will be executed in
|
||||||
|
> your local environment and may contain code that could be harmful or unwanted.**
|
||||||
|
>
|
||||||
|
> AI-generated code may contain errors or unsafe practices, so it's crucial to
|
||||||
|
> thoroughly review and test any code before using it in a production environment.
|
||||||
|
>
|
||||||
|
> Never run code generated by AI models without understanding its implications and
|
||||||
|
> ensuring it adheres to your security and safety standards.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
>
|
||||||
|
> **Disclaimer:** This notebook and the Gradio app are provided for educational purposes
|
||||||
|
> only. Use them at your own risk.
|
||||||
|
|
||||||
|
## Gradio app overview
|
||||||
|
|
||||||
|
In this image, you can see the Gradio app dashboard whose main sections are
|
||||||
|
described below.
|
||||||
|
|
||||||
|
\
|
||||||
|
*Image: Gradio app dashboard with default example `hello world` code loaded.*
|
||||||
|
*(compile output redacted for privacy)*
|
||||||
|
|
||||||
|
Sections:
|
||||||
|
|
||||||
|
* **Dropdown selectors and input fields**:
|
||||||
|
* **Module name input**:
|
||||||
|
A text input field where users can specify the name of the C extension module to be
|
||||||
|
generated.
|
||||||
|
|
||||||
|
That name will be used to create the C extension file `<module_name>.c` and
|
||||||
|
the `setup.py` file required to compile the extension.
|
||||||
|
|
||||||
|
That name will also be used to import the compiled module as usual in Python:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import <module_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
```python
|
||||||
|
from <module_name> import <function_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Model selector**:
|
||||||
|
A dropdown menu to select the Frontier model to use for code generation.
|
||||||
|
|
||||||
|
Currently it includes:
|
||||||
|
* `gpt-4o` (default)
|
||||||
|
* `gpt-5`
|
||||||
|
|
||||||
|
* **Text input areas**:
|
||||||
|
|
||||||
|
This areas are all editable, included those filled with generated code by the model.
|
||||||
|
this allows users to modify and experiment with the code as needed.
|
||||||
|
|
||||||
|
* **Python code**:
|
||||||
|
A text area where users can input their Python code.
|
||||||
|
* **C extension code**:
|
||||||
|
A text area that displays the generated C extension code and allows to edit it.
|
||||||
|
* **Compilation code**:
|
||||||
|
A text area that shows the `setup.py` file generated,
|
||||||
|
this file is required to compile the C extension.
|
||||||
|
* **Test compare code**:
|
||||||
|
A text area that provides example code to run the compiled C extension.
|
||||||
|
|
||||||
|
* **Output areas**:
|
||||||
|
|
||||||
|
This are non-editable areas that display the results of various operations.
|
||||||
|
|
||||||
|
* **C Extension result**:
|
||||||
|
A text area that displays the output of the C extension code build.
|
||||||
|
|
||||||
|
Beware that this area can contain a large amount of text including warnings during
|
||||||
|
the compilation process and sensible information about the local environment,
|
||||||
|
like: paths, Python version, etc may be included.
|
||||||
|
|
||||||
|
Redact that information if you plan to share the output.
|
||||||
|
|
||||||
|
* **Test result**:
|
||||||
|
A text area that displays the output of the test code run.
|
||||||
|
|
||||||
|
* **Buttons**:
|
||||||
|
* **Generate extension code**:
|
||||||
|
A button that triggers the generation of the C extension code from the provided
|
||||||
|
Python code.
|
||||||
|
|
||||||
|
It will call the Frontier model to generate the C code, the setup.py file and
|
||||||
|
the test code, filling the corresponding text areas automatically.
|
||||||
|
|
||||||
|
* **Compile extension**:
|
||||||
|
A button that compiles the generated C extension using the provided `setup.py` file.
|
||||||
|
It will create the extension c file, `<module_name>.c` and the `setup.py` files in
|
||||||
|
the local folder, then it will run the compilation command and build the C extension.
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
>
|
||||||
|
> **Always review the `setup.py` code before running it, as it will be executed in
|
||||||
|
> your local environment and may contain code that could be harmful or unwanted.**
|
||||||
|
>
|
||||||
|
> **Also review the generated C code, as it will be compiled and executed in your
|
||||||
|
> local environment and may contain code that could be harmful or unwanted.**
|
||||||
|
|
||||||
|
It will display the compilation output in the "C Extension result" area.
|
||||||
|
|
||||||
|
* **Test code**:
|
||||||
|
A button that executes the test code to compare the performance of the original
|
||||||
|
Python code and the generated C extension.
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
>
|
||||||
|
> **Always review the test code before running it, as it will be executed in
|
||||||
|
> your local environment and may contain code that could be harmful or unwanted.**
|
||||||
|
|
||||||
|
Will save the test code provided in the "Test compare code" into the
|
||||||
|
`usage_example.py` file and execute it showing the output in the "Test result" area.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static PyObject* leibniz_pi(PyObject* self, PyObject* args) {
|
||||||
|
PyObject* iterations_obj;
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &iterations_obj)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long n_signed;
|
||||||
|
int overflow = 0;
|
||||||
|
n_signed = PyLong_AsLongLongAndOverflow(iterations_obj, &overflow);
|
||||||
|
if (n_signed == -1 && PyErr_Occurred() && overflow == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long n = 0ULL;
|
||||||
|
if (overflow < 0) {
|
||||||
|
n = 0ULL;
|
||||||
|
} else if (overflow > 0) {
|
||||||
|
unsigned long long tmp = PyLong_AsUnsignedLongLong(iterations_obj);
|
||||||
|
if (tmp == (unsigned long long)-1 && PyErr_Occurred()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
n = tmp;
|
||||||
|
} else {
|
||||||
|
if (n_signed <= 0) {
|
||||||
|
n = 0ULL;
|
||||||
|
} else {
|
||||||
|
n = (unsigned long long)n_signed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double result = 1.0;
|
||||||
|
if (n == 0ULL) {
|
||||||
|
return PyFloat_FromDouble(result * 4.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
for (unsigned long long i = 1ULL; i <= n; ++i) {
|
||||||
|
double jd1;
|
||||||
|
if (i <= ULLONG_MAX / 4ULL) {
|
||||||
|
unsigned long long j1 = i * 4ULL - 1ULL;
|
||||||
|
jd1 = (double)j1;
|
||||||
|
} else {
|
||||||
|
jd1 = (double)i * 4.0 - 1.0;
|
||||||
|
}
|
||||||
|
result -= 1.0 / jd1;
|
||||||
|
|
||||||
|
double jd2;
|
||||||
|
if (i <= (ULLONG_MAX - 1ULL) / 4ULL) {
|
||||||
|
unsigned long long j2 = i * 4ULL + 1ULL;
|
||||||
|
jd2 = (double)j2;
|
||||||
|
} else {
|
||||||
|
jd2 = (double)i * 4.0 + 1.0;
|
||||||
|
}
|
||||||
|
result += 1.0 / jd2;
|
||||||
|
}
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
return PyFloat_FromDouble(result * 4.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef CalculatePiMethods[] = {
|
||||||
|
{"leibniz_pi", leibniz_pi, METH_VARARGS, "Compute pi using the Leibniz series with the given number of iterations."},
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PyModuleDef calculate_pimodule = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"calculate_pi",
|
||||||
|
"High-performance Leibniz pi calculation.",
|
||||||
|
-1,
|
||||||
|
CalculatePiMethods
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC PyInit_calculate_pi(void) {
|
||||||
|
return PyModule_Create(&calculate_pimodule);
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 243 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
|||||||
|
#include <Python.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
// LCG step with 32-bit wrap-around
|
||||||
|
static inline uint32_t lcg_next(uint32_t *state) {
|
||||||
|
*state = (uint32_t)(1664525u * (*state) + 1013904223u);
|
||||||
|
return *state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int add_overflow_int64(int64_t a, int64_t b, int64_t *res) {
|
||||||
|
if ((b > 0 && a > INT64_MAX - b) || (b < 0 && a < INT64_MIN - b)) return 1;
|
||||||
|
*res = a + b;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kadane for int64 array with overflow detection; returns PyLong or NULL (on overflow -> signal via *overflowed)
|
||||||
|
static PyObject* kadane_int64(const int64_t *arr, Py_ssize_t n, int *overflowed) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return PyFloat_FromDouble(-INFINITY);
|
||||||
|
}
|
||||||
|
int64_t meh = arr[0];
|
||||||
|
int64_t msf = arr[0];
|
||||||
|
for (Py_ssize_t i = 1; i < n; ++i) {
|
||||||
|
int64_t x = arr[i];
|
||||||
|
if (meh > 0) {
|
||||||
|
int64_t tmp;
|
||||||
|
if (add_overflow_int64(meh, x, &tmp)) { *overflowed = 1; return NULL; }
|
||||||
|
meh = tmp;
|
||||||
|
} else {
|
||||||
|
meh = x;
|
||||||
|
}
|
||||||
|
if (meh > msf) msf = meh;
|
||||||
|
}
|
||||||
|
return PyLong_FromLongLong(msf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kadane for PyObject* integer array
|
||||||
|
static PyObject* kadane_big(PyObject **arr, Py_ssize_t n) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return PyFloat_FromDouble(-INFINITY);
|
||||||
|
}
|
||||||
|
PyObject *meh = arr[0]; Py_INCREF(meh);
|
||||||
|
PyObject *msf = arr[0]; Py_INCREF(msf);
|
||||||
|
PyObject *zero = PyLong_FromLong(0);
|
||||||
|
if (!zero) { Py_DECREF(meh); Py_DECREF(msf); return NULL; }
|
||||||
|
|
||||||
|
for (Py_ssize_t i = 1; i < n; ++i) {
|
||||||
|
int cmp = PyObject_RichCompareBool(meh, zero, Py_GT);
|
||||||
|
if (cmp < 0) { Py_DECREF(meh); Py_DECREF(msf); Py_DECREF(zero); return NULL; }
|
||||||
|
if (cmp == 1) {
|
||||||
|
PyObject *t = PyNumber_Add(meh, arr[i]);
|
||||||
|
if (!t) { Py_DECREF(meh); Py_DECREF(msf); Py_DECREF(zero); return NULL; }
|
||||||
|
Py_DECREF(meh);
|
||||||
|
meh = t;
|
||||||
|
} else {
|
||||||
|
Py_DECREF(meh);
|
||||||
|
meh = arr[i]; Py_INCREF(meh);
|
||||||
|
}
|
||||||
|
int cmp2 = PyObject_RichCompareBool(meh, msf, Py_GT);
|
||||||
|
if (cmp2 < 0) { Py_DECREF(meh); Py_DECREF(msf); Py_DECREF(zero); return NULL; }
|
||||||
|
if (cmp2 == 1) {
|
||||||
|
Py_DECREF(msf);
|
||||||
|
msf = meh; Py_INCREF(msf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(meh);
|
||||||
|
Py_DECREF(zero);
|
||||||
|
return msf; // new reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate int64 array fast path; returns 0 on success
|
||||||
|
static int gen_array_int64(Py_ssize_t n, uint32_t seed, int64_t min_v, int64_t max_v, int64_t *out) {
|
||||||
|
uint32_t state = seed;
|
||||||
|
uint64_t umax = (uint64_t)max_v;
|
||||||
|
uint64_t umin = (uint64_t)min_v;
|
||||||
|
uint64_t range = (umax - umin) + 1ULL; // max>=min guaranteed by caller
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||||
|
state = lcg_next(&state);
|
||||||
|
uint32_t r32 = state;
|
||||||
|
uint64_t r = (range > 0x100000000ULL) ? (uint64_t)r32 : ((uint64_t)r32 % range);
|
||||||
|
int64_t val = (int64_t)(min_v + (int64_t)r);
|
||||||
|
out[i] = val;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate PyObject* int array general path using Python arithmetic
|
||||||
|
static PyObject** gen_array_big(Py_ssize_t n, uint32_t seed, PyObject *min_val, PyObject *max_val) {
|
||||||
|
PyObject **arr = (PyObject**)PyMem_Malloc((n > 0 ? n : 1) * sizeof(PyObject*));
|
||||||
|
if (!arr) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *one = PyLong_FromLong(1);
|
||||||
|
if (!one) { PyMem_Free(arr); return NULL; }
|
||||||
|
PyObject *diff = PyNumber_Subtract(max_val, min_val);
|
||||||
|
if (!diff) { Py_DECREF(one); PyMem_Free(arr); return NULL; }
|
||||||
|
PyObject *range_obj = PyNumber_Add(diff, one);
|
||||||
|
Py_DECREF(diff);
|
||||||
|
Py_DECREF(one);
|
||||||
|
if (!range_obj) { PyMem_Free(arr); return NULL; }
|
||||||
|
|
||||||
|
uint32_t state = seed;
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||||
|
state = lcg_next(&state);
|
||||||
|
PyObject *v = PyLong_FromUnsignedLong((unsigned long)state);
|
||||||
|
if (!v) {
|
||||||
|
Py_DECREF(range_obj);
|
||||||
|
for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr[k]);
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *r = PyNumber_Remainder(v, range_obj);
|
||||||
|
Py_DECREF(v);
|
||||||
|
if (!r) {
|
||||||
|
Py_DECREF(range_obj);
|
||||||
|
for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr[k]);
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *val = PyNumber_Add(r, min_val);
|
||||||
|
Py_DECREF(r);
|
||||||
|
if (!val) {
|
||||||
|
Py_DECREF(range_obj);
|
||||||
|
for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr[k]);
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
arr[i] = val;
|
||||||
|
}
|
||||||
|
Py_DECREF(range_obj);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* max_subarray_sum_internal(Py_ssize_t n, uint32_t seed, PyObject *min_val, PyObject *max_val) {
|
||||||
|
if (n <= 0) {
|
||||||
|
return PyFloat_FromDouble(-INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyLong_Check(min_val) && PyLong_Check(max_val)) {
|
||||||
|
int overflow1 = 0, overflow2 = 0;
|
||||||
|
long long min64 = PyLong_AsLongLongAndOverflow(min_val, &overflow1);
|
||||||
|
if (overflow1) goto BIGINT_PATH;
|
||||||
|
long long max64 = PyLong_AsLongLongAndOverflow(max_val, &overflow2);
|
||||||
|
if (overflow2) goto BIGINT_PATH;
|
||||||
|
if (max64 >= min64) {
|
||||||
|
int64_t *arr = (int64_t*)PyMem_Malloc((size_t)n * sizeof(int64_t));
|
||||||
|
if (!arr) { PyErr_NoMemory(); return NULL; }
|
||||||
|
if (gen_array_int64(n, seed, (int64_t)min64, (int64_t)max64, arr) != 0) {
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int overflowed = 0;
|
||||||
|
PyObject *res = kadane_int64(arr, n, &overflowed);
|
||||||
|
if (!res && overflowed) {
|
||||||
|
// fallback to big-int Kadane
|
||||||
|
PyObject **arr_obj = (PyObject**)PyMem_Malloc((size_t)n * sizeof(PyObject*));
|
||||||
|
if (!arr_obj) { PyMem_Free(arr); PyErr_NoMemory(); return NULL; }
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||||
|
arr_obj[i] = PyLong_FromLongLong(arr[i]);
|
||||||
|
if (!arr_obj[i]) {
|
||||||
|
for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr_obj[k]);
|
||||||
|
PyMem_Free(arr_obj);
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyObject *bires = kadane_big(arr_obj, n);
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) Py_DECREF(arr_obj[i]);
|
||||||
|
PyMem_Free(arr_obj);
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return bires;
|
||||||
|
}
|
||||||
|
PyMem_Free(arr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BIGINT_PATH: ;
|
||||||
|
PyObject **arr_obj = gen_array_big(n, seed, min_val, max_val);
|
||||||
|
if (!arr_obj) return NULL;
|
||||||
|
PyObject *res = kadane_big(arr_obj, n);
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) Py_DECREF(arr_obj[i]);
|
||||||
|
PyMem_Free(arr_obj);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* py_max_subarray_sum(PyObject *self, PyObject *args) {
|
||||||
|
Py_ssize_t n;
|
||||||
|
PyObject *seed_obj, *min_val, *max_val;
|
||||||
|
if (!PyArg_ParseTuple(args, "nOOO", &n, &seed_obj, &min_val, &max_val)) return NULL;
|
||||||
|
if (n < 0) n = 0;
|
||||||
|
uint32_t seed = (uint32_t)(PyLong_AsUnsignedLongLongMask(seed_obj) & 0xFFFFFFFFULL);
|
||||||
|
if (PyErr_Occurred()) return NULL;
|
||||||
|
return max_subarray_sum_internal(n, seed, min_val, max_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* py_total_max_subarray_sum(PyObject *self, PyObject *args) {
|
||||||
|
Py_ssize_t n;
|
||||||
|
PyObject *init_seed_obj, *min_val, *max_val;
|
||||||
|
if (!PyArg_ParseTuple(args, "nOOO", &n, &init_seed_obj, &min_val, &max_val)) return NULL;
|
||||||
|
if (n < 0) n = 0;
|
||||||
|
uint32_t state = (uint32_t)(PyLong_AsUnsignedLongLongMask(init_seed_obj) & 0xFFFFFFFFULL);
|
||||||
|
if (PyErr_Occurred()) return NULL;
|
||||||
|
|
||||||
|
PyObject *total = PyLong_FromLong(0);
|
||||||
|
if (!total) return NULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; ++i) {
|
||||||
|
uint32_t seed = lcg_next(&state);
|
||||||
|
PyObject *part = max_subarray_sum_internal(n, seed, min_val, max_val);
|
||||||
|
if (!part) { Py_DECREF(total); return NULL; }
|
||||||
|
PyObject *new_total = PyNumber_Add(total, part);
|
||||||
|
Py_DECREF(part);
|
||||||
|
if (!new_total) { Py_DECREF(total); return NULL; }
|
||||||
|
Py_DECREF(total);
|
||||||
|
total = new_total;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
{"max_subarray_sum", (PyCFunction)py_max_subarray_sum, METH_VARARGS, "Compute maximum subarray sum using LCG-generated array."},
|
||||||
|
{"total_max_subarray_sum", (PyCFunction)py_total_max_subarray_sum, METH_VARARGS, "Compute total of maximum subarray sums over 20 LCG seeds."},
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PyModuleDef moduledef = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"python_hard",
|
||||||
|
NULL,
|
||||||
|
-1,
|
||||||
|
module_methods,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC PyInit_python_hard(void) {
|
||||||
|
return PyModule_Create(&moduledef);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from setuptools import setup, Extension
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
extra_compile_args = []
|
||||||
|
extra_link_args = []
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
extra_compile_args.extend(['/O2', '/fp:precise'])
|
||||||
|
else:
|
||||||
|
extra_compile_args.extend(['-O3', '-fno-strict-aliasing'])
|
||||||
|
|
||||||
|
module = Extension(
|
||||||
|
'calculate_pi',
|
||||||
|
sources=['calculate_pi.c'],
|
||||||
|
extra_compile_args=extra_compile_args,
|
||||||
|
extra_link_args=extra_link_args,
|
||||||
|
)
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='calculate_pi',
|
||||||
|
version='1.0.0',
|
||||||
|
description='High-performance C extension for computing pi via the Leibniz series',
|
||||||
|
ext_modules=[module],
|
||||||
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from setuptools import setup, Extension
|
||||||
|
import sys
|
||||||
|
|
||||||
|
extra_compile_args = []
|
||||||
|
extra_link_args = []
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
extra_compile_args = ['/O2', '/Ot', '/GL', '/fp:fast']
|
||||||
|
extra_link_args = ['/LTCG']
|
||||||
|
else:
|
||||||
|
extra_compile_args = ['-O3', '-march=native']
|
||||||
|
|
||||||
|
module = Extension(
|
||||||
|
name='python_hard',
|
||||||
|
sources=['python_hard.c'],
|
||||||
|
extra_compile_args=extra_compile_args,
|
||||||
|
extra_link_args=extra_link_args,
|
||||||
|
language='c'
|
||||||
|
)
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='python_hard',
|
||||||
|
version='1.0.0',
|
||||||
|
description='High-performance C extension reimplementation',
|
||||||
|
ext_modules=[module]
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
from setuptools import setup, Extension
|
||||||
|
|
||||||
|
module = Extension(
|
||||||
|
'zz_my_module',
|
||||||
|
sources=['zz_my_module.c'],
|
||||||
|
)
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='zz_my_module',
|
||||||
|
version='1.0',
|
||||||
|
description='This is a custom C extension module.',
|
||||||
|
ext_modules=[module]
|
||||||
|
)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Build first: python setup.py build_ext --inplace
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import calculate_pi
|
||||||
|
|
||||||
|
# Original Python implementation
|
||||||
|
def py_leibniz_pi(iterations):
|
||||||
|
result = 1.0
|
||||||
|
for i in range(1, iterations + 1):
|
||||||
|
j = i * 4 - 1
|
||||||
|
result -= (1 / j)
|
||||||
|
j = i * 4 + 1
|
||||||
|
result += (1 / j)
|
||||||
|
return result * 4
|
||||||
|
|
||||||
|
iters = 5_000_000
|
||||||
|
|
||||||
|
# Warm-up
|
||||||
|
calculate_pi.leibniz_pi(10)
|
||||||
|
py_leibniz_pi(10)
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
res_c = calculate_pi.leibniz_pi(iters)
|
||||||
|
end = time.perf_counter()
|
||||||
|
ctime = end - start
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
res_py = py_leibniz_pi(iters)
|
||||||
|
end = time.perf_counter()
|
||||||
|
pytime = end - start
|
||||||
|
|
||||||
|
print(f"Iterations: {iters}")
|
||||||
|
print(f"C extension result: {res_c}")
|
||||||
|
print(f"Python result: {res_py}")
|
||||||
|
print(f"Absolute difference: {abs(res_c - res_py)}")
|
||||||
|
print(f"C extension time: {ctime:.6f} s")
|
||||||
|
print(f"Python time: {pytime:.6f} s")
|
||||||
|
print(f"Speedup: {pytime/ctime if ctime > 0 else float('inf'):.2f}x")
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
# Original Python code
|
||||||
|
|
||||||
|
def lcg(seed, a=1664525, c=1013904223, m=2**32):
|
||||||
|
value = seed
|
||||||
|
while True:
|
||||||
|
value = (a * value + c) % m
|
||||||
|
yield value
|
||||||
|
|
||||||
|
def max_subarray_sum_py(n, seed, min_val, max_val):
|
||||||
|
lcg_gen = lcg(seed)
|
||||||
|
random_numbers = [next(lcg_gen) % (max_val - min_val + 1) + min_val for _ in range(n)]
|
||||||
|
max_sum = float('-inf')
|
||||||
|
for i in range(n):
|
||||||
|
current_sum = 0
|
||||||
|
for j in range(i, n):
|
||||||
|
current_sum += random_numbers[j]
|
||||||
|
if current_sum > max_sum:
|
||||||
|
max_sum = current_sum
|
||||||
|
return max_sum
|
||||||
|
|
||||||
|
def total_max_subarray_sum_py(n, initial_seed, min_val, max_val):
|
||||||
|
total_sum = 0
|
||||||
|
lcg_gen = lcg(initial_seed)
|
||||||
|
for _ in range(20):
|
||||||
|
seed = next(lcg_gen)
|
||||||
|
total_sum += max_subarray_sum_py(n, seed, min_val, max_val)
|
||||||
|
return total_sum
|
||||||
|
|
||||||
|
# Build and import extension (after running: python setup.py build && install or develop)
|
||||||
|
import python_hard as ext
|
||||||
|
|
||||||
|
# Example parameters
|
||||||
|
n = 600
|
||||||
|
initial_seed = 12345678901234567890
|
||||||
|
min_val = -1000
|
||||||
|
max_val = 1000
|
||||||
|
|
||||||
|
# Time Python
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
py_res1 = max_subarray_sum_py(n, (initial_seed * 1664525 + 1013904223) % (2**32), min_val, max_val)
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
py_time1 = t1 - t0
|
||||||
|
|
||||||
|
# Time C extension
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
ext_res1 = ext.max_subarray_sum(n, (initial_seed * 1664525 + 1013904223) % (2**32), min_val, max_val)
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
ext_time1 = t1 - t0
|
||||||
|
|
||||||
|
print('max_subarray_sum equality:', py_res1 == ext_res1)
|
||||||
|
print('Python time:', py_time1)
|
||||||
|
print('C ext time:', ext_time1)
|
||||||
|
|
||||||
|
# Total over 20 seeds
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
py_res2 = total_max_subarray_sum_py(n, initial_seed, min_val, max_val)
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
py_time2 = t1 - t0
|
||||||
|
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
ext_res2 = ext.total_max_subarray_sum(n, initial_seed, min_val, max_val)
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
ext_time2 = t1 - t0
|
||||||
|
|
||||||
|
print('total_max_subarray_sum equality:', py_res2 == ext_res2)
|
||||||
|
print('Python total time:', py_time2)
|
||||||
|
print('C ext total time:', ext_time2)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
import time
|
||||||
|
import zz_my_module
|
||||||
|
|
||||||
|
def python_hello_world():
|
||||||
|
print("Hello, World!")
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
python_hello_world()
|
||||||
|
end = time.time()
|
||||||
|
print(f"Python function execution time: {end - start:.6f} seconds")
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
zz_my_module.hello_world()
|
||||||
|
end = time.time()
|
||||||
|
print(f"C extension execution time: {end - start:.6f} seconds")
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
// Function to be called from Python
|
||||||
|
static PyObject* zz_hello_world(PyObject* self, PyObject* args) {
|
||||||
|
printf("Hello, World!\n");
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method definition structure
|
||||||
|
static PyMethodDef zz_my_methods[] = {
|
||||||
|
{"hello_world", zz_hello_world, METH_VARARGS, "Print 'Hello, World!'"},
|
||||||
|
{NULL, NULL, 0, NULL} // Sentinel
|
||||||
|
};
|
||||||
|
|
||||||
|
// Module definition
|
||||||
|
static struct PyModuleDef zz_my_module = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"zz_my_module",
|
||||||
|
"Extension module that prints Hello, World!",
|
||||||
|
-1,
|
||||||
|
zz_my_methods
|
||||||
|
};
|
||||||
|
|
||||||
|
// Module initialization function
|
||||||
|
PyMODINIT_FUNC PyInit_zz_my_module(void) {
|
||||||
|
return PyModule_Create(&zz_my_module);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user