10.7.1 : The wrapper function

The header file sgemmWrapper.h is very simple :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/***************************************
	Auteur : Pierre Aubert
	Mail : aubertp7@gmail.com
	Licence : CeCILL-C
****************************************/

#ifndef __SGEMM_WRAPPER_H__
#define __SGEMM_WRAPPER_H__

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

PyObject * sgemmWrapper(PyObject *self, PyObject *args);

#endif
You can download it here.

Now, let's write the sgemmWrapper.cpp file :

Again in this example, since we are using numpy in the module, we have to define the NO_IMPORT_ARRAY to avoid multiple definitions of the same numpy function. And we also have to specify the version of the numpy API to avoid warnings :
1
2
3
4
5
6
#define NO_IMPORT_ARRAY
#ifndef DISABLE_COOL_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL core_ARRAY_API
#endif

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
Then we include appropriate files. Do not forget to include the asterics_alloc.h file to use our allocation function :
1
2
3
4
5
6
#include <iostream>
#include <numpy/arrayobject.h>
#include <bytearrayobject.h>

#include "sgemm_intrinsics_pitch.h"
#include "sgemmWrapper.h"
To parse static parameters, we have to use the function PyArg_ParseTuple, this function works the same as the scanf function from the C standard library.

Here, we parse the output table and the two input tables with 'O' which corresponds to a PyObject type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
///Get the pitch of a matrix
/**	@param nbCol : number of columns of the matrix
 * 	@return pitch of the matrix
*/
long unsigned int getPitch(long unsigned int nbCol){
	long unsigned int vecSize(VECTOR_ALIGNEMENT/sizeof(float));
	long unsigned int pitch(vecSize - (nbCol % vecSize));
	if(pitch == vecSize){pitch = 0lu;}
	return pitch;
}

///Do the hadamard computation
/**	@param self : parent of the function if it exist
 * 	@param args : arguments passed to the function
 * 	@return result of the Hadamard product
*/
PyObject * sgemmWrapper(PyObject *self, PyObject *args){
	PyArrayObject *objMatX = NULL, *objMatY = NULL, *objMatRes = NULL;
	
	if(!PyArg_ParseTuple(args, "OOO", &objMatRes, &objMatX, &objMatY)){
		PyErr_SetString(PyExc_RuntimeError, "sgemmWrapper : wrong set of arguments. Expect matRes, matX, matY\n");
		return NULL;
	}
	if(PyArray_NDIM(objMatX) != 2 || PyArray_NDIM(objMatY) != 2 || PyArray_NDIM(objMatRes) != 2){
		PyErr_SetString(PyExc_TypeError, "sgemmWrapper : input matrices must be a two dimension array");
		return NULL;
	}
	if(PyArray_DIMS(objMatX)[0] != PyArray_DIMS(objMatY)[0] || PyArray_DIMS(objMatX)[0] != PyArray_DIMS(objMatRes)[0] ||
		PyArray_DIMS(objMatX)[1] != PyArray_DIMS(objMatY)[1] || PyArray_DIMS(objMatX)[1] != PyArray_DIMS(objMatRes)[1] ||
		PyArray_DIMS(objMatX)[0] != PyArray_DIMS(objMatX)[1] ||
		PyArray_DIMS(objMatY)[0] != PyArray_DIMS(objMatY)[1] ||
		PyArray_DIMS(objMatRes)[0] != PyArray_DIMS(objMatRes)[1])
	{
		PyErr_SetString(PyExc_TypeError, "sgemmWrapper : input matrices must be of the same size and square");
		return NULL;
	}
	long unsigned int sizeElement(PyArray_DIMS(objMatX)[0]);
	
	const float * matX = (const float*)PyArray_DATA(objMatX);
	const float * matY = (const float*)PyArray_DATA(objMatY);
	float * matRes = (float*)PyArray_DATA(objMatRes);
	
	sgemm(matRes, matX, matY, sizeElement, getPitch(sizeElement));
	
	Py_RETURN_NONE;
}
The full sgemmWrapper.cpp file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/***************************************
	Auteur : Pierre Aubert
	Mail : aubertp7@gmail.com
	Licence : CeCILL-C
****************************************/

#define NO_IMPORT_ARRAY
#ifndef DISABLE_COOL_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL core_ARRAY_API
#endif

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <iostream>
#include <numpy/arrayobject.h>
#include <bytearrayobject.h>

#include "sgemm_intrinsics_pitch.h"
#include "sgemmWrapper.h"

///Get the pitch of a matrix
/**	@param nbCol : number of columns of the matrix
 * 	@return pitch of the matrix
*/
long unsigned int getPitch(long unsigned int nbCol){
	long unsigned int vecSize(VECTOR_ALIGNEMENT/sizeof(float));
	long unsigned int pitch(vecSize - (nbCol % vecSize));
	if(pitch == vecSize){pitch = 0lu;}
	return pitch;
}

///Do the hadamard computation
/**	@param self : parent of the function if it exist
 * 	@param args : arguments passed to the function
 * 	@return result of the Hadamard product
*/
PyObject * sgemmWrapper(PyObject *self, PyObject *args){
	PyArrayObject *objMatX = NULL, *objMatY = NULL, *objMatRes = NULL;
	
	if(!PyArg_ParseTuple(args, "OOO", &objMatRes, &objMatX, &objMatY)){
		PyErr_SetString(PyExc_RuntimeError, "sgemmWrapper : wrong set of arguments. Expect matRes, matX, matY\n");
		return NULL;
	}
	if(PyArray_NDIM(objMatX) != 2 || PyArray_NDIM(objMatY) != 2 || PyArray_NDIM(objMatRes) != 2){
		PyErr_SetString(PyExc_TypeError, "sgemmWrapper : input matrices must be a two dimension array");
		return NULL;
	}
	if(PyArray_DIMS(objMatX)[0] != PyArray_DIMS(objMatY)[0] || PyArray_DIMS(objMatX)[0] != PyArray_DIMS(objMatRes)[0] ||
		PyArray_DIMS(objMatX)[1] != PyArray_DIMS(objMatY)[1] || PyArray_DIMS(objMatX)[1] != PyArray_DIMS(objMatRes)[1] ||
		PyArray_DIMS(objMatX)[0] != PyArray_DIMS(objMatX)[1] ||
		PyArray_DIMS(objMatY)[0] != PyArray_DIMS(objMatY)[1] ||
		PyArray_DIMS(objMatRes)[0] != PyArray_DIMS(objMatRes)[1])
	{
		PyErr_SetString(PyExc_TypeError, "sgemmWrapper : input matrices must be of the same size and square");
		return NULL;
	}
	long unsigned int sizeElement(PyArray_DIMS(objMatX)[0]);
	
	const float * matX = (const float*)PyArray_DATA(objMatX);
	const float * matY = (const float*)PyArray_DATA(objMatY);
	float * matRes = (float*)PyArray_DATA(objMatRes);
	
	sgemm(matRes, matX, matY, sizeElement, getPitch(sizeElement));
	
	Py_RETURN_NONE;
}
You can download it here.