python - 在 Python 中生成非常大的文本文件的时间性能

标签 python algorithm performance large-data large-files

我需要生成一个非常大的文本文件。每行都有一个简单的格式:

Seq_num<SPACE>num_val
12343234 759

假设我要生成一个包含 1 亿行的文件。 我尝试了两种方法,令人惊讶的是它们给出了截然不同的时间性能。

  1. For 循环超过 100 米。在每个循环中,我制作短字符串 seq_num<SPACE>num_val ,然后我将其写入文件。 这种方法需要很多时间。

    ## APPROACH 1  
    for seq_id in seq_ids:
        num_val=rand()
        line=seq_id+' '+num_val
        data_file.write(line)
    
  2. For 循环超过 100 米。在每个循环中,我制作短字符串 seq_num<SPACE>num_val ,然后我将其附加到列表中。 当循环结束时,我遍历列表项并将每个项目写入一个文件。 这种方法花费的时间少得多

    ## APPROACH 2  
    data_lines=list()
    for seq_id in seq_ids:
        num_val=rand()
        l=seq_id+' '+num_val
        data_lines.append(l)
    for line in data_lines:
        data_file.write(line)
    

注意:

  • 方法 2 有 2 个循环而不是 1 个循环。
  • 对于方法 1 和方法 2,我都在循环中写入文件。因此这一步必须对两者相同。

所以方法 1 必须花费更少的时间。有什么提示我遗漏了什么吗?

最佳答案

很多远少于在技术上是非常模糊的术语:)基本上如果你不能衡量它,你就不能改进它。

为简单起见,我们有一个简单的基准测试,loop1.py:

import random
from datetime import datetime

start = datetime.now()
data_file = open('file.txt', 'w')
for seq_id in range(0, 1000000):
        num_val=random.random()
        line="%i %f\n" % (seq_id, num_val)
        data_file.write(line)

end = datetime.now()
print("elapsed time %s" % (end - start))

loop2.py 带有 2 个 for 循环:

import random
from datetime import datetime

start = datetime.now()
data_file = open('file.txt', 'w')
data_lines=list()
for seq_id in range(0, 1000000):
    num_val=random.random()
    line="%i %f\n" % (seq_id, num_val)
    data_lines.append(line)
for line in data_lines:
    data_file.write(line)

end = datetime.now()
print("elapsed time %s" % (end - start))

当我在我的计算机(使用 SSD 驱动器)上运行这两个脚本时,我得到类似的东西:

$ python3 loop1.py 
elapsed time 0:00:00.684282
$ python3 loop2.py 
elapsed time 0:00:00.766182

每次测量可能略有不同,但直觉表明,第二次测量稍慢。

如果我们想优化写入时间,我们需要检查the manual how Python implements writing into files .对于文本文件,open() 函数应该使用 BufferedWriter .open 函数接受第三个参数,即缓冲区大小。这是有趣的部分:

Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable in text mode), and an integer > 1 to indicate the size in bytes of a fixed-size chunk buffer. When no buffering argument is given, the default buffering policy works as follows:

Binary files are buffered in fixed-size chunks; the size of the buffer is chosen using a heuristic trying to determine the underlying device’s “block size” and falling back on io.DEFAULT_BUFFER_SIZE. On many systems, the buffer will typically be 4096 or 8192 bytes long.

因此,我们可以修改 loop1.py 并使用行缓冲:

data_file = open('file.txt', 'w', 1)

事实证明这很慢:

$ python3 loop3.py 
elapsed time 0:00:02.470757

为了优化写入时间,我们可以根据需要调整缓冲区大小。首先,我们检查以字节为单位的行大小:len(line.encode('utf-8')),这给了我 11 字节。

将缓冲区大小更新为我们预期的字节大小后:

data_file = open('file.txt', 'w', 11)

我的写入速度非常快:

elapsed time 0:00:00.669622

根据您提供的详细信息,很难估计发生了什么。也许用于估计 block 大小的启发式方法在您的计算机上效果不佳。不管怎样,如果你写的是固定行长,优化缓冲区大小很容易。您可以利用 flush() 进一步优化文件写入.

结论:通常为了更快地写入文件,您应该尝试写入与文件系统上的 block 大小相对应的大量数据 - 这正是 Python 方法 open('file.txt', 'w') 正在尝试做。在大多数情况下,您使用默认设置是安全的,微基准测试中的差异微不足道。

您正在分配大量需要由 GC 收集的字符串对象。正如 @kevmo314 所建议的,为了进行公平比较,您应该为 loop1.py 禁用 GC:

gc.disable()

因为 GC 可能会在遍历循环时尝试删除字符串对象(您没有保留任何引用)。 seconds 方法保留对所有字符串对象的引用,GC 在最后收集它们。

关于python - 在 Python 中生成非常大的文本文件的时间性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49266939/

相关文章:

python - 更新整数的值(我认为)

python - 以类似枢轴的样式重新格式化数据框

算法 - 将嵌套数据转换为普通数据

performance - 使用 'exists' 进行额外的哈希查找?

xcode - 通过火焰图分析 Node.js 性能(Mac 上)

python - 动态创建的Enum的方法重载

python 帮助!如果/否则语句

algorithm - 连接组件标记 - 实现

algorithm - 这种最坏情况分析是否正确?

java,计时三个不同的循环