【问题标题】:Python is slow when iterating over a large list迭代大列表时,Python 很慢
【发布时间】:2012-03-13 04:59:36
【问题描述】:

我目前正在使用 pyodbc 从数据库中选择大量行。然后将结果复制到一个大列表中,然后我尝试遍历该列表。在我放弃 python 并尝试在 C# 中创建它之前,我想知道我是否做错了什么。

clientItems.execute("Select ids from largetable where year =?", year);
allIDRows = clientItemsCursor.fetchall() #takes maybe 8 seconds.

for clientItemrow in allIDRows:
    aID = str(clientItemRow[0])
    # Do something with str -- Removed because I was trying to determine what was slow
    count = count+1

更多信息:

  • for 循环目前以每秒 5 次左右的速度运行,这对我来说似乎非常慢。
  • 选择的总行数约为 489,000。
  • 运行它的机器有很多 RAM 和 CPU。它似乎只运行一两个核心,ram 是 1.72GB 的 4gb。

谁能告诉我怎么了?脚本运行这么慢吗?

谢谢

【问题讨论】:

  • clientItemRow[0] 真的很大吗? 489,000 是一个低行数,但 5rows/s 慢得可笑。另外,如果我错了,有人可以纠正我,但我很确定你写的代码只能在一个核心上运行,但仍然应该比每秒 5 次迭代快几英里。此外,您可以使用内置的 cProfile 查看您遇到瓶颈的位置。
  • 至于 CPU 使用率——如果你正在对检索到的结果做一些花哨的事情,你可以通过使用 Python 的多处理模块来获得更多的 cpu 内核——但让我们先解决这个问题。
  • type(allIDRows) 返回什么?
  • @prelic:我怀疑 odbc 驱动程序“fetchall”返回的 watherver 对象存在严重问题 - 唯一可能如此缓慢的方法是迭代它是 O(N) 或更糟。

标签: python sql database pyodbc


【解决方案1】:

这对于 Python 原生列表应该不会很慢 - 但也许 ODBC 的驱动程序正在返回一个“惰性”对象,它试图变得聪明但只是变慢了。试着做

allIDRows = list(clientItemsCursor.fetchall())

在您的代码中并发布进一步的基准测试。

(如果您开始在其中间插入东西,Python 列表可能会变慢,但仅迭代一个大列表应该会很快)

【讨论】:

  • 哇。我继续做了这个改变,每秒的数字超过了 1000。这太神奇了。将接受这个作为答案。
  • 找到 pyodbc 项目的网页并将其作为错误输入是一件好事。如果 fetchall 没有返回列表,它应该比列表更有效,而不是显示停止器
  • 很好的答案,让我想到了一个列表是存储收集到的数据的最快方式吗?集合、元组、dict(带有虚拟值)等会比这样的列表快吗?
  • 通过迭代列表,我从 2000 行/秒,随着时间的推移减慢到 9000 行/秒,没有性能损失。谢谢!!
【解决方案2】:

这可能很慢,因为您首先将所有结果加载到内存中并在列表上执行迭代。尝试迭代光标。

不,脚本不应该那么慢。

clientItemsCursor.execute("Select ids from largetable where year =?", year);
for clientItemrow in clientItemsCursor:
    aID = str(clientItemrow[0])
    count = count + 1

【讨论】:

  • 这与我的建议相反——只有测试才能证明,我希望 O.P. 让我们更新。无论如何,对列表的迭代不应该很慢,但可能for clientItemRow in cursor.fetchall(): ... 你建议更好。
  • 我试过这种薄荷醇,它一点也不快。这确实与 pyODBC 文档相匹配。迭代它们仍然很慢。
【解决方案3】:

这里需要更多调查...考虑以下脚本:

bigList = range(500000)
doSomething = ""
arrayList = [[x] for x in bigList]  # takes a few seconds
for x in arrayList:
    doSomething += str(x[0])
    count+=1

这与您的脚本几乎相同,但减去了数据库内容,并且需要几秒钟才能在我的速度不太快的机器上运行。

【讨论】:

    【解决方案4】:

    当你直接连接到你的数据库时(我的意思是你得到一个 SQL 提示),多少秒运行这个查询?

    查询结束时,您会收到如下消息:

    NNNNN rows in set (0.01 sec)
    

    所以,如果那个时间很长,并且您的查询速度像“本机”一样慢,那么您可能必须在该表上创建一个索引。

    【讨论】:

      【解决方案5】:

      这很慢,因为你是

      1. 获取所有结果
      2. 分配内存并将值分配给该内存以创建列表 allIDRows
      3. 遍历该列表并计数。

      如果执行返回一个游标,则使用游标来发挥它的优势,并在你取回东西时开始计数,并节省内存分配的时间。

      clientItemsCursor.execute("Select ids from largetable where year =?", year);
      for clientItemrow in clientItemsCursor:
         count +=1
      

      其他提示:

      • 按年份创建索引
      • 使用 'select count(*) from ... 来获取年份的计数' 这可能会在 db 上进行优化。
      • 如果不需要,请删除 aID 行,这会将行的第一项转换为字符串,即使它没有被使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-03
        • 2017-09-28
        • 2019-06-25
        • 1970-01-01
        • 2016-12-05
        • 2019-07-30
        相关资源
        最近更新 更多