gpt4 book ai didi

python - Python 中(命名)元组的字典和速度/RAM 性能

转载 作者:行者123 更新时间:2023-12-04 16:25:31 31 4
gpt4 key购买 nike

我正在创建一个包含一百万个元组项的字典 d,理想情况下我想通过以下方式访问它们:

d[1634].id       # or  d[1634]['id']
d[1634].name # or d[1634]['name']
d[1634].isvalid # or d[1634]['isvalid']

而不是d[1634][0], d[1634][1], d[1634][2]不太明确。

根据我的测试:

import os, psutil, time, collections, typing
Tri = collections.namedtuple('Tri', 'id,name,isvalid')
Tri2 = typing.NamedTuple("Tri2", [('id', int), ('name', str), ('isvalid', bool)])
t0 = time.time()
# uncomment only one of these 4 next lines:
d = {i: (i+1, 'hello', True) for i in range(1000000)} # tuple
# d = {i: {'id': i+1, 'name': 'hello', 'isvalid': True} for i in range(1000000)} # dict
# d = {i: Tri(id=i+1, name='hello', isvalid=True) for i in range(1000000)} # namedtuple
# d = {i: Tri2(id=i+1, name='hello', isvalid=True) for i in range(1000000)} # NamedTuple
print('%.3f s %.1f MB' % (time.time()-t0, psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2))

"""
tuple: 0.257 s 193.3 MB
dict: 0.329 s 363.6 MB
namedtuple: 1.253 s 193.3 MB (collections)
NamedTuple: 1.250 s 193.5 MB (typing)
"""
  • tuple
  • 相比,使用 dict 会使 RAM 使用量翻倍
  • tuple 相比,使用 namedtupleNamedTuple 所花费的时间乘以 5!

问题:Python 3 中是否有类似元组的数据结构,它允许使用 x.idx.name 等访问数据。 RAM 和 CPU 是否高效?


注意事项:

  • 在我的实际用例中,tuple 类似于 (uint64, uint64, bool) 类型的 C-struct。

  • 我也试过:

    • slots(为了避免内部对象的__dict__,见Usage of __slots__?)

    • 数据类:

      @dataclasses.dataclass
      class Tri3:
      id: int
      ...
    • ctypes.Structure:

      class Tri7(ctypes.Structure):
      _fields_ = [("id", ctypes.c_int), ...]

    但它并没有更好(所有这些都 ~ 1.2 秒。),就性能而言,与真正的 tuple 相差无几

  • 以下是其他选项:C-like structures in Python

最佳答案

Cython 的 cdef-classes可能是您想要的:它们比纯 Python 类使用更少的内存,即使在访问成员时会付出更多开销(因为字段存储为 C 值而不是 Python 对象)。

例如:

%%cython
cdef class CTuple:
cdef public unsigned long long int id
cdef public str name
cdef public bint isvalid

def __init__(self, id, name, isvalid):
self.id = id
self.name = name
self.isvalid = isvalid

可以随意使用:

ob=CTuple(1,"mmm",3)
ob.id, ob.name, ob.isvalid # prints (2, "mmm", 3)

时间/内存消耗:

首先,我机器上的基线:

0.258 s  252.4 MB  # tuples
0.343 s 417.5 MB # dict
1.181 s 264.0 MB # namedtuple collections

使用 CTuple 我们得到:

0.306 s  191.0 MB

几乎一样快,而且需要的内存要少得多。

如果 C 类型的成员在编译时不清楚,可以使用简单的 python 对象:

%%cython
cdef class PTuple:
cdef public object id
cdef public object name
cdef public object isvalid

def __init__(self, id, name, isvalid):
self.id = id
self.name = name
self.isvalid = isvalid

时间安排有点令人惊讶:

0.648 s  249.8 MB

我没想到它会比 CTuple 版本慢这么多,但至少是命名元组的两倍。


这种方法的一个缺点是它需要编译。然而,Cython 提供 cython.inline,可用于编译动态创建的 Cython 代码。

我已经发布了cynamedtuple可以通过pip install cynamedtuple安装,基于原型(prototype)如下:

import cython

# for generation of cython code:
tab = " "
def create_members_definition(name_to_ctype):
members = []
for my_name, my_ctype in name_to_ctype.items():
members.append(tab+"cdef public "+my_ctype+" "+my_name)
return members

def create_signature(names):
return tab + "def __init__(self,"+", ".join(names)+"):"

def create_initialization(names):
inits = [tab+tab+"self."+x+" = "+x for x in names]
return inits

def create_cdef_class_code(classname, names):
code_lines = ["cdef class " + classname + ":"]
code_lines.extend(create_members_definition(names))
code_lines.append(create_signature(names.keys()))
code_lines.extend(create_initialization(names.keys()))
return "\n".join(code_lines)+"\n"

# utilize cython.inline to generate and load pyx-module:
def create_cnamedtuple_class(classname, names):
code = create_cdef_class_code(classname, names)
code = code + "GenericClass = " + classname +"\n"
ret = cython.inline(code)
return ret["GenericClass"]

可以如下使用,从上面动态定义CTuple:

CTuple = create_cnamedtuple_class("CTuple", 
{"id":"unsigned long long int",
"name":"str",
"isvalid":"bint"})

ob = CTuple(1,"mmm",3)
...

另一种选择是使用 jit 编译和 Numba 的 jitted-classes这提供了这种可能性。然而,它们似乎要慢得多:

from numba import jitclass, types

spec = [
('id', types.uint64),
('name', types.string),
('isvalid', types.uint8),
]

@jitclass(spec)
class NBTuple(object):
def __init__(self, id, name, isvalid):
self.id = id
self.name = name
self.isvalid = isvalid

结果是:

20.622 s  394.0 MB

所以 numba jitted 类(还不是?)不是一个好的选择。

关于python - Python 中(命名)元组的字典和速度/RAM 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65159938/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com