Python speed vs csharp

Bengt Richter bokr at oz.net
Fri Aug 1 16:05:36 CEST 2003

```On 1 Aug 2003 03:11:35 GMT, bokr at oz.net (Bengt Richter) wrote:
[...]
>    # inlining code from old version:
>    # constant assignments hoisted out of loop, of course
>    p  =  0.3275911
>    a1 =  0.254829592
>    a2 = -0.284496736
>    a3 =  1.421413741
>    a4 = -1.453152027
>    a5 =  1.061405429
>    x = 1.2345
>    for i in xrange(100000):
>        t = 1.0 / (1.0 + p*float(x))
>        erfcx = ( (a1 + (a2 + (a3 +
>                  (a4 + a5*t)*t)*t)*t)*t ) * math.exp(-(x**2))
>    t4 = clock()
>    for i in xrange(100000): erfc_in_c(1.2345)
>    t5=clock()
>    print 'old: %f, new: %f, inline: %f, in_c %f' %(t2-t1, t3-t2, t4-t3, t5-t4)

D'oh ...

# inlining the new version code
p  =  0.3275911
a1 =  0.254829592
a2 = -0.284496736
a3 =  1.421413741
a4 = -1.453152027
a5 =  1.061405429
exp = math.exp
for i in xrange(100000):
t = 1.0 / (1.0 + p*x) #XXX# don't need float(), since p is float
erfcx = ( (a1 + (a2 + (a3 +
(a4 + a5*t)*t)*t)*t)*t ) * exp(-x**2) #XXX# elim attr lookup on global math.exp
t6=clock()
print 'old: %f, new: %f, inline: %f, in_c %f, inline new %f' %(
t2-t1, t3-t2, t4-t3, t5-t4, t6-t5)

>=======================================================================
>
>Result is:
>
>
>[19:48] C:\pywk\cstuff>erfcopt.py
>old: 4.490975, new: 3.123005, inline: 3.051674, in_c 0.806907
>
>I was surprised that the inline gain was not more, but I guess there was enough computation
>to obscure that cost. Anyway, moving some of the work to def-time paid off about 30%. But
>going to C cut 82%.
>

[ 6:48] C:\pywk\cstuff>erfcopt.py
old: 4.448979, new: 3.112778, inline: 3.073672, in_c 0.806066, inline new 2.193491

Ok, that last number is a bit more like it. Of course the C version is still the fastest.
Forgot to include the source ;-/ If you have MSVC++6 and python sources you can compile

====< mkpydll.cmd >===========================================
@cl -LD -nologo -Id:\python22\include %1.c -link -LIBPATH:D:\python22\libs -export:init%1
==============================================================
Invoked thus:

[ 7:03] C:\pywk\cstuff>mkpydll erfc
erfc.c
Creating library erfc.lib and object erfc.exp

(Tested only as far as you've seen in recent posts!)
====< erfc.c >================================================
/*
** erfc.c taken with slight mod from Mike @ nospam.com's c# posted version of
** Rational approximation for erfc(x) (Abramowitz & Stegun, Sec. 7.1.26)
** Fifth order approximation. |error| <= 1.5e-7 for all x
** Version 0.01 20030731 Bengt Richter bokr at oz.net
*/

#include <math.h>
static double
erfc( double x )
{
double p, a1, a2, a3, a4, a5;
double t, erfcx;

p  =  0.3275911;
a1 =  0.254829592;
a2 = -0.284496736;
a3 =  1.421413741;
a4 = -1.453152027;
a5 =  1.061405429;

t = 1.0 / (1.0 + p*x);
erfcx = ( (a1 + (a2 + (a3 +
(a4 + a5*t)*t)*t)*t)*t ) * exp(-pow(x,2.0));
return erfcx;
}

#include "Python.h"
#include <windows.h>

static char doc_erfc[] =
"erfc(x) -> approximation\n";

static PyObject *
erfc_erfc(PyObject *self, PyObject *args)
{
PyObject *rv;
double farg;

if (!PyArg_ParseTuple(args, "d:erfc", &farg)) /* Single floatingpoint arg */
return NULL;
rv = Py_BuildValue("d", erfc(farg));
return rv;
}

/* List of functions defined in the module */
static struct PyMethodDef erfc_module_methods[] = {
{"erfc", erfc_erfc, METH_VARARGS, doc_erfc},
{NULL, NULL}                 /* sentinel */
};

/* Initialization function for the module (*must* be called initerfc) */
static char doc_erfc_module[] =
"# Rational approximation for erfc(x) (Abramowitz & Stegun, Sec. 7.1.26)\n"
"# Fifth order approximation. |error| <= 1.5e-7 for all x\n";

DL_EXPORT(void)
initerfc(void)
{
PyObject *m, *d, *x;

/* Create the module and add the functions */
m = Py_InitModule("erfc", erfc_module_methods);
d = PyModule_GetDict(m);
x = PyString_FromString(doc_erfc_module);
PyDict_SetItemString(d, "__doc__", x);
Py_XDECREF(x);
}
==============================================================
>>> help('erfc')
Help on module erfc:

NAME
erfc

FILE
c:\pywk\cstuff\erfc.dll

DESCRIPTION
# Rational approximation for erfc(x) (Abramowitz & Stegun, Sec. 7.1.26)
# Fifth order approximation. |error| <= 1.5e-7 for all x

FUNCTIONS
erfc(...)
erfc(x) -> approximation

DATA
__file__ = 'erfc.dll'
__name__ = 'erfc'

>>> from erfc import erfc
>>> for x in range(10): print '%s => %g'%(x,erfc(x))
...
0 => 1
1 => 0.157299
2 => 0.00467786
3 => 2.21051e-005
4 => 1.54603e-008
5 => 1.54782e-012
6 => 2.17852e-017
7 => 4.26424e-023
8 => 1.15275e-029
9 => 4.28336e-037

Regards,
Bengt Richter

```