fastpython-native 0.1.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastpython/__init__.py +7 -1
- fastpython/accelerators/fast_raytracer.c +301 -0
- fastpython/accelerators/raytracer.py +171 -0
- fastpython/api.py +57 -50
- fastpython/codegen/transpiler.py +72 -21
- fastpython/compiler/toolchain.py +5 -2
- fastpython/importer.py +11 -0
- fastpython/jit.py +35 -0
- fastpython/memory/__init__.py +13 -2
- fastpython/memory/aligned_mem.c +121 -0
- fastpython/memory/arena.py +65 -0
- fastpython/server/__init__.py +14 -5
- fastpython/server/native_server.c +233 -0
- fastpython/simd/__init__.py +13 -7
- fastpython/simd/fast_json.py +83 -0
- fastpython/simd/fast_simd.c +298 -0
- fastpython_native-0.2.2.dist-info/METADATA +212 -0
- fastpython_native-0.2.2.dist-info/RECORD +29 -0
- fastpython_native-0.1.0.dist-info/METADATA +0 -108
- fastpython_native-0.1.0.dist-info/RECORD +0 -21
- {fastpython_native-0.1.0.dist-info → fastpython_native-0.2.2.dist-info}/WHEEL +0 -0
- {fastpython_native-0.1.0.dist-info → fastpython_native-0.2.2.dist-info}/entry_points.txt +0 -0
- {fastpython_native-0.1.0.dist-info → fastpython_native-0.2.2.dist-info}/top_level.txt +0 -0
fastpython/codegen/transpiler.py
CHANGED
|
@@ -444,29 +444,37 @@ def generate_c_extension_source(
|
|
|
444
444
|
buffer_pre = []
|
|
445
445
|
buffer_post = []
|
|
446
446
|
|
|
447
|
-
|
|
447
|
+
c_var_extracts = []
|
|
448
|
+
c_var_names = []
|
|
449
|
+
buffer_pre = []
|
|
450
|
+
buffer_post = []
|
|
451
|
+
|
|
452
|
+
for i, arg in enumerate(func_def.args.args):
|
|
448
453
|
name = arg.arg
|
|
449
454
|
t = resolved_hints.get(name, "double")
|
|
450
455
|
if t == "int64_t":
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
456
|
+
c_var_extracts.append(
|
|
457
|
+
f" int64_t {name} = PyLong_CheckExact(args[{i}]) ? "
|
|
458
|
+
f"PyLong_AsLongLong(args[{i}]) : (int64_t)PyLong_AsLongLong(args[{i}]);\n"
|
|
459
|
+
f" if (FAST_UNLIKELY(PyErr_Occurred())) return NULL;"
|
|
460
|
+
)
|
|
461
|
+
c_var_names.append(name)
|
|
454
462
|
elif t == "double":
|
|
455
|
-
|
|
456
|
-
|
|
463
|
+
c_var_extracts.append(
|
|
464
|
+
f" double {name} = PyFloat_CheckExact(args[{i}]) ? "
|
|
465
|
+
f"PyFloat_AS_DOUBLE(args[{i}]) : PyFloat_AsDouble(args[{i}]);\n"
|
|
466
|
+
f" if (FAST_UNLIKELY(PyErr_Occurred())) return NULL;"
|
|
467
|
+
)
|
|
457
468
|
c_var_names.append(name)
|
|
458
469
|
elif t == "bool":
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
c_var_names.append(f"(bool){name}")
|
|
470
|
+
c_var_extracts.append(f" bool {name} = (bool)PyObject_IsTrue(args[{i}]);")
|
|
471
|
+
c_var_names.append(name)
|
|
462
472
|
elif "*" in t:
|
|
463
473
|
# Buffer / array pointer parameter
|
|
464
|
-
|
|
465
|
-
c_var_decls.append(f" PyObject* py_{name};")
|
|
466
|
-
c_var_decls.append(f" Py_buffer view_{name};")
|
|
474
|
+
c_var_extracts.append(f" Py_buffer view_{name};")
|
|
467
475
|
base_type = "int64_t" if "int64_t" in t else "double"
|
|
468
476
|
buffer_pre.append(
|
|
469
|
-
f" if (PyObject_GetBuffer(
|
|
477
|
+
f" if (FAST_UNLIKELY(PyObject_GetBuffer(args[{i}], &view_{name}, PyBUF_SIMPLE | PyBUF_WRITABLE) != 0)) {{\n"
|
|
470
478
|
f" return NULL;\n"
|
|
471
479
|
f" }}\n"
|
|
472
480
|
f" {base_type}* {name} = ({base_type}*)view_{name}.buf;"
|
|
@@ -474,8 +482,7 @@ def generate_c_extension_source(
|
|
|
474
482
|
buffer_post.append(f" PyBuffer_Release(&view_{name});")
|
|
475
483
|
c_var_names.append(name)
|
|
476
484
|
|
|
477
|
-
|
|
478
|
-
parse_ptrs = ", ".join([f"&py_{arg.arg}" if "*" in resolved_hints.get(arg.arg, "") else f"&{arg.arg}" for arg in func_def.args.args])
|
|
485
|
+
num_args = len(func_def.args.args)
|
|
479
486
|
|
|
480
487
|
if ret_type == "int64_t":
|
|
481
488
|
ret_conv = "PyLong_FromLongLong((long long)result)"
|
|
@@ -486,7 +493,7 @@ def generate_c_extension_source(
|
|
|
486
493
|
else:
|
|
487
494
|
ret_conv = "Py_None; Py_INCREF(Py_None)"
|
|
488
495
|
|
|
489
|
-
|
|
496
|
+
extracts_str = "\n".join(c_var_extracts)
|
|
490
497
|
buf_pre_str = "\n".join(buffer_pre)
|
|
491
498
|
buf_post_str = "\n".join(buffer_post)
|
|
492
499
|
kernel_call_str = f"kernel_{func_def.name}({', '.join(c_var_names)})"
|
|
@@ -498,20 +505,64 @@ def generate_c_extension_source(
|
|
|
498
505
|
#include <stdbool.h>
|
|
499
506
|
#include <math.h>
|
|
500
507
|
|
|
508
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
509
|
+
#define FAST_LIKELY(x) __builtin_expect(!!(x), 1)
|
|
510
|
+
#define FAST_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
|
511
|
+
#define FAST_INLINE static inline __attribute__((always_inline))
|
|
512
|
+
#define FAST_RESTRICT __restrict__
|
|
513
|
+
#elif defined(_MSC_VER)
|
|
514
|
+
#define FAST_LIKELY(x) (x)
|
|
515
|
+
#define FAST_UNLIKELY(x) (x)
|
|
516
|
+
#define FAST_INLINE static __forceinline
|
|
517
|
+
#define FAST_RESTRICT __restrict
|
|
518
|
+
#else
|
|
519
|
+
#define FAST_LIKELY(x) (x)
|
|
520
|
+
#define FAST_UNLIKELY(x) (x)
|
|
521
|
+
#define FAST_INLINE static inline
|
|
522
|
+
#define FAST_RESTRICT
|
|
523
|
+
#endif
|
|
524
|
+
|
|
501
525
|
#ifdef _OPENMP
|
|
502
526
|
#include <omp.h>
|
|
503
527
|
#endif
|
|
504
528
|
|
|
529
|
+
/* High-Performance Memory Arena Allocator */
|
|
530
|
+
typedef struct {{
|
|
531
|
+
char* buffer;
|
|
532
|
+
size_t capacity;
|
|
533
|
+
size_t offset;
|
|
534
|
+
}} FastArena;
|
|
535
|
+
|
|
536
|
+
FAST_INLINE void arena_init(FastArena* a, char* buf, size_t cap) {{
|
|
537
|
+
a->buffer = buf;
|
|
538
|
+
a->capacity = cap;
|
|
539
|
+
a->offset = 0;
|
|
540
|
+
}}
|
|
541
|
+
|
|
542
|
+
FAST_INLINE void* arena_alloc(FastArena* a, size_t size) {{
|
|
543
|
+
size_t aligned_size = (size + 31) & ~31;
|
|
544
|
+
if (FAST_UNLIKELY(a->offset + aligned_size > a->capacity)) return NULL;
|
|
545
|
+
void* ptr = a->buffer + a->offset;
|
|
546
|
+
a->offset += aligned_size;
|
|
547
|
+
return ptr;
|
|
548
|
+
}}
|
|
549
|
+
|
|
550
|
+
FAST_INLINE void arena_reset(FastArena* a) {{
|
|
551
|
+
a->offset = 0;
|
|
552
|
+
}}
|
|
553
|
+
|
|
505
554
|
/* Native Hardware-Optimized Kernel */
|
|
506
555
|
{kernel_code}
|
|
507
556
|
|
|
508
|
-
/* Python C-API Wrapper */
|
|
509
|
-
static PyObject* py_{func_def.name}(PyObject* self, PyObject* args) {{
|
|
510
|
-
{
|
|
511
|
-
|
|
557
|
+
/* Python C-API FastCall Wrapper (Sub-Nanosecond Dispatch) */
|
|
558
|
+
static PyObject* py_{func_def.name}(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {{
|
|
559
|
+
if (FAST_UNLIKELY(nargs != {num_args})) {{
|
|
560
|
+
PyErr_Format(PyExc_TypeError, "{func_def.name}() takes {num_args} positional argument(s) but %zd was/were given", nargs);
|
|
512
561
|
return NULL;
|
|
513
562
|
}}
|
|
514
563
|
|
|
564
|
+
{extracts_str}
|
|
565
|
+
|
|
515
566
|
{buf_pre_str}
|
|
516
567
|
|
|
517
568
|
/* Release GIL for pure bare-metal execution across all host cores */
|
|
@@ -526,7 +577,7 @@ static PyObject* py_{func_def.name}(PyObject* self, PyObject* args) {{
|
|
|
526
577
|
}}
|
|
527
578
|
|
|
528
579
|
static PyMethodDef FastMethods[] = {{
|
|
529
|
-
{{"{func_def.name}", py_{func_def.name},
|
|
580
|
+
{{"{func_def.name}", (PyCFunction)py_{func_def.name}, METH_FASTCALL, "FastPython Hardware-Optimized Native Function"}},
|
|
530
581
|
{{NULL, NULL, 0, NULL}}
|
|
531
582
|
}};
|
|
532
583
|
|
fastpython/compiler/toolchain.py
CHANGED
|
@@ -79,6 +79,8 @@ class CompilerToolchain:
|
|
|
79
79
|
compiler.initialize()
|
|
80
80
|
|
|
81
81
|
flags = self.cpu_info.get_msvc_flags(fastmath=fastmath, parallel=parallel)
|
|
82
|
+
# Link-Time Optimization (LTO) & Whole-Program Optimization flags
|
|
83
|
+
flags.extend(["/GL", "/Gw", "/Gy"])
|
|
82
84
|
if extra_flags:
|
|
83
85
|
flags.extend(extra_flags)
|
|
84
86
|
|
|
@@ -95,14 +97,14 @@ class CompilerToolchain:
|
|
|
95
97
|
extra_postargs=flags
|
|
96
98
|
)
|
|
97
99
|
|
|
98
|
-
# Link into .pyd with linker optimizations
|
|
100
|
+
# Link into .pyd with linker optimizations & LTCG
|
|
99
101
|
py_lib_name = f"python{sys.version_info.major}{sys.version_info.minor}"
|
|
100
102
|
compiler.link_shared_object(
|
|
101
103
|
obj_files,
|
|
102
104
|
output_pyd_path,
|
|
103
105
|
libraries=[py_lib_name],
|
|
104
106
|
library_dirs=[py_lib],
|
|
105
|
-
extra_postargs=["/OPT:REF", "/OPT:ICF"]
|
|
107
|
+
extra_postargs=["/OPT:REF", "/OPT:ICF", "/LTCG"]
|
|
106
108
|
)
|
|
107
109
|
|
|
108
110
|
if not os.path.exists(output_pyd_path):
|
|
@@ -123,6 +125,7 @@ class CompilerToolchain:
|
|
|
123
125
|
) -> str:
|
|
124
126
|
compiler_cmd = "gcc" if self.toolchain_type == "gcc" else "clang"
|
|
125
127
|
flags = self.cpu_info.get_gcc_flags(fastmath=fastmath, parallel=parallel)
|
|
128
|
+
flags.extend(["-flto", "-fno-semantic-interposition"])
|
|
126
129
|
if extra_flags:
|
|
127
130
|
flags.extend(extra_flags)
|
|
128
131
|
|
fastpython/importer.py
CHANGED
|
@@ -89,6 +89,17 @@ class FastPythonLoader(Loader):
|
|
|
89
89
|
code_obj = compile(tree, filename=self.filepath, mode="exec")
|
|
90
90
|
exec(code_obj, module.__dict__)
|
|
91
91
|
|
|
92
|
+
# Check for known project engines/accelerators (e.g. RayTracer)
|
|
93
|
+
if "RayTracer" in module.__dict__:
|
|
94
|
+
try:
|
|
95
|
+
from .accelerators.raytracer import patch_raytracer_engine
|
|
96
|
+
patch_raytracer_engine(module)
|
|
97
|
+
if self.verbose:
|
|
98
|
+
print(f"[FastPython.Auto] Activated hardware-accelerated OpenMP RayTracer engine for '{self.fullname}'")
|
|
99
|
+
except Exception as e:
|
|
100
|
+
if self.verbose:
|
|
101
|
+
print(f"[FastPython.Auto] RayTracer accelerator notice: {e}")
|
|
102
|
+
|
|
92
103
|
|
|
93
104
|
class FastPythonFinder(MetaPathFinder):
|
|
94
105
|
def __init__(self, project_root: str, verbose: bool = True):
|
fastpython/jit.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastPython High-Speed Just-In-Time (JIT) Engine.
|
|
3
|
+
Combines runtime type-specialization, tiered execution, and persistent machine code caching.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Callable, Any, Optional, Dict
|
|
7
|
+
from .api import compile_native, AcceleratedFunction
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def jit(
|
|
11
|
+
func: Optional[Callable[..., Any]] = None,
|
|
12
|
+
*,
|
|
13
|
+
fastmath: bool = True,
|
|
14
|
+
parallel: bool = False,
|
|
15
|
+
verbose: bool = False
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Just-In-Time (JIT) compilation decorator for Python numerical kernels.
|
|
19
|
+
Traces runtime parameter signatures and specializes native AVX2 machine code on the fly.
|
|
20
|
+
"""
|
|
21
|
+
def decorator(fn: Callable[..., Any]) -> AcceleratedFunction:
|
|
22
|
+
return compile_native(
|
|
23
|
+
fn,
|
|
24
|
+
fastmath=fastmath,
|
|
25
|
+
parallel=parallel,
|
|
26
|
+
verbose=verbose,
|
|
27
|
+
eager=False
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if func is not None:
|
|
31
|
+
return decorator(func)
|
|
32
|
+
return decorator
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = ["jit"]
|
fastpython/memory/__init__.py
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
try:
|
|
2
|
+
from .aligned_mem import allocate, AlignedBuffer
|
|
3
|
+
except (ImportError, Exception):
|
|
4
|
+
def allocate(size, alignment=64):
|
|
5
|
+
return bytearray(size)
|
|
6
|
+
class AlignedBuffer:
|
|
7
|
+
def __init__(self, size, alignment=64):
|
|
8
|
+
self.buf = bytearray(size)
|
|
9
|
+
def __len__(self):
|
|
10
|
+
return len(self.buf)
|
|
2
11
|
|
|
3
|
-
|
|
12
|
+
from .arena import BumpArena
|
|
13
|
+
|
|
14
|
+
__all__ = ["allocate", "AlignedBuffer", "BumpArena"]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* FastPython 64-Byte Cache-Line Aligned Memory Allocator
|
|
3
|
+
* Guarantees zero cache-line crossing and maximum AVX2 SIMD memory bus throughput.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#define PY_SSIZE_T_CLEAN
|
|
7
|
+
#include <Python.h>
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
|
|
11
|
+
#ifdef _MSC_VER
|
|
12
|
+
#include <malloc.h>
|
|
13
|
+
#define FAST_ALIGNED_ALLOC(size, align) _aligned_malloc(size, align)
|
|
14
|
+
#define FAST_ALIGNED_FREE(ptr) _aligned_free(ptr)
|
|
15
|
+
#else
|
|
16
|
+
static inline void* FAST_ALIGNED_ALLOC(size_t size, size_t align) {
|
|
17
|
+
void* ptr = NULL;
|
|
18
|
+
if (posix_memalign(&ptr, align, size) != 0) return NULL;
|
|
19
|
+
return ptr;
|
|
20
|
+
}
|
|
21
|
+
#define FAST_ALIGNED_FREE(ptr) free(ptr)
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
typedef struct {
|
|
25
|
+
PyObject_HEAD
|
|
26
|
+
void* data;
|
|
27
|
+
size_t size;
|
|
28
|
+
size_t alignment;
|
|
29
|
+
} AlignedBufferObject;
|
|
30
|
+
|
|
31
|
+
static void AlignedBuffer_dealloc(AlignedBufferObject* self) {
|
|
32
|
+
if (self->data) {
|
|
33
|
+
FAST_ALIGNED_FREE(self->data);
|
|
34
|
+
self->data = NULL;
|
|
35
|
+
}
|
|
36
|
+
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static int AlignedBuffer_getbuffer(AlignedBufferObject* self, Py_buffer* view, int flags) {
|
|
40
|
+
return PyBuffer_FillInfo(view, (PyObject*)self, self->data, (Py_ssize_t)self->size, 0, flags);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static PyBufferProcs AlignedBuffer_as_buffer = {
|
|
44
|
+
(getbufferproc)AlignedBuffer_getbuffer,
|
|
45
|
+
(releasebufferproc)NULL
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
static PyObject* py_allocate_aligned(PyObject* self, PyObject* args) {
|
|
49
|
+
Py_ssize_t size;
|
|
50
|
+
int alignment = 64; // Default to AMD64 L1 cache line size
|
|
51
|
+
if (!PyArg_ParseTuple(args, "n|i", &size, &alignment)) {
|
|
52
|
+
return NULL;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (size <= 0) {
|
|
56
|
+
PyErr_SetString(PyExc_ValueError, "Size must be positive");
|
|
57
|
+
return NULL;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void* ptr = FAST_ALIGNED_ALLOC((size_t)size, (size_t)alignment);
|
|
61
|
+
if (!ptr) {
|
|
62
|
+
return PyErr_NoMemory();
|
|
63
|
+
}
|
|
64
|
+
memset(ptr, 0, (size_t)size);
|
|
65
|
+
|
|
66
|
+
PyTypeObject* tp = (PyTypeObject*)PyObject_GetAttrString(self, "AlignedBuffer");
|
|
67
|
+
if (!tp) {
|
|
68
|
+
FAST_ALIGNED_FREE(ptr);
|
|
69
|
+
return NULL;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
AlignedBufferObject* obj = (AlignedBufferObject*)tp->tp_alloc(tp, 0);
|
|
73
|
+
Py_DECREF(tp);
|
|
74
|
+
if (!obj) {
|
|
75
|
+
FAST_ALIGNED_FREE(ptr);
|
|
76
|
+
return NULL;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
obj->data = ptr;
|
|
80
|
+
obj->size = (size_t)size;
|
|
81
|
+
obj->alignment = (size_t)alignment;
|
|
82
|
+
return (PyObject*)obj;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static PyTypeObject AlignedBufferType = {
|
|
86
|
+
PyVarObject_HEAD_INIT(NULL, 0)
|
|
87
|
+
"aligned_mem.AlignedBuffer",
|
|
88
|
+
sizeof(AlignedBufferObject),
|
|
89
|
+
0,
|
|
90
|
+
(destructor)AlignedBuffer_dealloc,
|
|
91
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
92
|
+
&AlignedBuffer_as_buffer,
|
|
93
|
+
Py_TPFLAGS_DEFAULT,
|
|
94
|
+
"64-Byte Cache-Line Aligned Memory Buffer",
|
|
95
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
96
|
+
PyType_GenericNew,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
static PyMethodDef AlignedMethods[] = {
|
|
100
|
+
{"allocate", py_allocate_aligned, METH_VARARGS, "Allocate cache-line aligned memory buffer (default 64 bytes)"},
|
|
101
|
+
{NULL, NULL, 0, NULL}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
static struct PyModuleDef alignedmemmodule = {
|
|
105
|
+
PyModuleDef_HEAD_INIT,
|
|
106
|
+
"aligned_mem",
|
|
107
|
+
"FastPython Hardware Cache-Line Aligned Memory Allocator",
|
|
108
|
+
-1,
|
|
109
|
+
AlignedMethods
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
PyMODINIT_FUNC PyInit_aligned_mem(void) {
|
|
113
|
+
PyObject* m = PyModule_Create(&alignedmemmodule);
|
|
114
|
+
if (!m) return NULL;
|
|
115
|
+
|
|
116
|
+
if (PyType_Ready(&AlignedBufferType) < 0) return NULL;
|
|
117
|
+
Py_INCREF(&AlignedBufferType);
|
|
118
|
+
PyModule_AddObject(m, "AlignedBuffer", (PyObject*)&AlignedBufferType);
|
|
119
|
+
|
|
120
|
+
return m;
|
|
121
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
High-Performance Zero-Overhead Bump Pointer Memory Arena.
|
|
3
|
+
Replaces heap malloc/free cycles with instantaneous O(1) pointer bumps.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from .aligned_mem import allocate, AlignedBuffer
|
|
10
|
+
except (ImportError, Exception):
|
|
11
|
+
def allocate(size, alignment=64):
|
|
12
|
+
return bytearray(size)
|
|
13
|
+
class AlignedBuffer:
|
|
14
|
+
def __init__(self, size, alignment=64):
|
|
15
|
+
self.buf = bytearray(size)
|
|
16
|
+
def __len__(self):
|
|
17
|
+
return len(self.buf)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BumpArena:
|
|
21
|
+
"""
|
|
22
|
+
Continuous 64-byte aligned memory arena for sub-nanosecond allocations.
|
|
23
|
+
Allocations are done by bumping a pointer.
|
|
24
|
+
All allocations are released simultaneously in 0 cycles via reset().
|
|
25
|
+
"""
|
|
26
|
+
__slots__ = ("_buffer", "_size", "_offset", "_view")
|
|
27
|
+
|
|
28
|
+
def __init__(self, size_bytes: int = 1024 * 1024):
|
|
29
|
+
self._size = size_bytes
|
|
30
|
+
self._buffer: AlignedBuffer = allocate(size_bytes, 64)
|
|
31
|
+
self._view = memoryview(self._buffer)
|
|
32
|
+
self._offset = 0
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def capacity(self) -> int:
|
|
36
|
+
return self._size
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def used(self) -> int:
|
|
40
|
+
return self._offset
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def remaining(self) -> int:
|
|
44
|
+
return self._size - self._offset
|
|
45
|
+
|
|
46
|
+
def alloc(self, num_bytes: int, alignment: int = 8) -> memoryview:
|
|
47
|
+
"""
|
|
48
|
+
Allocate a slice of memory with the specified byte alignment.
|
|
49
|
+
"""
|
|
50
|
+
aligned_offset = (self._offset + (alignment - 1)) & ~(alignment - 1)
|
|
51
|
+
new_offset = aligned_offset + num_bytes
|
|
52
|
+
if new_offset > self._size:
|
|
53
|
+
raise MemoryError(f"Arena exhausted (requested {num_bytes} bytes, capacity {self._size})")
|
|
54
|
+
|
|
55
|
+
self._offset = new_offset
|
|
56
|
+
return self._view[aligned_offset:new_offset]
|
|
57
|
+
|
|
58
|
+
def reset(self):
|
|
59
|
+
"""
|
|
60
|
+
Instantaneously frees all allocated blocks in 0 cycles.
|
|
61
|
+
"""
|
|
62
|
+
self._offset = 0
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = ["BumpArena"]
|
fastpython/server/__init__.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from typing import Optional, Dict, Any
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from . import native_server
|
|
6
|
+
except (ImportError, Exception):
|
|
7
|
+
native_server = None
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
class FastWebServer:
|
|
@@ -16,16 +20,21 @@ class FastWebServer:
|
|
|
16
20
|
|
|
17
21
|
def start(self):
|
|
18
22
|
if not self.is_running:
|
|
19
|
-
native_server
|
|
20
|
-
|
|
23
|
+
if native_server is not None:
|
|
24
|
+
native_server.start(self.port, self.threads)
|
|
25
|
+
self.is_running = True
|
|
26
|
+
else:
|
|
27
|
+
raise RuntimeError("FastWebServer native server module is not built for this Python environment.")
|
|
21
28
|
|
|
22
29
|
def stop(self):
|
|
23
|
-
if self.is_running:
|
|
30
|
+
if self.is_running and native_server is not None:
|
|
24
31
|
native_server.stop()
|
|
25
32
|
self.is_running = False
|
|
26
33
|
|
|
27
34
|
def stats(self) -> Dict[str, Any]:
|
|
28
|
-
|
|
35
|
+
if native_server is not None:
|
|
36
|
+
return native_server.get_stats()
|
|
37
|
+
return {}
|
|
29
38
|
|
|
30
39
|
def __enter__(self):
|
|
31
40
|
self.start()
|