Python 这个cProfile结果告诉我需要修复什么?
我想提高Python脚本的性能,并一直在使用Python 这个cProfile结果告诉我需要修复什么?,python,performance,profiling,profile,cprofile,Python,Performance,Profiling,Profile,Cprofile,我想提高Python脚本的性能,并一直在使用cProfile生成性能报告: python -m cProfile -o chrX.prof ./bgchr.py ...args... 我用Python的pstats打开了这个chrX.prof文件,并打印出了统计数据: Python 2.7 (r27:82500, Oct 5 2010, 00:24:22) [GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2 Type "help", "cop
cProfile
生成性能报告:
python -m cProfile -o chrX.prof ./bgchr.py ...args...
我用Python的pstats
打开了这个chrX.prof
文件,并打印出了统计数据:
Python 2.7 (r27:82500, Oct 5 2010, 00:24:22)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pstats
>>> p = pstats.Stats('chrX.prof')
>>> p.sort_stats('name')
>>> p.print_stats()
Sun Oct 10 00:37:30 2010 chrX.prof
8760583 function calls in 13.780 CPU seconds
Ordered by: function name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 {_locale.setlocale}
1 1.128 1.128 1.128 1.128 {bz2.decompress}
1 0.002 0.002 13.780 13.780 {execfile}
1750678 0.300 0.000 0.300 0.000 {len}
48 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'close' of 'file' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1750676 0.496 0.000 0.496 0.000 {method 'join' of 'str' objects}
1 0.007 0.007 0.007 0.007 {method 'read' of 'file' objects}
1 0.000 0.000 0.000 0.000 {method 'readlines' of 'file' objects}
1 0.034 0.034 0.034 0.034 {method 'rstrip' of 'str' objects}
23 0.000 0.000 0.000 0.000 {method 'seek' of 'file' objects}
1757785 1.230 0.000 1.230 0.000 {method 'split' of 'str' objects}
1 0.000 0.000 0.000 0.000 {method 'startswith' of 'str' objects}
1750676 0.872 0.000 0.872 0.000 {method 'write' of 'file' objects}
1 0.007 0.007 13.778 13.778 ./bgchr:3(<module>)
1 0.000 0.000 13.780 13.780 <string>:1(<module>)
1 0.001 0.001 0.001 0.001 {open}
1 0.000 0.000 0.000 0.000 {sys.exit}
1 0.000 0.000 0.000 0.000 ./bgchr:36(checkCommandLineInputs)
1 0.000 0.000 0.000 0.000 ./bgchr:27(checkInstallation)
1 1.131 1.131 13.701 13.701 ./bgchr:97(extractData)
1 0.003 0.003 0.007 0.007 ./bgchr:55(extractMetadata)
1 0.064 0.064 13.771 13.771 ./bgchr:5(main)
1750677 8.504 0.000 11.196 0.000 ./bgchr:122(parseJarchLine)
1 0.000 0.000 0.000 0.000 ./bgchr:72(parseMetadata)
1 0.000 0.000 0.000 0.000 /home/areynolds/proj/tools/lib/python2.7/locale.py:517(setlocale)
编辑
如果在parseJarchLine()
的第一个条件中注释掉sys.stdout.write
语句,则我的运行时间从10.2秒变为4.8秒:
# with first conditional's "sys.stdout.write" enabled
$ time ./bgchr chrX test.bjarch > /dev/null
real 0m10.186s
user 0m9.917s
sys 0m0.160s
# after first conditional's "sys.stdout.write" is commented out
$ time ./bgchr chrX test.bjarch > /dev/null
real 0m4.808s
user 0m4.561s
sys 0m0.156s
在Python中编写stdout是否真的那么昂贵?与可能的优化相关的条目是那些具有较高nCall和tottime值的条目
bgchr:4()
和:1()
可能指的是模块主体的执行,与此处无关
显然,性能问题来自字符串处理。这或许应该减少。热点是split
、join
和sys.stdout.write
<代码>bz2。解压缩似乎成本也很高
我建议您尝试以下方法:
- 您的主要数据似乎由选项卡分隔的CSV值组成。如果CSV阅读器性能更好,请尝试
- sys.stdout在每次写入换行符时都进行行缓冲和刷新。考虑写入具有较大缓冲区大小的文件。
- 不要在写出元素之前连接它们,而是按顺序将它们写入输出文件。您也可以考虑使用CSV编写器。
- 使用BZ2File对象并将其传递给CSV阅读器,而不是立即将数据解压缩为单个字符串
dataHandle.read(size)
,该调用会生成一个巨大的字符串,然后对该字符串进行解压缩,并直接使用file对象
附录:BZ2File可能不适用于您的情况,因为它需要一个filename参数。您需要的是一个具有集成读取限制的文件对象视图,类似于ZipExtFile,但使用BZ2Decompressor进行解压缩
这里我的主要观点是,您的代码应该进行更改,以对数据执行更迭代的处理,而不是将其作为一个整体进行拼凑,然后再将其拆分。如果您的代码如Lie Ryan所述更模块化,则此输出将更有用。但是,您可以从输出中获取一些信息,只需查看源代码即可: 您正在进行许多Python中实际上不需要的比较。例如,而不是:
如果len(entryText)>0:
你可以写:
if entryText:
在Python中,空列表的计算结果为False。对于空字符串也是如此,您也可以在代码中对其进行测试,更改它也会使代码更简短、更可读,因此:
for line in metadataLines:
if line == '':
break
else:
metadataList.append(line)
您只需执行以下操作:
for line in metadataLines:
if line:
metadataList.append(line)
在组织和性能方面,此代码还存在一些其他问题。例如,将变量多次分配给同一事物,而不是只创建一次对象实例并对对象执行所有访问。这样做会减少赋值的数量,也会减少全局变量的数量。我不想听起来过于挑剔,但这段代码似乎并没有考虑到性能
ncalls
仅当将数字与其他计数(如文件中的字符数/字段数/行数)进行比较可能会出现严重异常时才相关<代码>tottime和累计时间
才是真正重要的cumtime
是在函数/方法中花费的时间,包括在其调用的函数/方法中花费的时间tottime
是在函数/方法中花费的时间,不包括在其调用的函数/方法中花费的时间
我发现在tottime
和cumtime
上对统计数据进行排序很有帮助,而不是在name
上
bgchar
肯定是指脚本的执行,并不无关,因为它占用了13.5秒中的8.9秒;这8.9秒不包括它调用的函数/方法中的时间!仔细阅读@Lie Ryan关于将脚本模块化为函数的内容,并实施他的建议。同样地,@jonesy说
之所以提到string
,是因为您导入string
并仅在一个位置使用它:string.find(元素[0],'p')
。在输出的另一行中,您会注意到string.find只被调用了一次,因此在本次脚本运行中不会出现性能问题。但是:您可以在其他任何地方使用str
方法string
函数现在已被弃用,它们通过调用相应的str
方法来实现。您最好编写元素[0]。查找('p')==0
以获得准确但更快的等价物,并可能希望使用元素[0]。使用('p')
开始,这会让读者不知道==0
是否应该是=-1
@Bernd Petersohn提到的四种方法仅占用13.541秒的总执行时间中的3.7秒。在过分担心这些之前,先将脚本模块化为函数,再次运行cProfile,然后按tottime
对统计数据进行排序
修改脚本后的问题更新:
“”“问题:如何处理联接、拆分和写入操作,以减少它们对此脚本性能的明显影响?”
哈?这三个函数总共占用了13.8秒中的2.6秒。你的parseJarchLine函数占用了8.5秒(不包括它调用的函数/方法占用的时间。assert(8.5>2.6)
贝尔恩德已经指出了你可能会考虑做些什么。
for line in metadataLines:
if line:
metadataList.append(line)
def parseJarchLine(chromosome, line):
global pLength
global lastEnd
elements = line.split('\t')
if len(elements) > 1:
if lastEnd != "":
start = long(lastEnd) + long(elements[0])
# [1] start = lastEnd + long(elements[0])
# [2] start = lastEnd + int(elements[0])
lastEnd = long(start + pLength)
# [1] lastEnd = start + pLength
sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:])))
else:
lastEnd = long(elements[0]) + long(pLength)
# [1] lastEnd = long(elements[0]) + pLength
# [2] lastEnd = int(elements[0]) + pLength
sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, long(elements[0]), lastEnd, '\t'.join(elements[1:])))
else:
if elements[0].startswith('p'):
pLength = long(elements[0][1:])
# [2] pLength = int(elements[0][1:])
else:
start = long(long(lastEnd) + long(elements[0]))
# [1] start = lastEnd + long(elements[0])
# [2] start = lastEnd + int(elements[0])
lastEnd = long(start + pLength)
# [1] lastEnd = start + pLength
sys.stdout.write("%s\t%ld\t%ld\n" % (chromosome, start, lastEnd))
return
sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:])))
payload = "%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:]))
sys.stdout.write(payload)
def parseJarchLine(chromosome, line):
global pLength
global lastEnd
elements = line.split('\t', 1)
if elements[0][0] == 'p':
pLength = int(elements[0][1:])
return
start = lastEnd + int(elements[0])
lastEnd = start + pLength
sys.stdout.write("%s\t%ld\t%ld" % (chromosome, start, lastEnd))
if elements[1:]:
sys.stdout.write(elements[1])
sys.stdout.write(\n)