diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-29 19:07:27 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-29 19:07:27 -0400 |
commit | 5eff49e08d547536a0ea87be2a64d8aed2335c61 (patch) | |
tree | 70f309c2a32bc56fa394e5e2f2f5a874fd8d68c5 | |
parent | ab5a1a7f4a87e6e74335986a571b03e2fb6c15ee (diff) | |
download | sqlalchemy-attempt_c_loading.tar.gz |
- attempt to write part of _populate_full in C. Performance differenceattempt_c_loading
is pretty much zippo! 1%. to use C code here would require a much more
fundamental rewrite of everything so that we aren't just calling python
functions.
-rw-r--r-- | lib/sqlalchemy/cextension/loader.c | 256 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/loading.py | 3 | ||||
-rw-r--r-- | setup.py | 4 |
3 files changed, 261 insertions, 2 deletions
diff --git a/lib/sqlalchemy/cextension/loader.c b/lib/sqlalchemy/cextension/loader.c new file mode 100644 index 000000000..743d61632 --- /dev/null +++ b/lib/sqlalchemy/cextension/loader.c @@ -0,0 +1,256 @@ +/* +loader.c +Copyright (C) 2014 the SQLAlchemy authors and contributors <see AUTHORS file> + +This module is part of SQLAlchemy and is released under +the MIT License: http://www.opensource.org/licenses/mit-license.php +*/ + +#include <Python.h> +#include <stdio.h> + +#define MODULE_NAME "cloader" +#define MODULE_DOC "Module containing C versions of ORM loading." + + +static PyObject * populate_full(PyObject *self, PyObject *args) { + + PyObject *context; + PyObject *load_path; + PyObject *row; + PyObject *state; + PyObject *dict_; + PyObject *isnew; + PyObject *loaded_instance; + PyObject *populate_existing; + PyObject *populators; + + int isnew_bool; + PyObject *runid; + PyObject *propagate_options; + int propagate_options_bool; + PyObject *load_options; + int load_options_bool; + + PyObject *populator_collection; + + PyObject *seq; + PyObject *seq_item; + PyObject *key; + PyObject *getter; + int len; + int i; + + PyObject *arglist; + PyObject *retvalue; + + if (!PyArg_UnpackTuple(args, "populate_full", 9, 9, + &context, &load_path, &row, + &state, &dict_, &isnew, + &loaded_instance, &populate_existing, + &populators + )) { + return NULL; + } + + + isnew_bool = PyObject_IsTrue(isnew); + if (isnew_bool == -1) { + return NULL; + } + /* if isnew: + # first time we are seeing a row with this identity. + */ + + else if (isnew_bool == 1) { + + /* state.runid = context.runid */ + + runid = PyObject_GetAttrString(context, "runid"); + if (runid == NULL) + return NULL; + + if (PyObject_SetAttrString(state, "runid", runid) == -1) { + Py_DECREF(runid); + return NULL; + } + + /* if context.propagate_options: + state.load_options = context.propagate_options */ + + propagate_options = PyObject_GetAttrString(context, "propagate_options"); + if (propagate_options == NULL) + return NULL; + propagate_options_bool = PyObject_IsTrue(propagate_options); + if (propagate_options_bool == -1) + return NULL; + else if (propagate_options_bool == 1) { + if (PyObject_SetAttrString(state, "load_options", propagate_options) == -1) { + Py_DECREF(propagate_options); + return NULL; + } + } + else { + Py_DECREF(propagate_options); + } + + /* if state.load_options: + state.load_path = load_path */ + + load_options = PyObject_GetAttrString(state, "load_options"); + if (load_options == NULL) + return NULL; + load_options_bool = PyObject_IsTrue(load_options); + if (load_options_bool == -1) + return NULL; + else if (load_options_bool == 1) { + if (PyObject_SetAttrString(state, "load_path", load_path) == -1) { + Py_DECREF(load_options); + return NULL; + } + } + Py_DECREF(load_options); + + /*for key, getter in populators["quick"]: */ + populator_collection = PyDict_GetItemString(populators, "quick"); + if (populator_collection == NULL) + return NULL; + seq = PySequence_Fast(populator_collection, "expected a sequence"); + if (seq == NULL) + return NULL; + + + len = PySequence_Fast_GET_SIZE(seq); + for (i = 0; i < len; i++) { + seq_item = PySequence_Fast_GET_ITEM(seq, i); + key = PyTuple_GET_ITEM(seq_item, 0); + getter = PyTuple_GET_ITEM(seq_item, 1); + + if (key == NULL || getter == NULL) { + Py_DECREF(seq); + return NULL; + } + + arglist = Py_BuildValue("(O)", row); + if (arglist == NULL) { + Py_DECREF(seq); + return NULL; + } + retvalue = PyObject_CallObject(getter, arglist); + Py_DECREF(arglist); + if (retvalue == NULL) { + Py_DECREF(seq); + return NULL; + } + + /* dict_[key] = getter(row) */ + PyDict_SetItem(dict_, key, retvalue); + } + Py_DECREF(seq); + + /* if populate_existing: */ + /* for key, set_callable in populators["expire"]: + dict_.pop(key, None) */ + /* if set_callable: + state.callables[key] = state */ + /* else: */ + /* for key, set_callable in populators["expire"]: */ + /* if set_callable: + state.callables[key] = state */ + /* for key, populator in populators["new"]: + populator(state, dict_, row) */ + /* for key, populator in populators["delayed"]: + populator(state, dict_, row) */ + + + } + else { + + + } + + Py_RETURN_NONE; +} + +/* +def _populate_full( + context, load_path, row, state, dict_, isnew, + loaded_instance, populate_existing, populators): + if isnew: + # first time we are seeing a row with this identity. + state.runid = context.runid + if context.propagate_options: + state.load_options = context.propagate_options + if state.load_options: + state.load_path = load_path + + for key, getter in populators["quick"]: + dict_[key] = getter(row) + if populate_existing: + for key, set_callable in populators["expire"]: + dict_.pop(key, None) + if set_callable: + state.callables[key] = state + else: + for key, set_callable in populators["expire"]: + if set_callable: + state.callables[key] = state + for key, populator in populators["new"]: + populator(state, dict_, row) + for key, populator in populators["delayed"]: + populator(state, dict_, row) + + else: + # have already seen rows with this identity. + for key, populator in populators["existing"]: + populator(state, dict_, row) +*/ + +static PyMethodDef module_methods[] = { + {"_populate_full", populate_full, METH_VARARGS, + "Run ORM population for a new row."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif + +#if PY_MAJOR_VERSION >= 3 + +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + MODULE_NAME, + MODULE_DOC, + -1, + module_methods + }; +#endif + + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit_cloader(void) +#else +PyMODINIT_FUNC +initcloader(void) +#endif +{ + PyObject *m; + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&module_def); +#else + m = Py_InitModule3(MODULE_NAME, module_methods, MODULE_DOC); +#endif + +#if PY_MAJOR_VERSION >= 3 + if (m == NULL) + return NULL; + return m; +#else + if (m == NULL) + return; +#endif +} + diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index 28843a528..bc51b021b 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -416,8 +416,9 @@ def instance_processor(mapper, context, result, path, adapter, return instance return _instance +from sqlalchemy.cloader import _populate_full -def _populate_full( +def _dont_populate_full( context, load_path, row, state, dict_, isnew, loaded_instance, populate_existing, populators): if isnew: @@ -43,7 +43,9 @@ ext_modules = [ Extension('sqlalchemy.cresultproxy', sources=['lib/sqlalchemy/cextension/resultproxy.c']), Extension('sqlalchemy.cutils', - sources=['lib/sqlalchemy/cextension/utils.c']) + sources=['lib/sqlalchemy/cextension/utils.c']), + Extension('sqlalchemy.cloader', + sources=['lib/sqlalchemy/cextension/loader.c']) ] ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError) |