【问题标题】:Handling 128-bit integers with ctypes使用 ctypes 处理 128 位整数
【发布时间】:2013-11-07 01:01:08
【问题描述】:

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

可能是两个 uint64_t 的用户定义结构,但这会在需要的地方产生对齐问题。

对于为什么 ctypes 没有扩展为支持 128 位整数有什么想法吗?

【问题讨论】:

  • 压缩结构 (pack = 1) 至少可以解决对齐问题。
  • 并非如此,这些向量需要保存在与 16 字节对齐的内存中以获得最佳性能。
  • 注意:__uint128_t 似乎是 GCC 扩展:stackoverflow.com/a/18531871/2419207
  • 结构中有 _pack_ 魔法,派生自 ctypes.Structure,但值 16 似乎没有兑现,至少 ctypes.alignment() 仍然报告 8

标签: python ctypes int128


【解决方案1】:

如果您真的想使用 128 位 整数,那么您无需担心对齐问题。当前的体系结构,没有运行 Python 的机器支持 128 位本机整数运算。因此,没有机器需要或受益于 128 位整数 16 字节对齐。只需使用该用户定义的结构就可以了。

如果您真正需要的是对 128 位 vector 类型的支持,那么您可能需要将它们对齐。也就是说,如果您在 Python 代码中创建它们并通过引用 C/C++ 代码传递它们,则需要它们对齐。您无法可靠地按值传递它们,也无法让 ctypes 在堆栈上正确对齐它们(如果架构 ABI 需要)。从 C/C++ 传递到 Python 的向量可能已经正确对齐。因此,如果您可以安排它以便所有向量都在 C/C++ 代码中分配,那么您也应该可以使用用户定义的结构。

假设您确实需要在 Python 代码中创建对齐向量,那么我已经包含了对齐 ctypes 数组的代码。我还有代码可以将我未包含的其他 ctypes 类型与合理的代码大小对齐。对于大多数用途,数组应该足够了。这些对齐的数组有几个限制。如果将它们按值传递给 C/C++ 函数,或者将它们作为成员包含在结构或联合中,它们将无法正确对齐。您可以使用 * 运算符制作对齐数组的对齐数组。

使用aligned_array_type(<em>ctypes-type</em>, <em>length</em>, <em>alignment</em>) 创建新的对齐数组类型。使用aligned_type(<em>ctypes-type</em>, <em>alignment</em>) 创建现有数组类型的对齐版本。

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)

【讨论】:

  • @ReubenThomas 您的编译器可能会对该类型进行对齐,但出于性能或正确性的原因,它不是必需的。
  • @ReubenThomas 我建议发布一个新问题,详细说明您的问题,我相信有人可以解释实际发生的情况。您需要将您的问题简化为 minimal reproducible example,并根据调试问题的要求将其包含在您的问题中。
  • __int128 在某些平台上确实是 16 字节对齐的,正如 stackoverflow.com/q/52531695/569229 中所讨论的那样,具体来说,GCC 可以为它们生成假定 16 字节对齐的代码。
  • 您提到您有其他 ctypes 类型的代码:如果您有 Structure,我很乐意看到它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-28
  • 2015-02-19
  • 2011-09-03
  • 1970-01-01
  • 2013-07-20
  • 2016-03-18
相关资源
最近更新 更多