2013-10-28 45 views
8

用Python ctypes支持128位整数(当前是__uint128_t)的最佳方式是什么?用ctypes处理128位整数

也许是两个uint64_t的用户定义的结构,但这会在需要时创建对齐问题。

为什么ctypes没有扩展到支持128位整数的任何想法?

+0

打包结构(_pack_ = 1)至少可以解决对齐问题。 – epx

+0

并非如此,为了获得最佳性能,需要将这些向量保存在与16字节对齐的内存中。 – Fil

+2

注意:'__uint128_t'似乎是一个GCC扩展:http://stackoverflow.com/a/18531871/2419207 – iljau

回答

1

如果你真的想与128位整数一起工作,那么你不必担心对齐。没有当前的体系结构,没有任何Python运行的机器支持128位本地整数算术。所以没有机器需要16字节对齐的128位整数。只需使用该用户定义的结构,你就会好起来的。

如果你真正要求的是支持128位矢量类型,那么你可能需要让它们对齐。也就是说,如果您使用Python代码创建并通过引用C/C++代码传递它们,则需要它们对齐。你无法可靠地通过它们传递它们,没有办法让ctypes在堆栈上正确地对齐它们(如果ABI体系结构要求的话)。从C/C++传递给Python的向量大概已经被正确对齐了。所以,如果你可以安排它,所有的载体都分配在C/C++代码中,那么你的用户定义的结构也应该没问题。

假设你确实需要在Python代码中创建对齐的向量,那么我已经包含了对齐ctypes数组的代码。我也有代码来对齐我没有包含在代码大小合理的其他ctypes类型。阵列应该足够用于大多数目的。这些对齐的阵列有一些限制。如果您将它们按值传递给C/C++函数,或者将它们作为成员包含在结构体或联合体中,它们将不会被正确对齐。您可以使用*运算符来对齐阵列的对齐阵列。使用aligned_array_type(ctypes-type, length, alignment)创建新的对齐数组类型。使用aligned_type(ctypes-type, alignment)可创建已有阵列类型的对齐版本。

import ctypes 

ArrayType = type(ctypes.Array) 

class _aligned_array_type(ArrayType): 
    def __mul__(self, length): 
     return aligned_array_type(self._type_ * self._length_, 
         length, self._alignment_) 

    def __init__(self, name, bases, d): 
     self._alignment_ = max(getattr(self, "_alignment_", 1), 
         ctypes.alignment(self)) 

def _aligned__new__(cls): 
    a = cls._baseclass_.__new__(cls) 
    align = cls._alignment_ 
    if ctypes.addressof(a) % align == 0: 
     return a 
    cls._baseclass_.__init__(a) # dunno if necessary 
    ctypes.resize(a, ctypes.sizeof(a) + align - 1) 
    addr = ctypes.addressof(a) 
    aligned = (addr + align - 1) // align * align 
    return cls.from_buffer(a, aligned - addr) 

class aligned_base(object): 
    @classmethod 
    def from_address(cls, addr): 
     if addr % cls._alignment_ != 0: 
      raise ValueError, ("address must be %d byte aligned" 
         % cls._alignment_) 
     return cls._baseclass_.from_address(cls, addr) 

    @classmethod 
    def from_param(cls, addr): 
     raise ValueError, ("%s objects may not be passed by value" 
        % cls.__name__) 

class aligned_array(ctypes.Array, aligned_base): 
    _baseclass_ = ctypes.Array 
    _type_ = ctypes.c_byte 
    _length_ = 1 
    __new__ = _aligned__new__ 

_aligned_type_cache = {} 

def aligned_array_type(typ, length, alignment = None): 
    """Create a ctypes array type with an alignment greater than natural""" 

    natural = ctypes.alignment(typ) 
    if alignment == None: 
     alignment = typ._alignment_ 
    else: 
     alignment = max(alignment, getattr(typ, "_alignment_", 1)) 

    if natural % alignment == 0: 
     return typ * length 
    eltsize = ctypes.sizeof(typ) 
    eltalign = getattr(typ, "_alignment_", 1) 
    if eltsize % eltalign != 0: 
     raise TypeError("type %s can't have element alignment %d" 
       " in an array" % (typ.__name__, alignment)) 
    key = (_aligned_array_type, (typ, length), alignment) 
    ret = _aligned_type_cache.get(key) 
    if ret == None: 
     name = "%s_array_%d_aligned_%d" % (typ.__name__, length, 
          alignment) 
     d = {"_type_": typ, 
      "_length_": length, 
      "_alignment_": alignment} 
     ret = _aligned_array_type(name, (aligned_array,), d) 
     _aligned_type_cache[key] = ret 
    return ret 

def aligned_type(typ, alignment): 
    """Create a ctypes type with an alignment greater than natural""" 

    if ctypes.alignment(typ) % alignment == 0: 
     return typ 
    if issubclass(typ, ctypes.Array): 
     return aligned_array_type(typ._type_, typ._length_, 
         alignment) 
    else: 
     raise TypeError("unsupported type %s" % typ)