Commit 2153e8e3 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Reduce allocations in a few places

- Check for the allocation of empty tuples and just return the singleton
- Try to avoid creating the kwargs dict since it might end up being empty
- Let unicode-creation special case apply to all argument types
- Fix type(obj) to be fast again (got superceded by a different special case)
- Do fewer allocations in int()
parent d7934a4c
......@@ -1409,7 +1409,7 @@ PyAPI_FUNC(int) _PyUnicode_IsAlpha(
) PYSTON_NOEXCEPT;
// Pyston addition:
PyAPI_FUNC(PyObject*) unicode_new_inner(PyObject* x, char* encoding, char* errors) PYSTON_NOEXCEPT;
PyAPI_FUNC(PyObject*) unicode_new_inner(PyTypeObject* type, PyObject* x, char* encoding, char* errors) PYSTON_NOEXCEPT;
#ifdef __cplusplus
}
......
......@@ -8745,61 +8745,63 @@ static PyBufferProcs unicode_as_buffer = {
static PyObject *
unicode_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
PyObject* unicode_new_inner(PyObject* x, char* encoding, char* errors) {
PyObject* unicode_new_inner(PyTypeObject* type, PyObject* x, char* encoding, char* errors) {
PyObject* tmp;
if (x == NULL)
return (PyObject *)_PyUnicode_New(0);
if (encoding == NULL && errors == NULL)
return PyObject_Unicode(x);
tmp = (PyObject *)_PyUnicode_New(0);
else if (encoding == NULL && errors == NULL)
tmp = PyObject_Unicode(x);
else
return PyUnicode_FromEncodedObject(x, encoding, errors);
}
tmp = PyUnicode_FromEncodedObject(x, encoding, errors);
static PyObject *
unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *x = NULL;
static char *kwlist[] = {"string", "encoding", "errors", 0};
char *encoding = NULL;
char *errors = NULL;
if (type == &PyUnicode_Type)
return tmp;
if (type != &PyUnicode_Type)
return unicode_subtype_new(type, args, kwds);
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:unicode",
kwlist, &x, &encoding, &errors))
if (tmp == NULL)
return NULL;
return unicode_new_inner(x, encoding, errors);
}
static PyObject *
unicode_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyUnicodeObject *tmp, *pnew;
// Inlined unicode_subtype_new:
PyUnicodeObject *tmp2;
PyUnicodeObject *pnew;
Py_ssize_t n;
assert(PyType_IsSubtype(type, &PyUnicode_Type));
tmp = (PyUnicodeObject *)unicode_new(&PyUnicode_Type, args, kwds);
if (tmp == NULL)
return NULL;
assert(PyUnicode_Check(tmp));
pnew = (PyUnicodeObject *) type->tp_alloc(type, n = tmp->length);
tmp2 = (PyUnicodeObject*)tmp;
pnew = (PyUnicodeObject *) type->tp_alloc(type, n = tmp2->length);
if (pnew == NULL) {
Py_DECREF(tmp);
Py_DECREF(tmp2);
return NULL;
}
pnew->str = (Py_UNICODE*) PyObject_MALLOC(sizeof(Py_UNICODE) * (n+1));
if (pnew->str == NULL) {
_Py_ForgetReference((PyObject *)pnew);
PyObject_Del(pnew);
Py_DECREF(tmp);
Py_DECREF(tmp2);
return PyErr_NoMemory();
}
Py_UNICODE_COPY(pnew->str, tmp->str, n+1);
Py_UNICODE_COPY(pnew->str, tmp2->str, n+1);
pnew->length = n;
pnew->hash = tmp->hash;
Py_DECREF(tmp);
pnew->hash = tmp2->hash;
Py_DECREF(tmp2);
return (PyObject *)pnew;
}
static PyObject *
unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *x = NULL;
static char *kwlist[] = {"string", "encoding", "errors", 0};
char *encoding = NULL;
char *errors = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:unicode",
kwlist, &x, &encoding, &errors))
return NULL;
return unicode_new_inner(type, x, encoding, errors);
}
PyDoc_STRVAR(unicode_doc,
"unicode(object='') -> unicode object\n\
unicode(string[, encoding[, errors]]) -> unicode object\n\
......
......@@ -1986,7 +1986,11 @@ extern "C" PyObject* PyNumber_Int(PyObject* o) noexcept {
return PyInt_FromLong(io->n);
}
PyObject* trunc_func = PyObject_GetAttrString(o, "__trunc__");
// Pyston change: this should be an optimization
// PyObject* trunc_func = PyObject_GetAttrString(o, "__trunc__");
static BoxedString* trunc_str = internStringImmortal("__trunc__");
PyObject* trunc_func = getattrInternal<ExceptionStyle::CAPI>(o, trunc_str, NULL);
if (trunc_func) {
PyObject* truncated = PyEval_CallObject(trunc_func, NULL);
Py_DECREF(trunc_func);
......
......@@ -29,6 +29,7 @@ namespace pyston {
#define DISABLE_STATS 0
#define STAT_ALLOCATIONS (0 && !DISABLE_STATS)
#define STAT_ALLOCATION_TYPES (0 && !DISABLE_STATS)
#define STAT_CALLATTR_DESCR_ABORTS (0 && !DISABLE_STATS)
#define STAT_EXCEPTIONS (0 && !DISABLE_STATS)
#define STAT_EXCEPTIONS_LOCATION (0 && STAT_EXCEPTIONS)
......
......@@ -581,7 +581,7 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
return Box::operator new(size, default_cls); \
}
#if STAT_ALLOCATIONS
#if STAT_ALLOCATION_TYPES
#define ALLOC_STATS(cls) \
if (cls->tp_name) { \
std::string per_name_alloc_name = "alloc." + std::string(cls->tp_name); \
......
......@@ -112,8 +112,8 @@ extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
#endif
#if STAT_ALLOCATIONS
gc_alloc_bytes.log(bytes);
gc_alloc_bytes_typed[(int)kind_id].log(bytes);
gc_alloc_bytes.log(alloc_bytes);
gc_alloc_bytes_typed[(int)kind_id].log(alloc_bytes);
#endif
return r;
......
......@@ -221,6 +221,21 @@ extern "C" PyObject* PyInt_FromString(const char* s, char** pend, int base) noex
if (*end != '\0') {
bad:
slen = strlen(s) < 200 ? strlen(s) : 200;
// Pyston addition: try to avoid doing the string repr if possible.
bool use_fast = true;
for (int i = 0; i < slen; i++) {
char c = s[i];
if (c == '\'' || c == '\\' || c == '\t' || c == '\n' || c == '\r' || c < ' ' || c >= 0x7f) {
use_fast = false;
break;
}
}
if (use_fast) {
PyErr_Format(PyExc_ValueError, "invalid literal for int() with base %d: '%.200s'", base, s);
return NULL;
}
sobj = PyString_FromStringAndSize(s, slen);
if (sobj == NULL)
return NULL;
......
......@@ -3254,7 +3254,11 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
////
// Second, apply any keywords:
BoxedDict* okwargs = NULL;
// Speed hack: we try to not create the kwargs dictionary if it will end up being empty.
// So if we see that we need to pass something, first set it to NULL, and then store the
// pointer here so that if we need to we can instantiate the dict and store it here.
// If you need to access the dict, you should call get_okwargs()
BoxedDict** _okwargs = NULL;
if (paramspec.takes_kwargs) {
int kwargs_idx = paramspec.num_args + (paramspec.takes_varargs ? 1 : 0);
if (rewrite_args) {
......@@ -3270,17 +3274,27 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
rewrite_args->args->setAttr((kwargs_idx - 3) * sizeof(Box*), r_kwargs);
}
okwargs = new BoxedDict();
getArg(kwargs_idx, oarg1, oarg2, oarg3, oargs) = okwargs;
_okwargs = (BoxedDict**)&getArg(kwargs_idx, oarg1, oarg2, oarg3, oargs);
*_okwargs = NULL;
}
auto get_okwargs = [=]() {
if (!paramspec.takes_kwargs)
return (BoxedDict*)nullptr;
BoxedDict* okw = *_okwargs;
if (okw)
return okw;
okw = *_okwargs = new BoxedDict();
return okw;
};
if ((!param_names || !param_names->takes_param_names) && argspec.num_keywords && !paramspec.takes_kwargs) {
raiseExcHelper(TypeError, "%s() doesn't take keyword arguments", func_name);
}
if (argspec.num_keywords)
if (argspec.num_keywords) {
assert(argspec.num_keywords == keyword_names->size());
BoxedDict* okwargs = get_okwargs();
for (int i = 0; i < argspec.num_keywords; i++) {
assert(!rewrite_args && "would need to be handled here");
......@@ -3288,16 +3302,16 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
Box* kw_val = getArg(arg_idx, arg1, arg2, arg3, args);
if (!param_names || !param_names->takes_param_names) {
assert(okwargs);
assert(!rewrite_args); // would need to add it to r_kwargs
okwargs->d[(*keyword_names)[i]] = kw_val;
continue;
}
auto dest = placeKeyword(param_names, params_filled, (*keyword_names)[i], kw_val, oarg1, oarg2, oarg3, oargs,
okwargs, func_name);
auto dest = placeKeyword(param_names, params_filled, (*keyword_names)[i], kw_val, oarg1, oarg2, oarg3,
oargs, okwargs, func_name);
assert(!rewrite_args);
}
}
if (argspec.has_kwargs) {
assert(!rewrite_args && "would need to be handled here");
......@@ -3316,6 +3330,10 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
assert(PyDict_Check(kwargs));
BoxedDict* d_kwargs = static_cast<BoxedDict*>(kwargs);
BoxedDict* okwargs = NULL;
if (d_kwargs->d.size())
okwargs = get_okwargs();
for (const auto& p : *d_kwargs) {
auto k = coerceUnicodeToStr(p.first);
......
......@@ -712,7 +712,8 @@ static PyObject* cpythonTypeCall(BoxedClass* type, PyObject* args, PyObject* kwd
static Box* unicodeNewHelper(BoxedClass* type, Box* string, Box* encoding_obj, Box** _args) {
Box* errors_obj = _args[0];
assert(type == unicode_cls);
assert(isSubclass(type, unicode_cls));
assert(type->tp_new == unicode_cls->tp_new);
char* encoding = NULL;
char* errors = NULL;
......@@ -723,10 +724,44 @@ static Box* unicodeNewHelper(BoxedClass* type, Box* string, Box* encoding_obj, B
if (!PyArg_ParseSingle(errors_obj, 1, "unicode", "s", &errors))
throwCAPIException();
Box* r = unicode_new_inner(string, encoding, errors);
Box* r = unicode_new_inner(type, string, encoding, errors);
if (!r)
throwCAPIException();
assert(r->cls == unicode_cls); // otherwise we'd need to call this object's init
if (type == unicode_cls) {
if (r->cls == unicode_cls) // no init
return r;
if (!PyUnicode_Check(r)) // skip init
return r;
} else {
if (!isSubclass(r->cls, type)) // skip init
return r;
}
// Call tp_init:
if (r->cls->tp_init == object_cls->tp_init)
return r;
if (!string)
assert(!encoding_obj);
if (!encoding_obj)
assert(!errors_obj);
Box* args;
if (!string)
args = EmptyTuple;
else if (!encoding_obj)
args = BoxedTuple::create1(string);
else if (!errors_obj)
args = BoxedTuple::create2(string, encoding_obj);
else
args = BoxedTuple::create3(string, encoding_obj, errors_obj);
int init_code = r->cls->tp_init(r, args, NULL);
if (init_code == -1)
throwCAPIException();
return r;
}
......@@ -762,19 +797,14 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
if (cls == unicode_cls && !argspec.has_kwargs && !argspec.has_starargs
&& (argspec.num_args == 1 || (argspec.num_args == 2 && arg2->cls == str_cls)) && S == CXX) {
// unicode() takes an "encoding" parameter which can cause the constructor to return unicode subclasses.
// Special-case unicode for now, maybe there's something about this that can eventually be generalized:
if (cls->tp_new == unicode_cls->tp_new) {
assert(S == CXX && "implement me");
if (rewrite_args) {
rewrite_args->arg1->addGuard((intptr_t)cls);
if (argspec.num_args >= 2)
rewrite_args->arg2->addGuard((intptr_t)arg2->cls);
}
// Special-case unicode for now, maybe there's something about this that can eventually be generalized:
ParamReceiveSpec paramspec(4, 3, false, false);
bool rewrite_success = false;
static ParamNames param_names({ "string", "encoding", "errors" }, "", "");
......@@ -795,12 +825,11 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
rewrite_args->out_success = true;
}
// TODO other encodings could return non-unicode?
return unicodeNewHelper(cls, arg2, arg3, oargs);
}
if (cls->tp_new != object_cls->tp_new && cls->tp_new != slot_tp_new && cls->tp_new != BaseException->tp_new
&& S == CXX) {
&& cls->tp_new != type_cls->tp_new && S == CXX) {
// Looks like we're calling an extension class and we're not going to be able to
// separately rewrite the new + init calls. But we can rewrite the fact that we
// should just call the cpython version, which will end up working pretty well.
......
......@@ -83,6 +83,9 @@ BoxedDict* getSysModulesDict();
BoxedList* getSysPath();
extern "C" Box* getSysStdout();
extern "C" BoxedTuple* EmptyTuple;
extern "C" BoxedString* EmptyString;
extern "C" {
extern BoxedClass* object_cls, *type_cls, *bool_cls, *int_cls, *long_cls, *float_cls, *str_cls, *function_cls,
*none_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls,
......@@ -492,11 +495,15 @@ static_assert(offsetof(BoxedList, capacity) == offsetof(PyListObject, allocated)
class BoxedTuple : public BoxVar {
public:
static BoxedTuple* create(int64_t size) {
if (size == 0)
return EmptyTuple;
BoxedTuple* rtn = new (size) BoxedTuple();
memset(rtn->elts, 0, size * sizeof(Box*)); // TODO not all callers want this (but some do)
return rtn;
}
static BoxedTuple* create(int64_t nelts, Box** elts) {
if (nelts == 0)
return EmptyTuple;
BoxedTuple* rtn = new (nelts) BoxedTuple();
for (int i = 0; i < nelts; i++)
assert(gc::isValidGCObject(elts[i]));
......@@ -643,8 +650,6 @@ static_assert(sizeof(BoxedTuple) == sizeof(PyTupleObject), "");
static_assert(offsetof(BoxedTuple, ob_size) == offsetof(PyTupleObject, ob_size), "");
static_assert(offsetof(BoxedTuple, elts) == offsetof(PyTupleObject, ob_item), "");
extern "C" BoxedTuple* EmptyTuple;
extern "C" BoxedString* EmptyString;
extern BoxedString* characters[UCHAR_MAX + 1];
struct PyHasher {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment