2013-10-28 91 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)