3

这是上一个问题的后续问题。

考虑一下这段代码,它比上一个问题中的代码不那么玩具(但仍然比我的真实代码简单得多)

import sys
data=[]

for line in open(sys.argv[1]):
    data.append(line[-1])

print data[-1]

现在,我期待更长的运行时间(我的基准文件长 65150224 行),可能更长。情况并非如此,它在与以前相同的硬件上运行约 2 分钟!

data.append() 是不是很轻量级?我不相信,所以我写了这个假代码来测试它:

data=[]
counter=0
string="a\n"

for counter in xrange(65150224):
    data.append(string[-1])

print data[-1]

这在 1.5 到 3 分钟内运行(运行之间存在很大的可变性)

为什么我在前一个节目中得不到 3.5 到 5 分钟?显然 data.append() 与 IO 并行发生。

这是个好消息!

但它是如何工作的?它是记录在案的功能吗?对我的代码是否有任何要求以使其尽可能地工作(除了负载平衡 IO 和内存/CPU 活动)?还是只是简单的缓冲/缓存在起作用?

再次,我标记了“linux”这个问题,因为我只对 linux 特定的答案感兴趣。如果您认为值得做,请随意给出与操作系统无关的,甚至是其他操作系统的答案。

4

5 回答 5

8

显然 data.append() 与 IO 并行发生。

恐怕不是。在 Python 中并行化 IO 和计算是可能的,但它不会神奇地发生。

您可以做的一件事是posix_fadvise(2)向操作系统提示您计划按顺序读取文件 ( POSIX_FADV_SEQUENTIAL)。

在对 600 meg 文件(ISO)执行“wc -l”的一些粗略测试中,性能提高了大约 20%。每个测试都是在清除磁盘缓存后立即完成的。

有关 fadvise 的 Python 接口,请参阅python-fadvise

于 2009-05-13T23:58:13.993 回答
1

您文件中的行有多大?如果它们不是很长(大约 1K 以下的任何东西都可能符合条件),那么您可能会因为输入缓冲而看到性能提升。

于 2009-05-13T23:37:27.210 回答
1

为什么你认为 list.append() 会是一个较慢的操作?它非常快,考虑到列表用于保存对其中对象的引用的内部指针数组被分配在越来越大的块中,因此每个追加实际上都不会重新分配数组,并且大多数可以简单地增加长度计数器和设置一个指针和增量。

于 2009-05-13T23:52:30.353 回答
1

我没有看到任何证据表明“data.append() 与 IO 并行发生”。像 Benji 一样,我不认为这会像你想的那样是自动的。您展示了执行 data.append(line[-1]) 所需的时间与 lc = lc + 1 大致相同(与 IO 和行拆分相比,基本上没有时间)。data.append(line[-1]) 非常快并不奇怪。人们会期望整行都在一个快速缓存中,并且如前所述,append 会提前准备缓冲区,并且很少需要重新分配。此外, line[-1] 将始终为 '\n',除了文件的最后一行(不知道 Python 是否对此进行了优化)。

唯一让我有点惊讶的是 xrange 是如此多变。我希望它总是更快,因为没有 IO,而且您实际上并没有使用计数器。

于 2009-05-14T00:02:32.507 回答
1

如果您的运行时间在第二个示例中变化了该数量,我怀疑您的计时方法或外部影响(其他进程/系统负载)会将时间扭曲到无法提供任何可靠信息的地步。

于 2009-05-14T04:11:56.533 回答