3

将仅包含数字数据的记录转换为固定格式字符串并将它们写入 Python 中的文件的最快方法是什么?例如,假设record是一个巨大的列表,由具有属性idxy和的对象组成,wt并且我们经常需要将它们刷新到外部文件中。可以使用以下代码段完成刷新:

with open(serial_fname(), "w") as f: 
    for r in records:
        f.write("%07d %11.5e %11.5e %7.5f\n" % (r.id, r.x, r.y, r.wt))

但是,我的代码花费了太多时间来生成外部文件,而在两次刷新之间做它应该做的事情的时间太少了。

修改原问题:

我在编写一个服务器软件时遇到了这个问题,该软件通过从多个“生产者”系统中提取信息并将对记录集的任何更改实时或近乎实时地转发给“消费者”系统来跟踪全局记录集以预处理的形式。许多消费者系统都是 Matlab 应用程序。

我在下面列出了到目前为止我收到的一些建议(谢谢)以及一些评论:

  • 只转储更改,而不是整个数据集:我实际上已经在这样做了。由此产生的变更集仍然很大。
  • 使用二进制(或其他更有效的)文件格式:我几乎受到 Matlab 可以合理有效地读取的内容的限制,此外格式应该与平台无关。
  • 使用数据库:我实际上是在尝试绕过当前被认为既太慢又太麻烦的数据库解决方案,尤其是在 Matlab 方面。
  • 将任务划分为单独的进程:此时转储代码正在其自己的线程中运行。然而,由于 GIL,它仍然使用相同的内核。我想我可以将它移动到完全独立的过程中。
4

5 回答 5

3

我试图检查numpy.savetxt是否可以加快速度,所以我编写了以下模拟:

import sys
import numpy as np

fmt = '%7.0f %11.5e %11.5e %7.5f'
records = 10000

np.random.seed(1234)
aray = np.random.rand(records, 4)

def writ(f, aray=aray, fmt=fmt):
  fw = f.write
  for row in aray:
    fw(fmt % tuple(row))

def prin(f, aray=aray, fmt=fmt):
  for row in aray:
    print>>f, fmt % tuple(row)

def stxt(f, aray=aray, fmt=fmt):
  np.savetxt(f, aray, fmt)

nul = open('/dev/null', 'w')
def tonul(func, nul=nul):
  func(nul)

def main():
  print 'looping:'
  loop(sys.stdout, aray)
  print 'savetxt:'
  savetxt(sys.stdout, aray)

我发现结果(在我的 2.4 GHz Core Duo Macbook Pro 上,Mac OS X 10.5.8,python.org 上 DMG 的 Python 2.5.4,从源代码构建的 numpy 1.4 rc1 上)有点令人惊讶,但它们非常可重复所以我认为他们可能会感兴趣:

$ py25 -mtimeit -s'import ft' 'ft.tonul(ft.writ)'
10 loops, best of 3: 101 msec per loop
$ py25 -mtimeit -s'import ft' 'ft.tonul(ft.prin)'
10 loops, best of 3: 98.3 msec per loop
$ py25 -mtimeit -s'import ft' 'ft.tonul(ft.stxt)'
10 loops, best of 3: 104 msec per loop

所以, savetxt 似乎比循环调用write了几个百分点......但是老旧的print(也在循环中)似乎比(我想它避免了某种调用开销)快了几个百分点。write我意识到 2.5% 左右的差异并不是很重要,但这不是我直觉预期的方向,所以我想我会报告它。(顺便说一句,使用真实文件而不是/dev/null仅仅均匀地增加 6 或 7 毫秒,所以它不会以一种或另一种方式改变太多)。

于 2009-12-06T19:26:55.140 回答
2

我没有看到任何关于我可以真正优化的代码片段。所以,我认为我们需要做一些完全不同的事情来解决你的问题。

您的问题似乎是您正在咀嚼大量数据,并且将数据格式化为字符串并将字符串写入文件很慢。您说“刷新”,这意味着您需要定期保存数据。

您是定期保存所有数据,还是只保存更改的数据?如果您正在处理一个非常大的数据集,只更改一些数据,然后写入所有数据......这是我们可以解决您问题的一个角度。

如果你有一个大数据集,并且你想不时更新它……你是数据库的候选人。一个真正的数据库,为了速度而用 C 语言编写,可以让你向它抛出大量数据更新,并将所有记录保持在一致的状态。然后,您可以每隔一段时间运行一个“报告”,该报告将提取记录并从中写入您的固定宽度文本文件。

换句话说,我建议您将问题分为两部分:在计算或接收更多数据时逐步更新数据集,并将整个数据集转储为固定宽度的文本格式,以供进一步处理。

请注意,您实际上可以从数据库生成文本文件,而无需停止正在更新它的 Python 进程。你会得到一个不完整的快照,但如果记录是独立的,那应该没问题。

如果您的进一步处理也在 Python 中,您可以将数据永远留在数据库中。不要费心通过固定宽度的文本文件来回传输数据。我假设您使用的是固定宽度的文本文件,因为很容易再次提取数据以供将来处理。

如果你使用数据库的思想,尝试使用 PostgreSQL。它是免费的,它是一个真正的数据库。要在 Python 中使用数据库,您应该使用 ORM。最好的之一是 SqlAlchemy。

要考虑的另一件事:如果您将数据保存为固定宽度的文本文件格式,以便将来在另一个应用程序中解析和使用数据,并且如果该应用程序可以读取 JSON 以及固定宽度,也许您可​​以使用编写 JSON 的 C 模块。它可能不会更快,但它可能;您可以对其进行基准测试并查看。

除了上述之外,我唯一的另一个想法是将您的程序拆分为“工作人员”部分和“更新程序”部分,其中工作人员生成更新的记录,而更新程序部分将记录保存到磁盘。或许通过让工作人员将更新的记录以文本格式输出到标准输出来让他们进行交流;并让更新程序从标准输入中读取并更新其数据记录。更新程序可以使用字典来存储文本记录,而不是 SQL 数据库;随着新的到来,它可以简单地更新字典。像这样的东西:

for line in sys.stdin:
    id = line[:7]  # fixed width: id is 7 wide
    records[id] = line # will insert or update as needed

实际上,您可以让更新程序保留两个字典,并在将另一个写入磁盘时继续更新一个。

划分为 worker 和 updater 是确保 worker 不会花费所有时间更新的好方法,也是平衡多个 CPU 内核工作的好方法。

我暂时没有想法。

于 2009-12-06T09:26:50.513 回答
0

现在您更新了您的问题,我对您所面临的问题有了一个更好的了解。

我不知道“当前被认为既慢又麻烦的数据库解决方案”是什么,但我仍然认为如果使用得当,数据库会有所帮助。

运行 Python 代码来收集数据,并使用 ORM 模块将数据插入/更新到数据库中。然后运行一个单独的进程来制作一个“报告”,这将是固定宽度的文本文件。数据库将完成生成文本文件的所有工作。如有必要,将数据库放在自己的服务器上,因为现在硬件非常便宜。

于 2009-12-06T19:21:42.453 回答
0

您可以尝试使用 ctypes 将循环推送到 C。

于 2009-12-06T19:36:29.193 回答
0

您可以尝试在内存中构建所有输出字符串,例如使用长字符串。然后在文件中写入这个长字符串。

更快:您可能希望使用二进制文件而不是文本文件来记录信息。但是你需要编写另一个工具来查看二进制文件。

于 2009-12-06T08:29:03.593 回答