Skip to content

Commit

Permalink
Interrogate overhaul that dramatically increases performance for the …
Browse files Browse the repository at this point in the history
…simpler methods
  • Loading branch information
rdb committed Jul 18, 2014
1 parent 3f6b1a2 commit 7631878
Show file tree
Hide file tree
Showing 17 changed files with 907 additions and 614 deletions.
5 changes: 3 additions & 2 deletions direct/src/showbase/extend_frozen.c
@@ -1,3 +1,4 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>

#ifdef _WIN32
Expand Down Expand Up @@ -105,7 +106,7 @@ py_extend_frozen_modules(PyObject *self, PyObject *args) {
PyObject *tuple;
const char *name;
const char *code;
int size;
Py_ssize_t size;

tuple = PySequence_GetItem(list, i);
if (!PyArg_ParseTuple(tuple, "ss#", &name, &code, &size)) {
Expand Down Expand Up @@ -178,7 +179,7 @@ py_get_frozen_module_code(PyObject *self, PyObject *args) {
if (strcmp(PyImport_FrozenModules[i].name, name) == 0) {
int is_package = (PyImport_FrozenModules[i].size < 0);
return Py_BuildValue("(s#i)", PyImport_FrozenModules[i].code,
abs(PyImport_FrozenModules[i].size),
(Py_ssize_t) abs(PyImport_FrozenModules[i].size),
is_package);
}
++i;
Expand Down
4 changes: 4 additions & 0 deletions dtool/src/dtoolbase/typeHandle.cxx
Expand Up @@ -16,6 +16,10 @@
#include "typeRegistryNode.h"
#include "atomicAdjust.h"

#ifdef HAVE_PYTHON
#include "Python.h"
#endif

// This is initialized to zero by static initialization.
TypeHandle TypeHandle::_none;

Expand Down
12 changes: 7 additions & 5 deletions dtool/src/dtoolbase/typeHandle.h
Expand Up @@ -66,7 +66,10 @@
class TypedObject;

#ifdef HAVE_PYTHON
#include "Python.h"
#ifndef PyObject_HEAD
struct _object;
typedef _object PyObject;
#endif
#endif

////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -127,15 +130,15 @@ class EXPCL_DTOOL TypeHandle {

INLINE TypeHandle get_parent_towards(TypeHandle ancestor,
TypedObject *object = (TypedObject *)NULL) const;
INLINE int get_best_parent_from_Set(const std::set< int > &legal_vals) const;

INLINE int get_best_parent_from_Set(const std::set< int > &legal_vals) const;

#ifdef DO_MEMORY_USAGE
int get_memory_usage(MemoryClass memory_class) const;
void inc_memory_usage(MemoryClass memory_class, int size);
void dec_memory_usage(MemoryClass memory_class, int size);
#else
INLINE int get_memory_usage(MemoryClass) const { return 0; }
CONSTEXPR int get_memory_usage(MemoryClass) const { return 0; }
INLINE void inc_memory_usage(MemoryClass, int) { }
INLINE void dec_memory_usage(MemoryClass, int) { }
#endif // DO_MEMORY_USAGE
Expand Down Expand Up @@ -170,4 +173,3 @@ EXPCL_DTOOL ostream &operator << (ostream &out, TypeHandle::MemoryClass mem_clas
#include "typeHandle.I"

#endif

102 changes: 58 additions & 44 deletions dtool/src/interrogate/functionRemap.cxx
Expand Up @@ -303,26 +303,6 @@ make_wrapper_entry(FunctionIndex function_index) {
assert(!iwrapper._parameters.empty());
iwrapper._parameters.front()._parameter_flags |=
InterrogateFunctionWrapper::PF_is_this;

if (_parameters.size() >= 2 && _parameters[1]._name == "self" &&
TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type())) {
// Here's a special case. If the first parameter of a nonstatic
// method is a PyObject * called "self", then we will
// automatically fill it in from the this pointer, and remove it
// from the generated parameter list.
_parameters.erase(_parameters.begin() + 1);
_flags |= F_explicit_self;
}

} else if (_type == T_constructor) {
// We also allow "self" to be passed in to a constructor, even
// though the constructor doesn't normally accept a this pointer.
// But this makes sense to Python programmers.
if (_parameters.size() >= 1 && _parameters[0]._name == "self" &&
TypeManager::is_pointer_to_PyObject(_parameters[0]._remap->get_orig_type())) {
_parameters.erase(_parameters.begin() + 0);
_flags |= F_explicit_self;
}
}

if (!_void_return) {
Expand Down Expand Up @@ -650,12 +630,32 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
}

// Check for a special meaning by name and signature.
if (_type == T_normal) {
int first_param = 0;
if (_has_this) {
first_param = 1;
int first_param = 0;
if (_has_this) {
first_param = 1;
}

if (_has_this || _type == T_constructor) {
if (_parameters.size() > first_param && _parameters[first_param]._name == "self" &&
TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
// Here's a special case. If the first parameter of a nonstatic
// method is a PyObject * called "self", then we will
// automatically fill it in from the this pointer, and remove it
// from the generated parameter list.
_parameters.erase(_parameters.begin() + first_param);
_flags |= F_explicit_self;
}
}

if (_parameters.size() == first_param) {
_flags |= F_no_args;
} else if (_parameters.size() == first_param + 1) {
_flags |= F_single_arg;
} else {
_flags |= F_varargs;
}

if (_type == T_normal) {
if (fname == "operator []" || fname == "__getitem__") {
_flags |= F_getitem;
if (_has_this && _parameters.size() == 2) {
Expand All @@ -670,7 +670,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
if (_has_this && _parameters.size() > 2) {
if (TypeManager::is_integer(_parameters[1]._remap->get_new_type())) {
// Its first parameter is an int parameter, presumably an index.
_flags |= F_setitem_int;
_flags |= (F_setitem_int | F_varargs);
}
}

Expand All @@ -681,55 +681,69 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
_flags |= F_size;
}

} else if (fname == "make_copy" ) {
} else if (fname == "make_copy") {
if (_has_this && _parameters.size() == 1 &&
TypeManager::is_pointer(_return_type->get_new_type())) {
// It receives no parameters, and returns a pointer.
_flags |= F_make_copy;
}

} else if (fname == "__iter__" ) {
} else if (fname == "__iter__") {
if (_has_this && _parameters.size() == 1 &&
TypeManager::is_pointer(_return_type->get_new_type())) {
// It receives no parameters, and returns a pointer.
_flags |= F_iter;
}

} else if (fname == "__getbuffer__" ) {
if (_has_this && _parameters.size() == 4 &&
} else if (fname == "__getbuffer__") {
if (_has_this && _parameters.size() == 3 &&
TypeManager::is_integer(_return_type->get_new_type()) &&
TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type()) &&
TypeManager::is_pointer_to_Py_buffer(_parameters[2]._remap->get_orig_type()) &&
TypeManager::is_integer(_parameters[3]._remap->get_orig_type())) {
TypeManager::is_pointer_to_Py_buffer(_parameters[1]._remap->get_orig_type()) &&
TypeManager::is_integer(_parameters[2]._remap->get_orig_type())) {

_flags |= F_getbuffer;
}

} else if (fname == "__releasebuffer__" ) {
if (_has_this && _parameters.size() == 3 &&
TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type()) &&
TypeManager::is_pointer_to_Py_buffer(_parameters[2]._remap->get_orig_type())) {
} else if (fname == "__releasebuffer__") {
if (_has_this && _parameters.size() == 2 &&
TypeManager::is_pointer_to_Py_buffer(_parameters[1]._remap->get_orig_type())) {

_flags |= F_releasebuffer;
}

} else if (fname == "compare_to" ) {
} else if (fname == "compare_to") {
if (_has_this && _parameters.size() == 2 &&
TypeManager::is_integer(_return_type->get_new_type())) {
// It receives one parameter, and returns an integer.
_flags |= F_compare_to;
}

} else if (fname == "operator ()" || fname == "__call__") {
// Call operators always take keyword arguments.
_flags |= (F_varargs | F_keyword_args);

} else if (fname == "__setattr__" || fname == "__getattr__") {
// Just to prevent these from getting keyword arguments.

} else {
if (_flags & F_varargs) {
// Every other method can take keyword arguments, if they
// take more than one argument.
_flags |= F_keyword_args;
}
}

} else if (_type == T_constructor) {
if (!_has_this && _parameters.size() == 1) {
if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) ==
TypeManager::unwrap(_return_type->get_orig_type())) {
// If this is the only parameter, and it's the same as the
// "this" type, this is a copy constructor.
_flags |= F_copy_constructor;
}
if (!_has_this && _parameters.size() == 1 &&
TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) ==
TypeManager::unwrap(_return_type->get_orig_type())) {
// If this is the only parameter, and it's the same as the
// "this" type, this is a copy constructor.
_flags |= F_copy_constructor;

}
// Constructors always take varargs and keyword args.
_flags |= (F_varargs | F_keyword_args);
}

return true;
Expand Down
4 changes: 4 additions & 0 deletions dtool/src/interrogate/functionRemap.h
Expand Up @@ -91,6 +91,10 @@ class FunctionRemap {
F_getbuffer = 0x0200,
F_releasebuffer = 0x0400,
F_compare_to = 0x0800,
F_no_args = 0x1000,
F_single_arg = 0x2000,
F_varargs = 0x4000,
F_keyword_args = 0x8000,
};

typedef vector<Parameter> Parameters;
Expand Down
11 changes: 9 additions & 2 deletions dtool/src/interrogate/interfaceMaker.cxx
Expand Up @@ -663,7 +663,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) {
make_function_remap(itype, ifunc, cppfunc, num_default_parameters);
if (remap != (FunctionRemap *)NULL) {
func->_remaps.push_back(remap);

// If *any* of the variants of this function has a "this"
// pointer, the entire set of functions is deemed to have a
// "this" pointer.
Expand All @@ -672,7 +672,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) {
}

func->_flags |= remap->_flags;

// Make a wrapper for the function.
FunctionWrapperIndex wrapper_index =
remap->make_wrapper_entry(func_index);
Expand All @@ -685,6 +685,13 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) {
}
}

// If there's a remap taking no args and a remap
// taking only a single arg, assume we need varargs.
if ((func->_flags & FunctionRemap::F_no_args) &&
(func->_flags & FunctionRemap::F_single_arg)) {
func->_flags |= FunctionRemap::F_varargs;
}

return func;
}

Expand Down
9 changes: 2 additions & 7 deletions dtool/src/interrogate/interfaceMakerPython.cxx
Expand Up @@ -36,17 +36,12 @@ InterfaceMakerPython(InterrogateModuleDef *def) :
void InterfaceMakerPython::
write_includes(ostream &out) {
InterfaceMaker::write_includes(out);
out << "#undef HAVE_LONG_LONG\n"
<< "#undef _POSIX_C_SOURCE\n\n"
out << "#undef _POSIX_C_SOURCE\n\n"
<< "#if PYTHON_FRAMEWORK\n"
<< " #include \"Python/Python.h\"\n"
<< "#else\n"
<< " #include \"Python.h\"\n"
<< "#endif\n"
<< "#ifdef HAVE_LONG_LONG\n"
<< "#undef HAVE_LONG_LONG\n"
<< "#endif \n";

<< "#endif\n";
}

////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 7631878

Please sign in to comment.