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( ...@@ -1409,7 +1409,7 @@ PyAPI_FUNC(int) _PyUnicode_IsAlpha(
) PYSTON_NOEXCEPT; ) PYSTON_NOEXCEPT;
// Pyston addition: // 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 #ifdef __cplusplus
} }
......
...@@ -8745,61 +8745,63 @@ static PyBufferProcs unicode_as_buffer = { ...@@ -8745,61 +8745,63 @@ static PyBufferProcs unicode_as_buffer = {
static PyObject * static PyObject *
unicode_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); 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) if (x == NULL)
return (PyObject *)_PyUnicode_New(0); tmp = (PyObject *)_PyUnicode_New(0);
if (encoding == NULL && errors == NULL) else if (encoding == NULL && errors == NULL)
return PyObject_Unicode(x); tmp = PyObject_Unicode(x);
else else
return PyUnicode_FromEncodedObject(x, encoding, errors); tmp = PyUnicode_FromEncodedObject(x, encoding, errors);
}
static PyObject * if (type == &PyUnicode_Type)
unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return tmp;
{
PyObject *x = NULL;
static char *kwlist[] = {"string", "encoding", "errors", 0};
char *encoding = NULL;
char *errors = NULL;
if (type != &PyUnicode_Type) if (tmp == NULL)
return unicode_subtype_new(type, args, kwds);
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:unicode",
kwlist, &x, &encoding, &errors))
return NULL; return NULL;
return unicode_new_inner(x, encoding, errors);
}
static PyObject * // Inlined unicode_subtype_new:
unicode_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyUnicodeObject *tmp2;
{ PyUnicodeObject *pnew;
PyUnicodeObject *tmp, *pnew;
Py_ssize_t n; 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)); 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) { if (pnew == NULL) {
Py_DECREF(tmp); Py_DECREF(tmp2);
return NULL; return NULL;
} }
pnew->str = (Py_UNICODE*) PyObject_MALLOC(sizeof(Py_UNICODE) * (n+1)); pnew->str = (Py_UNICODE*) PyObject_MALLOC(sizeof(Py_UNICODE) * (n+1));
if (pnew->str == NULL) { if (pnew->str == NULL) {
_Py_ForgetReference((PyObject *)pnew); _Py_ForgetReference((PyObject *)pnew);
PyObject_Del(pnew); PyObject_Del(pnew);
Py_DECREF(tmp); Py_DECREF(tmp2);
return PyErr_NoMemory(); 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->length = n;
pnew->hash = tmp->hash; pnew->hash = tmp2->hash;
Py_DECREF(tmp); Py_DECREF(tmp2);
return (PyObject *)pnew; 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, PyDoc_STRVAR(unicode_doc,
"unicode(object='') -> unicode object\n\ "unicode(object='') -> unicode object\n\
unicode(string[, encoding[, errors]]) -> unicode object\n\ unicode(string[, encoding[, errors]]) -> unicode object\n\
......
...@@ -1986,7 +1986,11 @@ extern "C" PyObject* PyNumber_Int(PyObject* o) noexcept { ...@@ -1986,7 +1986,11 @@ extern "C" PyObject* PyNumber_Int(PyObject* o) noexcept {
return PyInt_FromLong(io->n); 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) { if (trunc_func) {
PyObject* truncated = PyEval_CallObject(trunc_func, NULL); PyObject* truncated = PyEval_CallObject(trunc_func, NULL);
Py_DECREF(trunc_func); Py_DECREF(trunc_func);
......
...@@ -29,6 +29,7 @@ namespace pyston { ...@@ -29,6 +29,7 @@ namespace pyston {
#define DISABLE_STATS 0 #define DISABLE_STATS 0
#define STAT_ALLOCATIONS (0 && !DISABLE_STATS) #define STAT_ALLOCATIONS (0 && !DISABLE_STATS)
#define STAT_ALLOCATION_TYPES (0 && !DISABLE_STATS)
#define STAT_CALLATTR_DESCR_ABORTS (0 && !DISABLE_STATS) #define STAT_CALLATTR_DESCR_ABORTS (0 && !DISABLE_STATS)
#define STAT_EXCEPTIONS (0 && !DISABLE_STATS) #define STAT_EXCEPTIONS (0 && !DISABLE_STATS)
#define STAT_EXCEPTIONS_LOCATION (0 && STAT_EXCEPTIONS) #define STAT_EXCEPTIONS_LOCATION (0 && STAT_EXCEPTIONS)
......
...@@ -581,7 +581,7 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -581,7 +581,7 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
return Box::operator new(size, default_cls); \ return Box::operator new(size, default_cls); \
} }
#if STAT_ALLOCATIONS #if STAT_ALLOCATION_TYPES
#define ALLOC_STATS(cls) \ #define ALLOC_STATS(cls) \
if (cls->tp_name) { \ if (cls->tp_name) { \
std::string per_name_alloc_name = "alloc." + std::string(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) { ...@@ -112,8 +112,8 @@ extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
#endif #endif
#if STAT_ALLOCATIONS #if STAT_ALLOCATIONS
gc_alloc_bytes.log(bytes); gc_alloc_bytes.log(alloc_bytes);
gc_alloc_bytes_typed[(int)kind_id].log(bytes); gc_alloc_bytes_typed[(int)kind_id].log(alloc_bytes);
#endif #endif
return r; return r;
......
...@@ -221,6 +221,21 @@ extern "C" PyObject* PyInt_FromString(const char* s, char** pend, int base) noex ...@@ -221,6 +221,21 @@ extern "C" PyObject* PyInt_FromString(const char* s, char** pend, int base) noex
if (*end != '\0') { if (*end != '\0') {
bad: bad:
slen = strlen(s) < 200 ? strlen(s) : 200; 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); sobj = PyString_FromStringAndSize(s, slen);
if (sobj == NULL) if (sobj == NULL)
return NULL; return NULL;
......
...@@ -3254,7 +3254,11 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name ...@@ -3254,7 +3254,11 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
//// ////
// Second, apply any keywords: // 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) { if (paramspec.takes_kwargs) {
int kwargs_idx = paramspec.num_args + (paramspec.takes_varargs ? 1 : 0); int kwargs_idx = paramspec.num_args + (paramspec.takes_varargs ? 1 : 0);
if (rewrite_args) { if (rewrite_args) {
...@@ -3270,33 +3274,43 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name ...@@ -3270,33 +3274,43 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
rewrite_args->args->setAttr((kwargs_idx - 3) * sizeof(Box*), r_kwargs); rewrite_args->args->setAttr((kwargs_idx - 3) * sizeof(Box*), r_kwargs);
} }
okwargs = new BoxedDict(); _okwargs = (BoxedDict**)&getArg(kwargs_idx, oarg1, oarg2, oarg3, oargs);
getArg(kwargs_idx, oarg1, oarg2, oarg3, oargs) = okwargs; *_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) { if ((!param_names || !param_names->takes_param_names) && argspec.num_keywords && !paramspec.takes_kwargs) {
raiseExcHelper(TypeError, "%s() doesn't take keyword arguments", func_name); 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()); assert(argspec.num_keywords == keyword_names->size());
for (int i = 0; i < argspec.num_keywords; i++) { BoxedDict* okwargs = get_okwargs();
assert(!rewrite_args && "would need to be handled here"); for (int i = 0; i < argspec.num_keywords; i++) {
assert(!rewrite_args && "would need to be handled here");
int arg_idx = i + argspec.num_args; int arg_idx = i + argspec.num_args;
Box* kw_val = getArg(arg_idx, arg1, arg2, arg3, args); Box* kw_val = getArg(arg_idx, arg1, arg2, arg3, args);
if (!param_names || !param_names->takes_param_names) { if (!param_names || !param_names->takes_param_names) {
assert(okwargs); assert(!rewrite_args); // would need to add it to r_kwargs
assert(!rewrite_args); // would need to add it to r_kwargs okwargs->d[(*keyword_names)[i]] = kw_val;
okwargs->d[(*keyword_names)[i]] = kw_val; continue;
continue; }
}
auto dest = placeKeyword(param_names, params_filled, (*keyword_names)[i], kw_val, oarg1, oarg2, oarg3, oargs, auto dest = placeKeyword(param_names, params_filled, (*keyword_names)[i], kw_val, oarg1, oarg2, oarg3,
okwargs, func_name); oargs, okwargs, func_name);
assert(!rewrite_args); assert(!rewrite_args);
}
} }
if (argspec.has_kwargs) { if (argspec.has_kwargs) {
...@@ -3316,6 +3330,10 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name ...@@ -3316,6 +3330,10 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
assert(PyDict_Check(kwargs)); assert(PyDict_Check(kwargs));
BoxedDict* d_kwargs = static_cast<BoxedDict*>(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) { for (const auto& p : *d_kwargs) {
auto k = coerceUnicodeToStr(p.first); auto k = coerceUnicodeToStr(p.first);
......
...@@ -712,7 +712,8 @@ static PyObject* cpythonTypeCall(BoxedClass* type, PyObject* args, PyObject* kwd ...@@ -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) { static Box* unicodeNewHelper(BoxedClass* type, Box* string, Box* encoding_obj, Box** _args) {
Box* errors_obj = _args[0]; 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* encoding = NULL;
char* errors = NULL; char* errors = NULL;
...@@ -723,10 +724,44 @@ static Box* unicodeNewHelper(BoxedClass* type, Box* string, Box* encoding_obj, B ...@@ -723,10 +724,44 @@ static Box* unicodeNewHelper(BoxedClass* type, Box* string, Box* encoding_obj, B
if (!PyArg_ParseSingle(errors_obj, 1, "unicode", "s", &errors)) if (!PyArg_ParseSingle(errors_obj, 1, "unicode", "s", &errors))
throwCAPIException(); throwCAPIException();
Box* r = unicode_new_inner(string, encoding, errors); Box* r = unicode_new_inner(type, string, encoding, errors);
if (!r) if (!r)
throwCAPIException(); 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; return r;
} }
...@@ -762,19 +797,14 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo ...@@ -762,19 +797,14 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
BoxedClass* cls = static_cast<BoxedClass*>(_cls); BoxedClass* cls = static_cast<BoxedClass*>(_cls);
if (cls == unicode_cls && !argspec.has_kwargs && !argspec.has_starargs // Special-case unicode for now, maybe there's something about this that can eventually be generalized:
&& (argspec.num_args == 1 || (argspec.num_args == 2 && arg2->cls == str_cls)) && S == CXX) { if (cls->tp_new == unicode_cls->tp_new) {
// unicode() takes an "encoding" parameter which can cause the constructor to return unicode subclasses.
assert(S == CXX && "implement me"); assert(S == CXX && "implement me");
if (rewrite_args) { if (rewrite_args) {
rewrite_args->arg1->addGuard((intptr_t)cls); 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); ParamReceiveSpec paramspec(4, 3, false, false);
bool rewrite_success = false; bool rewrite_success = false;
static ParamNames param_names({ "string", "encoding", "errors" }, "", ""); static ParamNames param_names({ "string", "encoding", "errors" }, "", "");
...@@ -795,12 +825,11 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo ...@@ -795,12 +825,11 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
rewrite_args->out_success = true; rewrite_args->out_success = true;
} }
// TODO other encodings could return non-unicode?
return unicodeNewHelper(cls, arg2, arg3, oargs); 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 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 // 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 // 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. // should just call the cpython version, which will end up working pretty well.
......
...@@ -83,6 +83,9 @@ BoxedDict* getSysModulesDict(); ...@@ -83,6 +83,9 @@ BoxedDict* getSysModulesDict();
BoxedList* getSysPath(); BoxedList* getSysPath();
extern "C" Box* getSysStdout(); extern "C" Box* getSysStdout();
extern "C" BoxedTuple* EmptyTuple;
extern "C" BoxedString* EmptyString;
extern "C" { extern "C" {
extern BoxedClass* object_cls, *type_cls, *bool_cls, *int_cls, *long_cls, *float_cls, *str_cls, *function_cls, 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, *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) ...@@ -492,11 +495,15 @@ static_assert(offsetof(BoxedList, capacity) == offsetof(PyListObject, allocated)
class BoxedTuple : public BoxVar { class BoxedTuple : public BoxVar {
public: public:
static BoxedTuple* create(int64_t size) { static BoxedTuple* create(int64_t size) {
if (size == 0)
return EmptyTuple;
BoxedTuple* rtn = new (size) BoxedTuple(); BoxedTuple* rtn = new (size) BoxedTuple();
memset(rtn->elts, 0, size * sizeof(Box*)); // TODO not all callers want this (but some do) memset(rtn->elts, 0, size * sizeof(Box*)); // TODO not all callers want this (but some do)
return rtn; return rtn;
} }
static BoxedTuple* create(int64_t nelts, Box** elts) { static BoxedTuple* create(int64_t nelts, Box** elts) {
if (nelts == 0)
return EmptyTuple;
BoxedTuple* rtn = new (nelts) BoxedTuple(); BoxedTuple* rtn = new (nelts) BoxedTuple();
for (int i = 0; i < nelts; i++) for (int i = 0; i < nelts; i++)
assert(gc::isValidGCObject(elts[i])); assert(gc::isValidGCObject(elts[i]));
...@@ -643,8 +650,6 @@ static_assert(sizeof(BoxedTuple) == sizeof(PyTupleObject), ""); ...@@ -643,8 +650,6 @@ static_assert(sizeof(BoxedTuple) == sizeof(PyTupleObject), "");
static_assert(offsetof(BoxedTuple, ob_size) == offsetof(PyTupleObject, ob_size), ""); static_assert(offsetof(BoxedTuple, ob_size) == offsetof(PyTupleObject, ob_size), "");
static_assert(offsetof(BoxedTuple, elts) == offsetof(PyTupleObject, ob_item), ""); static_assert(offsetof(BoxedTuple, elts) == offsetof(PyTupleObject, ob_item), "");
extern "C" BoxedTuple* EmptyTuple;
extern "C" BoxedString* EmptyString;
extern BoxedString* characters[UCHAR_MAX + 1]; extern BoxedString* characters[UCHAR_MAX + 1];
struct PyHasher { 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