Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python ctypes align data structure

I have a C library that is compiled to a shared object and want to build a ctypes interface around it to call the C functions from Python.

In general it works fine but there is this definition of a double array in the C library:

typedef double __attribute__ ((aligned (32))) double_array[512];

I found no way to access this type directly, so I defined in Python:

DoubleArray = ctypes.c_double * 512

While this works out in most cases, sometimes the C library segfaults and I guess this happens because DoubleArray is not aligned to 32 bytes (maybe the library expects this because the data is passed to AVX).

How can I solve this problem?

like image 661
firefexx Avatar asked Mar 22 '16 13:03

firefexx


1 Answers

The array is at most 31 bytes shy of alignment. To get an aligned array, over-allocate by 31 bytes and then, if the base address is misaligned, add an offset to make it aligned. Here's a generic function for this:

def aligned_array(alignment, dtype, n):
    mask = alignment - 1
    if alignment == 0 or alignment & mask != 0:
        raise ValueError('alignment is not a power of 2')
    size = n * ctypes.sizeof(dtype) + mask
    buf = (ctypes.c_char * size)()
    misalignment = ctypes.addressof(buf) & mask
    if misalignment:
        offset = alignment - misalignment        
    else:
        offset = 0
    return (dtype * n).from_buffer(buf, offset)

For example:

>>> arr = aligned_array(2**4, ctypes.c_double, 512)
>>> hex(ctypes.addressof(arr))
'0x1754410'
>>> arr = aligned_array(2**8, ctypes.c_double, 512)
>>> hex(ctypes.addressof(arr))
'0x1755500'
>>> arr = aligned_array(2**12, ctypes.c_double, 512)
>>> hex(ctypes.addressof(arr))
'0x1757000'
>>> arr = aligned_array(2**16, ctypes.c_double, 512)
>>> hex(ctypes.addressof(arr))
'0x1760000'
like image 165
Eryk Sun Avatar answered Nov 03 '22 05:11

Eryk Sun