gpt4 book ai didi

python - Python/SQLAlchemy 中的 weakref 性能非常差

转载 作者:太空狗 更新时间:2023-10-29 22:26:44 30 4
gpt4 key购买 nike

我花了一天时间尝试调试 Python 脚本中的内存问题。我正在使用 SQL Alchemy 作为我的 ORM。这里有几个令人困惑的问题,我希望如果我将它们全部列出来,有人能够为我指明正确的方向。

为了实现我正在寻找的性能,我读入了表中的所有记录 (~400k),然后遍历电子表格,匹配我之前读入的记录,然后创建新记录 ( ~800k) 到另一个表中。代码大致如下所示:

dimensionMap = {}
for d in connection.session.query(Dimension):
dimensionMap[d.businessKey] = d.primarySyntheticKey

# len(dimensionMap) == ~400k, sys.getsizeof(dimensionMap) == ~4MB

allfacts = []
sheet = open_spreadsheet(path)
for row in sheet.allrows():
dimensionId = dimensionMap[row[0]]
metric = row[1]

fact = Fact(dimensionId, metric)
connection.session.add(fact)
allfacts.append(fact)

if row.number % 20000 == 0:
connection.session.flush()

# len(allfacts) == ~800k, sys.getsizeof(allfacts) == ~50MB

connection.session.commit()

sys.stdout.write('All Done')

400k 和 800k 对我来说似乎不是特别大的数字,但我仍然遇到内存问题,这是一台具有 4GB 内存的机器。这对我来说真的很奇怪,因为我在我的两个最大的集合上运行了 sys.getsizeof,它们都小于任何会导致问题的大小。

在尝试解决这个问题时,我注意到脚本运行得非常非常慢。所以我对它进行了分析,希望结果能引导我解决内存问题,并提出了两个令人困惑的问题。

Profiler output

首先,87% 的程序时间花在了提交上,特别是在这行代码上:

self.transaction._new[state] = True

这可以在 session.py:1367 中找到。 self.transaction._newweakref.WeakKeyDictionary() 的实例。为什么 weakref:261:__setitem__ 占用了这么多时间?

其次,即使程序已完成('All Done' 已打印到标准输出),脚本仍在继续,似乎永远都在使用 2.2GB 的内存。

我已经对 weakrefs 进行了一些搜索,但没有看到有人提到我面临的性能问题。最终,我对此无能为力,因为它深埋在 SQL Alchemy 中,但我仍然很感激任何想法。

重点学习

如@zzzeek 所述,维护持久对象需要大量开销。这是显示增长的小图表。

Total memory used vs number of persistent instances

趋势线表明每个持久化实例占用大约 2KB 的内存开销,即使实例本身只有 30 字节。这实际上让我学到了另一件事,那就是对 sys.getsizeof 持怀疑态度。

此函数仅返回对象的大小,并且不考虑第一个对象有意义所需的任何其他对象(__dict__,例如)。您确实需要使用 Heapy 之类的工具来很好地了解实例的实际内存占用量。

我学到的最后一件事是,当 Python 即将耗尽内存并且疯狂地抖动时,会发生不应被视为问题一部分的奇怪事情。在我的例子中,大量的减速,指向 weakref 创建的配置文件,以及程序完成后的挂起,都是内存问题的影响。一旦我停止创建和保留持久实例,而是只保留我需要的对象属性,所有其他问题就都消失了。

最佳答案

800K ORM 对象非常大。这些是 Python 对象,每个对象都有一个 __dict__ 和一个 _sa_instance_state 属性,它本身就是一个对象,然后它里面有 weakrefs 和其他东西,然后是Session 对您的对象有多个弱引用 - ORM 对象是身份跟踪的,这是一项功能它在持久性方面提供了高度的自动化,但代价是更多的内存和函数调用开销。

至于为什么你的分析都集中在那条 weakref 行上,这看起来很奇怪,我很想看看那里的实际分析结果(请参阅 How can I profile a SQLAlchemy powered application? 了解背景)。

您的代码示例可以修改为不使用任何 ORM 身份映射对象,如下所示。有关批量插入的更多详细信息,请参阅 Why is SQLAlchemy insert with sqlite 25 times slower than using sqlite3 directly? .

# 1. only load individual columns - loading simple tuples instead 
# of full ORM objects with identity tracking. these tuples can be
# used directly in a dict comprehension
dimensionMap = dict(
connection.session.query(Dimension.businessKey, Dimension.primarySyntheticKey)
)

# 2. For bulk inserts, use Table.insert() call with
# multiparams in chunks
buf = []
for row in sheet.allrows():
dimensionId = dimensionMap[row[0]]
metric = row[1]

buf.append({"dimensionId": dimensionId, "metric": metric})

if len(buf == 20000):
connection.session.execute(Fact.__table__.insert(), params=buf)
buf[:] = []

connection.session.execute(Fact.__table__.insert(), params=buf)
sys.stdout.write('All Done')

关于python - Python/SQLAlchemy 中的 weakref 性能非常差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19061330/

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