Skip to content

Commit

Permalink
Merge pull request #18 from ZeroIntensity/benchmark
Browse files Browse the repository at this point in the history
Benchmark
  • Loading branch information
ZeroIntensity authored Jun 24, 2024
2 parents 37efbff + ab6cd76 commit e942937
Show file tree
Hide file tree
Showing 12 changed files with 387 additions and 350 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ build/

# Misc
test.py
a.py
vgcore*
Empty file added a.py
Empty file.
33 changes: 25 additions & 8 deletions include/pyawaitable/awaitableobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

typedef int (*awaitcallback)(PyObject *, PyObject *);
typedef int (*awaitcallback_err)(PyObject *, PyObject *);
#define CALLBACK_ARRAY_SIZE 128
#define VALUE_ARRAY_SIZE 32

typedef struct _pyawaitable_callback
{
Expand All @@ -18,17 +20,27 @@ typedef struct _pyawaitable_callback
struct _PyAwaitableObject
{
PyObject_HEAD
pyawaitable_callback **aw_callbacks;
Py_ssize_t aw_callback_size;
PyObject *aw_result;
PyObject *aw_gen;
PyObject **aw_values;
Py_ssize_t aw_values_size;
void **aw_arb_values;
Py_ssize_t aw_arb_values_size;

// Callbacks
pyawaitable_callback *aw_callbacks[CALLBACK_ARRAY_SIZE];
Py_ssize_t aw_callback_index;

// Stored Values
PyObject *aw_values[VALUE_ARRAY_SIZE];
Py_ssize_t aw_values_index;

// Arbitrary Values
void *aw_arb_values[VALUE_ARRAY_SIZE];
Py_ssize_t aw_arb_values_index;

// Awaitable State
Py_ssize_t aw_state;
bool aw_done;
bool aw_awaited;

// Misc
PyObject *aw_result;
PyObject *aw_gen;
};

typedef struct _PyAwaitableObject PyAwaitableObject;
Expand Down Expand Up @@ -61,4 +73,9 @@ pyawaitable_await_function_impl(
...
);

int
alloc_awaitable_pool(void);
void
dealloc_awaitable_pool(void);

#endif
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
setup(
name="pyawaitable",
license="MIT",
version = "1.0.0-rc1",
version = "1.0.0-rc2",
ext_modules=[
Extension(
"_pyawaitable",
glob("./src/_pyawaitable/*.c"),
include_dirs=["./include/", "./src/pyawaitable/"],
extra_compile_args=["-g3"]
extra_compile_args=["-g", "-O3"]
)
],
package_dir={"": "src"},
Expand Down
143 changes: 82 additions & 61 deletions src/_pyawaitable/awaitable.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
#include <pyawaitable/genwrapper.h>
#include <pyawaitable/coro.h>
#include <stdlib.h>
#define AWAITABLE_POOL_SIZE 256

PyDoc_STRVAR(
awaitable_doc,
"Awaitable transport utility for the C API."
);

static Py_ssize_t pool_index = 0;
static PyObject *pool[AWAITABLE_POOL_SIZE];

static PyObject *
awaitable_new_func(PyTypeObject *tp, PyObject *args, PyObject *kwds)
{
Expand All @@ -22,18 +26,11 @@ awaitable_new_func(PyTypeObject *tp, PyObject *args, PyObject *kwds)
return NULL;
}

PyAwaitableObject *aw = (PyAwaitableObject *)self;
aw->aw_callbacks = NULL;
aw->aw_callback_size = 0;
aw->aw_result = Py_NewRef(Py_None);
aw->aw_gen = NULL;
aw->aw_values = NULL;
aw->aw_values_size = 0;
aw->aw_state = 0;
aw->aw_done = false;
PyAwaitableObject *aw = (PyAwaitableObject *) self;
aw->aw_awaited = false;
aw->aw_done = false;

return (PyObject *)aw;
return (PyObject *) aw;
}

PyObject *
Expand All @@ -44,7 +41,10 @@ awaitable_next(PyObject *self)

if (aw->aw_done)
{
PyErr_SetString(PyExc_RuntimeError, "cannot reuse awaitable");
PyErr_SetString(
PyExc_RuntimeError,
"pyawaitable: cannot reuse awaitable"
);
return NULL;
}

Expand All @@ -55,36 +55,35 @@ awaitable_next(PyObject *self)
return NULL;
}

aw->aw_gen = Py_NewRef(gen);
aw->aw_done = true;
return gen;
aw->aw_gen = gen;
return Py_NewRef(gen);
}

static void
awaitable_dealloc(PyObject *self)
{
PyAwaitableObject *aw = (PyAwaitableObject *)self;
if (aw->aw_values)
for (int i = 0; i < VALUE_ARRAY_SIZE; ++i)
{
for (int i = 0; i < aw->aw_values_size; i++)
Py_DECREF(aw->aw_values[i]);
PyMem_Free(aw->aw_values);
if (!aw->aw_values[i])
break;
Py_DECREF(aw->aw_values[i]);
}

Py_XDECREF(aw->aw_gen);
Py_XDECREF(aw->aw_result);

for (int i = 0; i < aw->aw_callback_size; i++)
for (int i = 0; i < CALLBACK_ARRAY_SIZE; ++i)
{
pyawaitable_callback *cb = aw->aw_callbacks[i];
if (cb == NULL)
break;

if (!cb->done)
Py_DECREF(cb->coro);
PyMem_Free(cb);
}

if (aw->aw_arb_values)
PyMem_Free(aw->aw_arb_values);

if (!aw->aw_done)
{
if (
Expand All @@ -108,17 +107,20 @@ pyawaitable_cancel_impl(PyObject *aw)
assert(aw != NULL);
Py_INCREF(aw);

PyAwaitableObject *a = (PyAwaitableObject *)aw;
PyAwaitableObject *a = (PyAwaitableObject *) aw;

for (int i = 0; i < a->aw_callback_size; i++)
for (int i = 0; i < CALLBACK_ARRAY_SIZE; ++i)
{
pyawaitable_callback *cb = a->aw_callbacks[i];
if (!cb)
break;

if (!cb->done)
Py_DECREF(cb->coro);

a->aw_callbacks[i] = NULL;
}

PyMem_Free(a->aw_callbacks);
a->aw_callback_size = 0;
Py_DECREF(aw);
}

Expand All @@ -134,47 +136,30 @@ pyawaitable_await_impl(
assert(coro != NULL);
Py_INCREF(coro);
Py_INCREF(aw);
PyAwaitableObject *a = (PyAwaitableObject *)aw;

pyawaitable_callback *aw_c = PyMem_Malloc(sizeof(pyawaitable_callback));
if (aw_c == NULL)
PyAwaitableObject *a = (PyAwaitableObject *) aw;
if (a->aw_callback_index == CALLBACK_ARRAY_SIZE)
{
Py_DECREF(aw);
Py_DECREF(coro);
PyErr_NoMemory();
return -1;
}

++a->aw_callback_size;
if (a->aw_callbacks == NULL)
{
a->aw_callbacks = PyMem_Calloc(
a->aw_callback_size,
sizeof(pyawaitable_callback *)
);
} else
{
a->aw_callbacks = PyMem_Realloc(
a->aw_callbacks,
sizeof(pyawaitable_callback *) *
a->aw_callback_size
PyErr_SetString(
PyExc_SystemError,
"pyawaitable: awaitable object cannot store more than 128 coroutines"
);
return -1;
}

if (a->aw_callbacks == NULL)
pyawaitable_callback *aw_c = PyMem_Malloc(sizeof(pyawaitable_callback));
if (aw_c == NULL)
{
--a->aw_callback_size;
Py_DECREF(aw);
Py_DECREF(coro);
PyMem_Free(aw_c);
PyErr_NoMemory();
return -1;
}

aw_c->coro = coro; // steal our own reference
aw_c->coro = coro; // Steal our own reference
aw_c->callback = cb;
aw_c->err_callback = err;
a->aw_callbacks[a->aw_callback_size - 1] = aw_c;
aw_c->done = false;
a->aw_callbacks[a->aw_callback_index++] = aw_c;
Py_DECREF(aw);

return 0;
Expand All @@ -185,21 +170,57 @@ pyawaitable_set_result_impl(PyObject *awaitable, PyObject *result)
{
assert(awaitable != NULL);
assert(result != NULL);
Py_INCREF(result);
Py_INCREF(awaitable);

PyAwaitableObject *aw = (PyAwaitableObject *)awaitable;
PyAwaitableObject *aw = (PyAwaitableObject *) awaitable;
aw->aw_result = Py_NewRef(result);
Py_DECREF(awaitable);
Py_DECREF(result);
return 0;
}

PyObject *
pyawaitable_new_impl(void)
{
PyObject *aw = awaitable_new_func(&_PyAwaitableType, NULL, NULL);
return aw;
if (pool_index == AWAITABLE_POOL_SIZE)
{
PyObject *aw = awaitable_new_func(&_PyAwaitableType, NULL, NULL);
return aw;
}

return pool[pool_index++];
}

void
dealloc_awaitable_pool(void)
{
for (Py_ssize_t i = pool_index; i < AWAITABLE_POOL_SIZE; ++i)
{
if (Py_REFCNT(pool[i]) != 1)
{
PyErr_Format(
PyExc_SystemError,
"expected %R to have a reference count of 1",
pool[i]
);
PyErr_WriteUnraisable(NULL);
}
Py_DECREF(pool[i]);
}
}

int
alloc_awaitable_pool(void)
{
for (Py_ssize_t i = 0; i < AWAITABLE_POOL_SIZE; ++i)
{
pool[i] = awaitable_new_func(&_PyAwaitableType, NULL, NULL);
if (!pool[i])
{
for (Py_ssize_t x = 0; x < i; ++x)
Py_DECREF(pool[x]);
return -1;
}
}

return 0;
}

int
Expand Down
2 changes: 1 addition & 1 deletion src/_pyawaitable/backport.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ PyErr_GetRaisedException(void)
void
PyErr_SetRaisedException(PyObject *err)
{
PyErr_Restore(err, NULL, NULL);
PyErr_Restore(Py_NewRef((PyObject *) Py_TYPE(err)), err, NULL);
}

#endif
Expand Down
Loading

0 comments on commit e942937

Please sign in to comment.