diff --git a/Include/graminit.h b/Include/graminit.h diff --git a/Objects/longobject.c b/Objects/longobject.c index 8f7ad4c..b45f809 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -29,6 +29,128 @@ static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; int quick_int_allocs, quick_neg_int_allocs; #endif +typedef enum { + STAT_FROMLONG = 0, + STAT_BOOL, + STAT_COMPARE, + STAT_RICHCOMPARE, + STAT_ADD, + STAT_ASLONG, + STAT_SUB, + STAT_BITWISE, + STAT_HASH, + STAT_INVERT, + STAT_NEG, + STAT_FORMAT, + STAT_MUL, + STAT_MOD, + STAT_POW, + STAT_DIV, + STAT_TRUEDIV, + STAT_RSHIFT, + STAT_LSHIFT, + STAT_LONG, + STAT_FLOAT, + STAT_ABS, + STAT_DIVMOD, + STAT_FORMAT_ADV, + STAT_ROUND, + + STAT_COUNT +} stat_id_t; + +typedef struct { + int min; + int max; + int total; +} stat_digits_t; + +typedef struct { + int id; + int calls; + stat_digits_t a; + stat_digits_t b; +} stat_data_t; + +stat_data_t _PyLong_stat[STAT_COUNT]; + +void init_stat(void) +{ + unsigned int id; + stat_data_t* data; + stat_digits_t *a, *b; + for (id=0; idid = id; + data->calls = 0; + a = &data->a; + a->min = INT_MAX; + a->max = INT_MIN; + a->total = 0; + b = &data->b; + b->min = INT_MAX; + b->max = INT_MIN; + b->total = 0; + } +} + +int cmp_stat(const void *va, const void *vb) +{ + const stat_data_t* a = (const stat_data_t*)va; + const stat_data_t* b = (const stat_data_t*)vb; + if (a->calls < b->calls) + return 1; + else if (a->calls > b->calls) + return -1; + else + return 0; +} + +void dump_stat(void) +{ + unsigned int id; + stat_data_t* data; + stat_digits_t *a, *b; + double avga, avgb; + qsort(_PyLong_stat, STAT_COUNT, sizeof(_PyLong_stat[0]), cmp_stat); + for (id=0; idid, data->calls); + if (!data->calls) { + printf("\n"); + continue; + } + a = &data->a; + avga = (double)a->total / data->calls; + b = &data->b; + avgb = (double)b->total / data->calls; + printf("min=(% 2i,% 2i), avg=(%+.1f, %+.1f), max=(% 3i, % 3i)\n", a->min, b->min, avga, avgb, a->max, b->max); + } + printf("\n"); +} + +void update_digits(stat_digits_t* stat, PyLongObject* v) +{ + int n; + if (v) + n = ABS(Py_SIZE(v)); + else + n = -1; + stat->total += n; + if (n < stat->min) + stat->min = n; + if (n > stat->max) + stat->max = n; +} + +void update_stat(stat_id_t id, PyLongObject* a, PyLongObject* b) +{ + stat_data_t* data = &_PyLong_stat[id]; + data->calls += 1; + update_digits(&data->a, a); + update_digits(&data->b, b); +} + static PyObject * get_small_int(int ival) { @@ -47,7 +169,7 @@ get_small_int(int ival) return get_small_int(ival); \ } while(0) -static PyLongObject * +static PyLongObject * maybe_small_long(PyLongObject *v) { if (v && ABS(Py_SIZE(v)) <= 1) { @@ -133,7 +255,7 @@ _PyLong_New(Py_ssize_t size) This computation would be incorrect on systems which have padding before the digits; with 16-bit digits this should not happen. */ - result = PyObject_MALLOC(sizeof(PyVarObject) + + result = PyObject_MALLOC(sizeof(PyVarObject) + size*sizeof(digit)); if (!result) { PyErr_NoMemory(); @@ -171,8 +293,8 @@ _PyLong_Copy(PyLongObject *src) /* Create a new long int object from a C long int */ -PyObject * -PyLong_FromLong(long ival) +static PyObject * +_PyLong_FromLong(long ival) { PyLongObject *v; unsigned long abs_ival; @@ -233,6 +355,14 @@ PyLong_FromLong(long ival) return (PyObject *)v; } +PyObject * +PyLong_FromLong(long ival) +{ + PyObject* v = _PyLong_FromLong(ival); + update_stat(STAT_FROMLONG, (PyLongObject*)v, NULL); + return v; +} + /* Create a new long int object from a C unsigned long int */ PyObject * @@ -396,7 +526,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) else { *overflow = Py_SIZE(v) > 0 ? 1 : -1; /* res is already set to -1 */ - } + } } exit: if (do_decref) { @@ -405,13 +535,14 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) return res; } -long +long PyLong_AsLong(PyObject *obj) { int overflow; + update_stat(STAT_ASLONG, (PyLongObject*)obj, NULL); long result = PyLong_AsLongAndOverflow(obj, &overflow); if (overflow) { - /* XXX: could be cute and give a different + /* XXX: could be cute and give a different message for overflow == -1 */ PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C long"); @@ -1498,6 +1629,8 @@ _PyLong_Format(PyObject *aa, int base) assert(base >= 2 && base <= 36); size_a = ABS(Py_SIZE(a)); + update_stat(STAT_FORMAT, (PyLongObject*)aa, NULL); + /* Compute a rough upper bound for the length of the string */ i = base; bits = 0; @@ -2211,6 +2344,8 @@ long_compare(PyLongObject *a, PyLongObject *b) { Py_ssize_t sign; + update_stat(STAT_COMPARE, a, b); + if (Py_SIZE(a) != Py_SIZE(b)) { if (ABS(Py_SIZE(a)) == 0 && ABS(Py_SIZE(b)) == 0) sign = 0; @@ -2237,7 +2372,8 @@ long_richcompare(PyObject *self, PyObject *other, int op) { PyObject *result; CHECK_BINOP(self, other); - result = Py_CmpToRich(op, long_compare((PyLongObject*)self, + update_stat(STAT_RICHCOMPARE, (PyLongObject*)self, (PyLongObject*)other); + result = Py_CmpToRich(op, long_compare((PyLongObject*)self, (PyLongObject*)other)); return result; } @@ -2249,6 +2385,8 @@ long_hash(PyLongObject *v) Py_ssize_t i; int sign; + update_stat(STAT_HASH, v, NULL); + /* This is designed so that Python ints and longs with the same value hash to the same value, otherwise comparisons of mapping keys will turn out weird */ @@ -2381,6 +2519,7 @@ long_add(PyLongObject *a, PyLongObject *b) PyLongObject *z; CHECK_BINOP(a, b); + update_stat(STAT_ADD, a, b); if (ABS(Py_SIZE(a)) <= 1 && ABS(Py_SIZE(b)) <= 1) { PyObject *result = PyLong_FromLong(MEDIUM_VALUE(a) + @@ -2411,6 +2550,7 @@ long_sub(PyLongObject *a, PyLongObject *b) PyLongObject *z; CHECK_BINOP(a, b); + update_stat(STAT_SUB, a, b); if (ABS(Py_SIZE(a)) <= 1 && ABS(Py_SIZE(b)) <= 1) { PyObject* r; @@ -2840,6 +2980,7 @@ long_mul(PyLongObject *a, PyLongObject *b) PyLongObject *z; CHECK_BINOP(a, b); + update_stat(STAT_MUL, a, b); if (ABS(Py_SIZE(a)) <= 1 && ABS(Py_SIZE(b)) <= 1) { PyObject *r; @@ -2925,6 +3066,7 @@ long_div(PyObject *a, PyObject *b) PyLongObject *div; CHECK_BINOP(a, b); + update_stat(STAT_DIV, (PyLongObject*)a, (PyLongObject*)b); if (l_divmod((PyLongObject*)a, (PyLongObject*)b, &div, NULL) < 0) div = NULL; return (PyObject *)div; @@ -2937,6 +3079,8 @@ long_true_divide(PyObject *a, PyObject *b) int failed, aexp = -1, bexp = -1; CHECK_BINOP(a, b); + update_stat(STAT_TRUEDIV, (PyLongObject*)a, (PyLongObject*)b); + ad = _PyLong_AsScaledDouble((PyObject *)a, &aexp); bd = _PyLong_AsScaledDouble((PyObject *)b, &bexp); failed = (ad == -1.0 || bd == -1.0) && PyErr_Occurred(); @@ -2977,8 +3121,9 @@ static PyObject * long_mod(PyObject *a, PyObject *b) { PyLongObject *mod; - + CHECK_BINOP(a, b); + update_stat(STAT_MOD, (PyLongObject*)a, (PyLongObject*)b); if (l_divmod((PyLongObject*)a, (PyLongObject*)b, NULL, &mod) < 0) mod = NULL; @@ -2992,6 +3137,7 @@ long_divmod(PyObject *a, PyObject *b) PyObject *z; CHECK_BINOP(a, b); + update_stat(STAT_DIVMOD, (PyLongObject*)a, (PyLongObject*)b); if (l_divmod((PyLongObject*)a, (PyLongObject*)b, &div, &mod) < 0) { return NULL; @@ -3027,6 +3173,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x) /* a, b, c = v, w, x */ CHECK_BINOP(v, w); + update_stat(STAT_POW, (PyLongObject*)v, (PyLongObject*)w); a = (PyLongObject*)v; Py_INCREF(a); b = (PyLongObject*)w; Py_INCREF(b); if (PyLong_Check(x)) { @@ -3199,6 +3346,7 @@ long_invert(PyLongObject *v) /* Implement ~x as -(x+1) */ PyLongObject *x; PyLongObject *w; + update_stat(STAT_INVERT, v, NULL); if (ABS(Py_SIZE(v)) <=1) return PyLong_FromLong(-(MEDIUM_VALUE(v)+1)); w = (PyLongObject *)PyLong_FromLong(1L); @@ -3216,6 +3364,7 @@ static PyObject * long_neg(PyLongObject *v) { PyLongObject *z; + update_stat(STAT_NEG, v, NULL); if (ABS(Py_SIZE(v)) <= 1) return PyLong_FromLong(-MEDIUM_VALUE(v)); z = (PyLongObject *)_PyLong_Copy(v); @@ -3227,6 +3376,7 @@ long_neg(PyLongObject *v) static PyObject * long_abs(PyLongObject *v) { + update_stat(STAT_ABS, v, NULL); if (Py_SIZE(v) < 0) return long_neg(v); else @@ -3236,6 +3386,7 @@ long_abs(PyLongObject *v) static int long_bool(PyLongObject *v) { + update_stat(STAT_BOOL, v, NULL); return ABS(Py_SIZE(v)) != 0; } @@ -3248,6 +3399,7 @@ long_rshift(PyLongObject *a, PyLongObject *b) digit lomask, himask; CHECK_BINOP(a, b); + update_stat(STAT_RSHIFT, a, b); if (Py_SIZE(a) < 0) { /* Right shifting negative numbers is harder */ @@ -3310,6 +3462,7 @@ long_lshift(PyObject *v, PyObject *w) twodigits accum; CHECK_BINOP(a, b); + update_stat(STAT_LSHIFT, a, b); shiftby = PyLong_AsLong((PyObject *)b); if (shiftby == -1L && PyErr_Occurred()) @@ -3369,6 +3522,8 @@ long_bitwise(PyLongObject *a, digit diga, digb; PyObject *v; + update_stat(STAT_BITWISE, a, b); + if (Py_SIZE(a) < 0) { a = (PyLongObject *) long_invert(a); if (a == NULL) @@ -3492,6 +3647,7 @@ long_or(PyObject *a, PyObject *b) static PyObject * long_long(PyObject *v) { + update_stat(STAT_LONG, (PyLongObject*)v, NULL); if (PyLong_CheckExact(v)) Py_INCREF(v); else @@ -3503,6 +3659,7 @@ static PyObject * long_float(PyObject *v) { double result; + update_stat(STAT_FLOAT, (PyLongObject*)v, NULL); result = PyLong_AsDouble(v); if (result == -1.0 && PyErr_Occurred()) return NULL; @@ -3608,6 +3765,7 @@ long__format__(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "U:__format__", &format_spec)) return NULL; + update_stat(STAT_FORMAT_ADV, (PyLongObject*)self, NULL); return _PyLong_FormatAdvanced(self, PyUnicode_AS_UNICODE(format_spec), PyUnicode_GET_SIZE(format_spec)); @@ -3621,7 +3779,7 @@ long_round(PyObject *self, PyObject *args) int ndigits = UNDEF_NDIGITS; double x; PyObject *res; - + if (!PyArg_ParseTuple(args, "|i", &ndigits)) return NULL; @@ -3629,6 +3787,7 @@ long_round(PyObject *self, PyObject *args) return long_long(self); /* If called with two args, defer to float.__round__(). */ + update_stat(STAT_ROUND, (PyLongObject*)self, NULL); x = PyLong_AsDouble(self); if (x == -1.0 && PyErr_Occurred()) return NULL; @@ -3682,19 +3841,19 @@ static PyMethodDef long_methods[] = { }; static PyGetSetDef long_getset[] = { - {"real", + {"real", (getter)long_long, (setter)NULL, "the real part of a complex number", NULL}, - {"imag", + {"imag", (getter)long_getN, (setter)NULL, "the imaginary part of a complex number", (void*)0}, - {"numerator", + {"numerator", (getter)long_long, (setter)NULL, "the numerator of a rational number in lowest terms", NULL}, - {"denominator", + {"denominator", (getter)long_getN, (setter)NULL, "the denominator of a rational number in lowest terms", (void*)1}, @@ -3812,7 +3971,7 @@ _PyLong_Init(void) _Py_NewReference(op); /* _Py_NewReference sets the ref count to 1 but * the ref count might be larger. Set the refcnt - * to the original refcnt + 1 */ + * to the original refcnt + 1 */ Py_REFCNT(op) = refcnt + 1; assert(Py_SIZE(op) == size); assert(v->ob_digit[0] == abs(ival)); @@ -3824,6 +3983,7 @@ _PyLong_Init(void) v->ob_digit[0] = abs(ival); } #endif + init_stat(); return 1; } @@ -3841,4 +4001,5 @@ PyLong_Fini(void) _Py_ForgetReference((PyObject*)v); } #endif + dump_stat(); } diff --git a/Python/graminit.c b/Python/graminit.c