- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我需要生成一个非常大的文本文件。每行都有一个简单的格式:
Seq_num<SPACE>num_val
12343234 759
假设我要生成一个包含 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)
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)
注意:
所以方法 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/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!