首发
我有一个非常长的运行程序,其中大约97%的性能被ftp.retrlines和ftp.retrbinary调用创建的套接字对象所占用。我已经使用进程和线程来并行化程序。我还能做些什么来提高速度吗?
示例代码:
# Get file list
ftpfilelist = []
ftp.retrlines('NLST %s' % ftp_directory, ftpfilelist.append)
... filter file list, this part takes almost no time ...
# Download a file
with open(path, 'wb') as fout:
ftp.retrbinary('RETR %s' % ftp_path, fout.write)
cProfiler的输出:
5890792 function calls (5888775 primitive calls) in 548.883 seconds
Ordered by: internal time
List reduced from 843 to 50 due to restriction <50>
ncalls tottime percall cumtime percall filename:lineno(function)
9166 249.154 0.027 249.154 0.027 {method 'recv_into' of '_socket.socket' objects}
99573 230.489 0.002 230.489 0.002 {method 'recv' of '_socket.socket' objects}
1767 53.113 0.030 53.129 0.030 {method 'connect' of '_socket.socket' objects}
98808 2.839 0.000 2.839 0.000 {method 'write' of '_io.BufferedWriter' objects}
跟进
支持python 3.4.1的gevent fork(https://github.com/fantix/gevent)的结果:
7645675 function calls (7153156 primitive calls) in 301.813 seconds
Ordered by: internal time
List reduced from 948 to 50 due to restriction <50>
ncalls tottime percall cumtime percall filename:lineno(function)
107541/4418 281.228 0.003 296.499 0.067 gevent/hub.py:354(wait)
99885/59883 4.466 0.000 405.922 0.007 gevent/_socket3.py:248(recv)
99097 2.244 0.000 2.244 0.000 {method 'write' of '_io.BufferedWriter' objects}
111125/2796 1.036 0.000 0.017 0.000 gevent/hub.py:345(switch)
107543/2788 1.000 0.000 0.039 0.000 gevent/hub.py:575(get)
concurrent.futures.ThreadPool的结果:
5319963 function calls (5318875 primitive calls) in 359.541 seconds
Ordered by: internal time
List reduced from 872 to 50 due to restriction <50>
ncalls tottime percall cumtime percall filename:lineno(function)
31 349.876 11.286 349.876 11.286 {method 'acquire' of '_thread.lock' objects}
2652 3.293 0.001 3.293 0.001 {method 'recv' of '_socket.socket' objects}
310270 0.790 0.000 0.790 0.000 {method 'timetuple' of 'datetime.date' objects}
25 0.661 0.026 0.661 0.026 {method 'recv_into' of '_socket.socket' objects}
结论: 对于我的用例,gevent将性能提高了大约20%!
答案 0 :(得分:1)
查看gevent。它可以修补您正在使用的任何库(例如您的FTP库),以通过使用协作线程来提高套接字性能。
一般的前提是线程程序对于繁重的I / O程序效率不高,因为调度程序不知道线程是否正在等待网络操作,因此当前线程可能被调度但也浪费时间等待I / O,而其他线程实际上可以正常工作。
使用gevent,只要您的线程(称为greenlet)遇到阻塞网络呼叫,它就会自动切换到另一个greenlet。通过这种机制,您的线程/ greenlets可以充分发挥其潜力。
以下是对此库的精彩介绍: http://www.gevent.org/intro.html#example
答案 1 :(得分:0)
我认为cProfile会计算功能的总花费时间,例如:用户空间中的时间以及系统时间,它在内核中等待的时间。这意味着像retrbinary
和retrlines
这样的函数将包括从网络获取数据所需的时间以及ftp服务器提供数据的速度越慢,在这些函数中花费的时间就越多
我建议您根据time(1)
或os.times()
的电话对您的探查器结果进行完整性检查。您可能会看到,该过程大部分时间都在等待数据(系统时间),因此您无法进行优化。