STINNER Victor <[EMAIL PROTECTED]> added the comment:

I wrote a patch to compute stat about PyLong function calls.

make (use setup.py):

PyLong_FromLong: 168572 calls, min=( 0,  ), avg=(1.4,    ), max=(  3,    )
long_bool:        48682 calls, min=( 0,  ), avg=(0.2,    ), max=(  2,    )
long_add:         39527 calls, min=( 0, 0), avg=(0.9, 1.0), max=(  2,   3)
long_compare:     39145 calls, min=( 0, 0), avg=(1.2, 1.1), max=(  3,   3)
PyLong_AsLong:    33689 calls, min=( 0,  ), avg=(0.9,    ), max=( 45,    )
long_sub:         13091 calls, min=( 0, 0), avg=(0.9, 0.8), max=(  1,   1)
long_bitwise:      4636 calls, min=( 0, 0), avg=(0.8, 0.6), max=(  2,   2)
long_hash:         1097 calls, min=( 0,  ), avg=(0.9,    ), max=(  3,    )
long_mul:           221 calls, min=( 0, 0), avg=(0.8, 1.1), max=(  2,   2)
long_invert:        204 calls, min=( 0,  ), avg=(1.0,    ), max=(  1,    )
long_neg:            35 calls, min=( 1,  ), avg=(1.0,    ), max=(  1,    )
long_format:          3 calls, min=( 0,  ), avg=(0.7,    ), max=(  1,    )
long_mod:             3 calls, min=( 1, 1), avg=(1.0, 1.0), max=(  1,   1)
long_pow:             1 calls, min=( 1, 1), avg=(1.0, 1.0), max=(  1,   1)

pystone:

PyLong_FromLong:1587652 calls, min=( 0,  ), avg=(1.0,    ), max=(  3,    )
long_add:        902487 calls, min=( 0, 0), avg=(1.0, 1.0), max=(  2,   2)
long_compare:    651165 calls, min=( 0, 0), avg=(1.0, 1.0), max=(  3,   3)
PyLong_AsLong:   252476 calls, min=( 0,  ), avg=(1.0,    ), max=(  2,    )
long_sub:        250032 calls, min=( 1, 0), avg=(1.0, 1.0), max=(  1,   1)
long_bool:       102655 calls, min=( 0,  ), avg=(0.5,    ), max=(  1,    )
long_mul:        100015 calls, min=( 0, 0), avg=(1.0, 1.0), max=(  1,   2)
long_div:         50000 calls, min=( 1, 1), avg=(1.0, 1.0), max=(  1,   1)
long_hash:          382 calls, min=( 0,  ), avg=(1.1,    ), max=(  2,    )
long_bitwise:       117 calls, min=( 0, 0), avg=(1.0, 1.0), max=(  1,   2)
long_format:          1 calls, min=( 2,  ), avg=(2.0,    ), max=(  2,    )

min/avg/max are the integer digit count (minimum, average, maximum).

What can we learn from this numbers?

PyLong_FromLong(), long_add() and long_compare() are the 3 most common 
operations on integers. 

Except PyLong_FromLong(), long_compare() and long_format(), arguments of the 
functions are mostly in range [-2^15; 2^15].

Biggest number is a number of 45 digits: maybe just one call to long_add(). 
Except this number/call, the biggest numbers have between 2 and 3 digits. 

long_bool() is never called with number bigger than 2 digits.

long_sub() is never called with number bigger than 1 digit!

Added file: http://bugs.python.org/file11952/long_stat.patch

_______________________________________
Python tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue4258>
_______________________________________
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; id<STAT_COUNT; id++) {
+               data = &_PyLong_stat[id];
+               data->id = 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; id<STAT_COUNT; id++) {
+               data = &_PyLong_stat[id];
+               printf("[%-2i] %-7u calls", data->id, 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
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to