by Gabriel von Winckler
Sum the absolute value of the elements on an array (float)
import random
n_elements = 1000000
values = [random.random() for _ in xrange(n_elements)]
def abs_sum_python(values):
total = 0;
for i in values:
total += abs(i)
return total
# abs_sum_python(values)
%timeit -n 100 abs_sum_python(values)
def abs_sum_c_in_python(values):
total = 0;
for i in range(len(values)):
total += abs(values[i])
return total
# abs_sum_c_in_python(values)
%timeit -n 100 abs_sum_c_in_python(values)
def abs_sum_very_python(values):
return sum(map(abs,values))
# abs_sum_very_python(values)
%timeit -n 100 abs_sum_very_python(values)
import numpy as np
def abs_sum_numpy(values):
return np.sum(np.absolute(values))
values_np = np.random.rand(n_elements)
# abs_sum_numpy(values_np)
%timeit -n 100 abs_sum_numpy(values_np)
Many options: * Python C API * Interface generators (f2py, swig, ...) * ctypes (https://docs.python.org/2/library/ctypes.html)
ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.
#include <stdlib.h>
/*
gcc -c -Wall -fPIC myabs.c
gcc -shared -o myabs.so myabs.o
*/
int myabs(int x) {
return abs(x);
}
!gcc -c -Wall -fPIC myabs.c
!gcc -shared -o myabs.so myabs.o
import ctypes
c_lib = ctypes.cdll.LoadLibrary('./myabs.so')
c_lib.myabs(-11)
ctypes type | C type | Python type |
---|---|---|
c_bool | Bool | bool (1) cchar | char |
c_wchar | wchar_t | 1-character unicode string |
c_byte | char | int/long |
c_ubyte | unsigned char | int/long |
c_short | short | int/long |
c_ushort | unsigned short | int/long |
c_int | int | int/long |
c_uint | unsigned int | int/long |
c_long | long | int/long |
c_ulong | unsigned long | int/long |
c_longlong | int64 or long long | int/long c_ulonglong | unsigned int64 or unsigned long long | int/long |
c_float | float | float |
c_double | double | float |
c_longdouble | long double | float |
c_char_p | char * (NUL terminated) | string or None |
c_wchar_p | wchar_t * (NUL terminated) | unicode or None |
c_void_p | void * | int/long or None |
#include <stdio.h>
#include <math.h>
/*
gcc -c -Wall -fPIC sum_abs.c
gcc -shared -o sum_abs.so sum_abs.o
*/
void sum_abs(float *in, int *num, float *out) {
int i;
float sum = 0;
for (i=0; i < *num; ++i) {
sum += fabs(in[i]);
}
*out = sum;
return;
}
!gcc -O3 -c -Wall -fPIC sum_abs.c
!gcc -shared -o sum_abs.so sum_abs.o
from ctypes import cdll, c_float, c_int, byref
c_lib = ctypes.cdll.LoadLibrary('./sum_abs.so')
# define a C array of floats
NFloatType = ctypes.c_float * n_elements
values_c = NFloatType()
# initialize
for i in xrange(len(values)):
values_c[i] = random.random()
# other params
n = ctypes.c_int(len(values))
s = ctypes.c_float()
# call
# c_lib.sum_abs(ctypes.byref(values_c),
# ctypes.byref(n),
# ctypes.byref(s))
%timeit -n 100 c_lib.sum_abs(ctypes.byref(values_c), ctypes.byref(n), ctypes.byref(s))
subroutine sum_abs(in, num, out)
integer, intent(in) :: num
real (kind=4), intent(in) :: in(num)
real (kind=4), intent(out) :: out
real (kind=4) :: sum
sum = 0
do i=1,num
sum = sum + ABS(in(i))
end do
out = sum
end subroutine sum_abs
!gfortran -shared -fPIC -g -o sum_abs_f.so sum_abs.f90
f_lib = ctypes.cdll.LoadLibrary('./sum_abs_f.so')
# f_lib.sum_abs_(ctypes.byref(values), ctypes.byref(n), ctypes.byref(s))
%timeit -n 100 f_lib.sum_abs_(ctypes.byref(values_c), ctypes.byref(n), ctypes.byref(s))
http://docs.scipy.org/doc/numpy-dev/f2py/
! f2py -c -m sum_abs_f2py sum_abs.f90
import sum_abs_f2py
print sum_abs_f2py.sum_abs.__doc__
#sum_abs_f2py.sum_abs(values, len(values))
%timeit -n 100 sum_abs_f2py.sum_abs(values)
This is because it uses python list (linked lists).
Let's try again with ctypes array:
import sum_abs_f2py
#sum_abs_f2py.sum_abs(values, len(values_c))
%timeit -n 100 sum_abs_f2py.sum_abs(values_c)
from ctypes import Structure
class POINT(Structure):
_fields_ = [("x", c_int),
("y", c_int)]
point = POINT(10, 20)
print point.x, point.y
point = POINT(y=5)
print point.x, point.y
class RECT(Structure):
_fields_ = [("upperleft", POINT),
("lowerright", POINT)]
rc = RECT(point)
print rc.upperleft.x, rc.upperleft.y
print rc.lowerright.x, rc.lowerright.y
r = RECT(POINT(1, 2), POINT(3, 4))
r = RECT((1, 2), (3, 4))
from ctypes import cdll, create_string_buffer, byref, c_char_p, c_double, c_char, c_int, c_float
libc = cdll.LoadLibrary('libc.so.6')
i = c_int()
f = c_float()
s = create_string_buffer('\000' * 32)
print i.value, f.value, repr(s.value)
libc.sscanf("1 3.14 Hello", "%d %f %s",
byref(i), byref(f), s)
print i.value, f.value, repr(s.value)
printf = libc.printf
printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
# works
printf("String '%s', Int %d, Double %f\n", "Hi", 10, 2.2)
# The output is in ipython console
# String 'Hi', Int 10, Double 2.200000
# raises an error
printf("String '%s', Int %d, Double %f\n", "Hi", 10, "a")
# By default functions are assumed to return the C int type.
# Other return types can be specified by setting the restype attribute of the function object.
strchr = libc.strchr
# fix argtype
strchr.argtypes = [c_char_p, c_char]
print strchr("abcdef", "d")
# c_char_p is a pointer to a string
strchr.restype = c_char_p
print strchr("abcdef", "d")
All material on this presentation is covered by CC BY-SA 4.0 and GPLv3 licenses.